diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 00000000000..46c7d3747f1 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,150 @@ +/* +👋 Hi! This file was autogenerated by tslint-to-eslint-config. +https://github.com/typescript-eslint/tslint-to-eslint-config + +It represents the closest reasonable ESLint configuration to this +project's original TSLint configuration. + +We recommend eventually switching this configuration to extend from +the recommended rulesets in typescript-eslint. +https://github.com/typescript-eslint/tslint-to-eslint-config/blob/master/docs/FAQs.md + +Happy linting! 💖 +*/ +module.exports = { + env: { + browser: true, + es6: true, + node: true, + }, + parser: '@typescript-eslint/parser', + parserOptions: { + project: 'tsconfig.json', + sourceType: 'module', + }, + plugins: [ + 'eslint-plugin-react', + '@typescript-eslint', + '@typescript-eslint/tslint', + 'eslint-plugin-import', + ], + root: true, + rules: { + '@typescript-eslint/consistent-type-imports': ['error', { disallowTypeAnnotations: false }], + '@typescript-eslint/dot-notation': 'error', + '@typescript-eslint/explicit-function-return-type': 'off', + '@typescript-eslint/explicit-module-boundary-types': 'off', + '@typescript-eslint/indent': 'off', + '@typescript-eslint/naming-convention': [ + 'off', + { + selector: 'variable', + format: ['camelCase', 'UPPER_CASE', 'PascalCase'], + leadingUnderscore: 'forbid', + trailingUnderscore: 'forbid', + }, + ], + '@typescript-eslint/no-array-constructor': 'off', + '@typescript-eslint/no-dynamic-delete': 'off', + '@typescript-eslint/no-empty-function': 'off', + '@typescript-eslint/no-empty-interface': 'off', + '@typescript-eslint/no-namespace': 'error', + '@typescript-eslint/no-unused-expressions': 'off', + '@typescript-eslint/no-var-requires': 'off', + '@typescript-eslint/quotes': 'off', + '@typescript-eslint/typedef': [ + 'error', + { + parameter: true, + }, + ], + 'brace-style': ['error', '1tbs'], + 'comma-dangle': 'off', + curly: 'error', + 'dot-notation': 'off', + 'eol-last': 'off', + eqeqeq: ['off', 'always'], + 'guard-for-in': 'error', + 'id-denylist': 'error', + 'id-match': 'error', + indent: 'off', + 'max-len': 'off', + 'no-array-constructor': 'off', + 'no-bitwise': 'off', + 'no-caller': 'error', + 'no-console': [ + 'error', + { + allow: [ + 'warn', + 'dir', + 'timeLog', + 'assert', + 'clear', + 'count', + 'countReset', + 'group', + 'groupEnd', + 'table', + 'dirxml', + 'error', + 'groupCollapsed', + 'Console', + 'profile', + 'profileEnd', + 'timeStamp', + 'context', + ], + }, + ], + 'no-constant-condition': 'error', + 'no-control-regex': 'error', + 'no-debugger': 'error', + 'no-duplicate-case': 'error', + 'no-empty': 'off', + 'no-empty-function': 'off', + 'no-eval': 'error', + 'no-extra-bind': 'error', + 'no-fallthrough': 'error', + 'no-invalid-regexp': 'error', + 'no-multi-str': 'error', + 'no-new-func': 'error', + 'no-new-wrappers': 'error', + 'no-octal': 'error', + 'no-octal-escape': 'error', + 'no-redeclare': 'off', + 'no-regex-spaces': 'error', + 'no-restricted-syntax': [ + 'error', + { + message: 'Forbidden call to document.cookie', + selector: 'MemberExpression[object.name="document"][property.name="cookie"]', + }, + ], + 'no-sparse-arrays': 'error', + 'no-trailing-spaces': 'error', + 'no-underscore-dangle': 'off', + 'no-unused-expressions': 'off', + 'no-unused-labels': 'error', + 'no-with': 'error', + quotes: 'off', + 'react/no-danger': 'error', + 'use-isnan': 'error', + '@typescript-eslint/tslint/config': [ + 'error', + { + rules: { + whitespace: [ + true, + 'check-branch', + 'check-decl', + 'check-operator', + 'check-separator', + 'check-type', + ], + }, + }, + ], + 'import/no-duplicates': 'error', + }, +}; diff --git a/demo/scripts/controls/BuildInPluginState.ts b/demo/scripts/controls/BuildInPluginState.ts index 47ac1c80f26..ca322275bfb 100644 --- a/demo/scripts/controls/BuildInPluginState.ts +++ b/demo/scripts/controls/BuildInPluginState.ts @@ -23,6 +23,7 @@ export interface BuildInPluginList { contextMenu: boolean; autoFormat: boolean; contentModelPaste: boolean; + announce: boolean; } export default interface BuildInPluginState { @@ -34,6 +35,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/MainPaneBase.tsx b/demo/scripts/controls/MainPaneBase.tsx index ccd28c983a7..46a6cee2dbd 100644 --- a/demo/scripts/controls/MainPaneBase.tsx +++ b/demo/scripts/controls/MainPaneBase.tsx @@ -3,12 +3,12 @@ import * as ReactDOM from 'react-dom'; import BuildInPluginState from './BuildInPluginState'; import SidePane from './sidePane/SidePane'; import SnapshotPlugin from './sidePane/snapshot/SnapshotPlugin'; -import { EditorOptions, EditorPlugin, IEditor } from 'roosterjs-editor-types'; import { getDarkColor } from 'roosterjs-color-utils'; import { PartialTheme, ThemeProvider } from '@fluentui/react/lib/Theme'; import { registerWindowForCss, unregisterWindowForCss } from '../utils/cssMonitor'; import { trustedHTMLHandler } from '../utils/trustedHTMLHandler'; import { WindowProvider } from '@fluentui/react/lib/WindowProvider'; +import { EditorOptions, EditorPlugin, IEditor } from 'roosterjs-editor-types'; import { createUpdateContentPlugin, Rooster, diff --git a/demo/scripts/controls/contentModel/components/model/ContentModelEntityView.tsx b/demo/scripts/controls/contentModel/components/model/ContentModelEntityView.tsx index 4f7c24b3c1c..74c65e90c6a 100644 --- a/demo/scripts/controls/contentModel/components/model/ContentModelEntityView.tsx +++ b/demo/scripts/controls/contentModel/components/model/ContentModelEntityView.tsx @@ -10,9 +10,9 @@ const styles = require('./ContentModelEntityView.scss'); export function ContentModelEntityView(props: { entity: ContentModelEntity }) { const { entity } = props; - const [id, setId] = useProperty(entity.id); - const [isReadonly, setIsReadonly] = useProperty(entity.isReadonly); - const [type, setType] = useProperty(entity.type); + const [id, setId] = useProperty(entity.entityFormat.id); + const [isReadonly, setIsReadonly] = useProperty(entity.entityFormat.isReadonly); + const [type, setType] = useProperty(entity.entityFormat.entityType); const idTextBox = React.useRef(null); const isReadonlyCheckBox = React.useRef(null); @@ -20,17 +20,17 @@ export function ContentModelEntityView(props: { entity: ContentModelEntity }) { const onIdChange = React.useCallback(() => { const newValue = idTextBox.current.value; - entity.id = newValue; + entity.entityFormat.id = newValue; setId(newValue); }, [id, setId]); const onTypeChange = React.useCallback(() => { const newValue = typeTextBox.current.value; - entity.type = newValue; + entity.entityFormat.entityType = newValue; setType(newValue); }, [type, setType]); const onReadonlyChange = React.useCallback(() => { const newValue = isReadonlyCheckBox.current.checked; - entity.isReadonly = newValue; + entity.entityFormat.isReadonly = newValue; setIsReadonly(newValue); }, [id, setId]); diff --git a/demo/scripts/controls/getToggleablePlugins.ts b/demo/scripts/controls/getToggleablePlugins.ts index 24029ca55a7..dcdab52c233 100644 --- a/demo/scripts/controls/getToggleablePlugins.ts +++ b/demo/scripts/controls/getToggleablePlugins.ts @@ -1,10 +1,11 @@ import BuildInPluginState, { BuildInPluginList, UrlPlaceholder } from './BuildInPluginState'; +import { Announce } from 'roosterjs-editor-plugins/lib/Announce'; import { AutoFormat } from 'roosterjs-editor-plugins/lib/AutoFormat'; import { ContentEdit } from 'roosterjs-editor-plugins/lib/ContentEdit'; import { ContentModelPastePlugin } from 'roosterjs-content-model-editor'; import { CustomReplace as CustomReplacePlugin } from 'roosterjs-editor-plugins/lib/CustomReplace'; import { CutPasteListChain } from 'roosterjs-editor-plugins/lib/CutPasteListChain'; -import { EditorPlugin } from 'roosterjs-editor-types'; +import { EditorPlugin, KnownAnnounceStrings } from 'roosterjs-editor-types'; import { HyperLink } from 'roosterjs-editor-plugins/lib/HyperLink'; import { ImageEdit } from 'roosterjs-editor-plugins/lib/ImageEdit'; import { Paste } from 'roosterjs-editor-plugins/lib/Paste'; @@ -59,7 +60,19 @@ export default function getToggleablePlugins(initState: BuildInPluginState) { : null, contextMenu: pluginList.contextMenu ? createContextMenuPlugin() : null, contentModelPaste: pluginList.contentModelPaste ? new ContentModelPastePlugin() : null, + announce: pluginList.announce ? new Announce(getDefaultStringsMap()) : null, }; return Object.values(plugins); } + +function getDefaultStringsMap(): Map { + return new Map([ + [KnownAnnounceStrings.AnnounceListItemBullet, 'Autocorrected Bullet'], + [KnownAnnounceStrings.AnnounceListItemNumbering, 'Autocorrected {0}'], + [ + KnownAnnounceStrings.AnnounceOnFocusLastCell, + 'Warning, pressing tab here adds an extra row.', + ], + ]); +} diff --git a/demo/scripts/controls/sidePane/editorOptions/ContentModelEditorOptionsPlugin.ts b/demo/scripts/controls/sidePane/editorOptions/ContentModelEditorOptionsPlugin.ts index 78d964f43d7..2ced4619a66 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 = { @@ -22,17 +21,16 @@ const initialState: BuildInPluginState = { contextMenu: true, autoFormat: true, contentModelPaste: true, + announce: true, }, contentEditFeatures: getDefaultContentEditFeatureSettings(), defaultFormat: {}, linkTitle: 'Ctrl+Click to follow the link:' + UrlPlaceholder, watermarkText: 'Type content here ...', forcePreserveRatio: false, - experimentalFeatures: [ - ExperimentalFeatures.InlineEntityReadOnlyDelimiters, - ExperimentalFeatures.ContentModelPaste, - ], + 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 97f1f8df0b6..47c52846e38 100644 --- a/demo/scripts/controls/sidePane/editorOptions/ContentModelExperimentalFeatures.tsx +++ b/demo/scripts/controls/sidePane/editorOptions/ContentModelExperimentalFeatures.tsx @@ -14,9 +14,6 @@ const FeatureNames: Partial> = { "Reuse ancestor list elements even if they don't match the types from the list item.", [ExperimentalFeatures.DeleteTableWithBackspace]: 'Delete a table selected with the table selector pressing Backspace key', - [ExperimentalFeatures.InlineEntityReadOnlyDelimiters]: - 'Add read entities around read only entities to handle browser edge cases.', - [ExperimentalFeatures.ContentModelPaste]: 'Paste with content model', [ExperimentalFeatures.DisableListChain]: 'Disable list chain functionality', }; @@ -38,7 +35,7 @@ export default class ContentModelExperimentalFeaturesPane extends React.Componen id={name} onChange={() => this.onClick(name)} /> - + ); } 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/demo/scripts/controls/sidePane/editorOptions/EditorOptionsPlugin.ts b/demo/scripts/controls/sidePane/editorOptions/EditorOptionsPlugin.ts index efc0aa9a051..8c111a1cbca 100644 --- a/demo/scripts/controls/sidePane/editorOptions/EditorOptionsPlugin.ts +++ b/demo/scripts/controls/sidePane/editorOptions/EditorOptionsPlugin.ts @@ -2,7 +2,6 @@ import BuildInPluginState, { BuildInPluginProps, UrlPlaceholder } from '../../Bu import getDefaultContentEditFeatureSettings from './getDefaultContentEditFeatureSettings'; import OptionsPane from './OptionsPane'; import SidePanePluginImpl from '../SidePanePluginImpl'; -import { ExperimentalFeatures } from 'roosterjs-editor-types'; import { SidePaneElementProps } from '../SidePaneElement'; const initialState: BuildInPluginState = { @@ -22,13 +21,14 @@ const initialState: BuildInPluginState = { contextMenu: true, autoFormat: true, contentModelPaste: false, + announce: true, }, contentEditFeatures: getDefaultContentEditFeatureSettings(), defaultFormat: {}, linkTitle: 'Ctrl+Click to follow the link:' + UrlPlaceholder, watermarkText: 'Type content here ...', forcePreserveRatio: false, - experimentalFeatures: [ExperimentalFeatures.InlineEntityReadOnlyDelimiters], + experimentalFeatures: [], isRtl: false, tableFeaturesContainerSelector: '#' + 'EditorContainer', }; diff --git a/demo/scripts/controls/sidePane/editorOptions/ExperimentalFeatures.tsx b/demo/scripts/controls/sidePane/editorOptions/ExperimentalFeatures.tsx index 21bbb750c59..760b75aa337 100644 --- a/demo/scripts/controls/sidePane/editorOptions/ExperimentalFeatures.tsx +++ b/demo/scripts/controls/sidePane/editorOptions/ExperimentalFeatures.tsx @@ -14,8 +14,6 @@ const FeatureNames: Partial> = { "Reuse ancestor list elements even if they don't match the types from the list item.", [ExperimentalFeatures.DeleteTableWithBackspace]: 'Delete a table selected with the table selector pressing Backspace key', - [ExperimentalFeatures.InlineEntityReadOnlyDelimiters]: - 'Add read entities around read only entities to handle browser edge cases.', [ExperimentalFeatures.DisableListChain]: 'Disable list chain functionality', }; diff --git a/demo/scripts/controls/sidePane/editorOptions/Plugins.tsx b/demo/scripts/controls/sidePane/editorOptions/Plugins.tsx index 1591a68058f..952f890ce85 100644 --- a/demo/scripts/controls/sidePane/editorOptions/Plugins.tsx +++ b/demo/scripts/controls/sidePane/editorOptions/Plugins.tsx @@ -61,6 +61,7 @@ export default class Plugins extends React.Component { 'Show customized context menu for special cases' )} {this.renderPluginItem('tableCellSelection', 'Table Cell Selection')} + {this.renderPluginItem('announce', 'Announce')} ); diff --git a/package.json b/package.json index 72516b909df..195ab200ce5 100644 --- a/package.json +++ b/package.json @@ -15,16 +15,16 @@ "scripts": { "clean": "node tools/build.js clean", "dts": "node tools/build.js clean normalize buildcommonjs dts", - "tslint": "node tools/build.js tslint", + "eslint": "node tools/build.js eslint", "normalize": "node tools/build.js normalize", "pack": "node tools/build.js pack", "pack:prod": "node tools/build.js packprod", "b": "node tools/build.js clean normalize buildcommonjs", "builddemo": "node tools/build.js builddemo", "builddoc": "node tools/build.js builddoc", - "build": "node tools/build.js clean checkdep normalize tslint buildcommonjs dts packprod builddemo", + "build": "node tools/build.js clean checkdep normalize eslint buildcommonjs dts packprod builddemo", "buildtest": "node tools/build.js normalize buildtest", - "build:ci": "node --max-old-space-size=8192 tools/build.js --noProgressBar clean checkdep normalize tslint buildcommonjs buildamd buildmjs buildtest dts pack packprod builddemo builddoc", + "build:ci": "node --max-old-space-size=8192 tools/build.js --noProgressBar clean checkdep normalize eslint buildcommonjs buildamd buildmjs buildtest dts pack packprod builddemo builddoc", "start": "node tools/start.js", "test": "node tools/build.js normalize & karma start --chrome", "test:chrome": "node tools/build.js normalize & karma start --chrome", @@ -35,9 +35,10 @@ "test:cm:firefox": "node tools/build.js normalize & karma start --no-single-run --firefox --contentmodel", "test:r": "node tools/build.js normalize & karma start --no-single-run --chrome --roosterjs", "test:ui": "node tools/build.js normalize & karma start --no-single-run --chrome --ui", - "publish": "node tools/build.js clean normalize tslint buildcommonjs buildamd buildmjs dts pack packprod builddemo builddoc publish" + "publish": "node tools/build.js clean normalize eslint buildcommonjs buildamd buildmjs dts pack packprod builddemo builddoc publish" }, "devDependencies": { + "@fluentui/react": "^8.0.0", "@microsoft/loader-load-themed-styles": "1.8.11", "@types/color": "3.0.0", "@types/dompurify": "2.2.3", @@ -46,12 +47,17 @@ "@types/object-assign": "4.0.30", "@types/react": "16.8.22", "@types/react-dom": "17.0.11", - "@fluentui/react": "^8.0.0", + "@typescript-eslint/eslint-plugin": "^6.7.3", + "@typescript-eslint/eslint-plugin-tslint": "^6.7.3", + "@typescript-eslint/parser": "^6.7.3", "color": "3.1.3", "coverage-istanbul-loader": "3.0.5", "css-loader": "3.5.3", "detect-port": "^1.3.0", "dompurify": "2.3.0", + "eslint": "^8.50.0", + "eslint-plugin-react": "^7.33.2", + "eslint-plugin-import": "2.28.1", "glob": "7.1.6", "husky": "^4.2.5", "jasmine-core": "3.5.0", diff --git a/packages-content-model/roosterjs-content-model-dom/lib/config/defaultContentModelFormatMap.ts b/packages-content-model/roosterjs-content-model-dom/lib/config/defaultContentModelFormatMap.ts index 18c40bdfcf8..9cf028d32b0 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/config/defaultContentModelFormatMap.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/config/defaultContentModelFormatMap.ts @@ -1,4 +1,4 @@ -import { DefaultImplicitFormatMap } from 'roosterjs-content-model-types'; +import type { DefaultImplicitFormatMap } from 'roosterjs-content-model-types'; /** * @internal diff --git a/packages-content-model/roosterjs-content-model-dom/lib/config/defaultHTMLStyleMap.ts b/packages-content-model/roosterjs-content-model-dom/lib/config/defaultHTMLStyleMap.ts index 73b487e852c..49559477a1e 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/config/defaultHTMLStyleMap.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/config/defaultHTMLStyleMap.ts @@ -1,4 +1,4 @@ -import { DefaultStyleMap } from 'roosterjs-content-model-types'; +import type { DefaultStyleMap } from 'roosterjs-content-model-types'; const blockElement: Partial = { display: 'block', diff --git a/packages-content-model/roosterjs-content-model-dom/lib/domToModel/context/createDomToModelContext.ts b/packages-content-model/roosterjs-content-model-dom/lib/domToModel/context/createDomToModelContext.ts index 8023f8c1794..e68d4b3eede 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/domToModel/context/createDomToModelContext.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/domToModel/context/createDomToModelContext.ts @@ -1,10 +1,10 @@ import { defaultProcessorMap } from './defaultProcessors'; -import { getObjectKeys } from 'roosterjs-editor-dom'; +import { getObjectKeys } from '../../domUtils/getObjectKeys'; import { defaultFormatKeysPerCategory, defaultFormatParsers, } from '../../formatHandlers/defaultFormatHandlers'; -import { +import type { ContentModelBlockFormat, DomToModelContext, DomToModelDecoratorContext, diff --git a/packages-content-model/roosterjs-content-model-dom/lib/domToModel/context/defaultProcessors.ts b/packages-content-model/roosterjs-content-model-dom/lib/domToModel/context/defaultProcessors.ts index 631dc2a3f51..31a4bda40cf 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/domToModel/context/defaultProcessors.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/domToModel/context/defaultProcessors.ts @@ -3,7 +3,6 @@ import { childProcessor } from '../processors/childProcessor'; import { codeProcessor } from '../processors/codeProcessor'; import { delimiterProcessor } from '../processors/delimiterProcessor'; import { elementProcessor } from '../processors/elementProcessor'; -import { ElementProcessorMap } from 'roosterjs-content-model-types'; import { entityProcessor } from '../processors/entityProcessor'; import { fontProcessor } from '../processors/fontProcessor'; import { formatContainerProcessor } from '../processors/formatContainerProcessor'; @@ -18,6 +17,7 @@ import { listProcessor } from '../processors/listProcessor'; import { pProcessor } from '../processors/pProcessor'; import { tableProcessor } from '../processors/tableProcessor'; import { textProcessor } from '../processors/textProcessor'; +import type { ElementProcessorMap } from 'roosterjs-content-model-types'; /** * @internal diff --git a/packages-content-model/roosterjs-content-model-dom/lib/domToModel/domToContentModel.ts b/packages-content-model/roosterjs-content-model-dom/lib/domToModel/domToContentModel.ts index 5b41db3d4f7..f16dce3595d 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/domToModel/domToContentModel.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/domToModel/domToContentModel.ts @@ -1,7 +1,10 @@ -import { ContentModelDocument, DomToModelContext } from 'roosterjs-content-model-types'; import { createContentModelDocument } from '../modelApi/creators/createContentModelDocument'; import { normalizeContentModel } from '../modelApi/common/normalizeContentModel'; -import { SelectionRangeEx } from 'roosterjs-editor-types'; +import type { + ContentModelDocument, + DOMSelection, + DomToModelContext, +} from 'roosterjs-content-model-types'; /** * Create Content Model from DOM tree in this editor @@ -13,11 +16,11 @@ import { SelectionRangeEx } from 'roosterjs-editor-types'; export function domToContentModel( root: HTMLElement | DocumentFragment, context: DomToModelContext, - selection?: SelectionRangeEx + selection?: DOMSelection ): ContentModelDocument { const model = createContentModelDocument(context.defaultFormat); - context.rangeEx = selection; + context.selection = selection; context.elementProcessors.child(model, root, context); normalizeContentModel(model); diff --git a/packages-content-model/roosterjs-content-model-dom/lib/domToModel/processors/blockProcessor.ts b/packages-content-model/roosterjs-content-model-dom/lib/domToModel/processors/blockProcessor.ts index 3cf4f8c384d..8fb60666fba 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/domToModel/processors/blockProcessor.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/domToModel/processors/blockProcessor.ts @@ -2,7 +2,7 @@ import { addBlock } from '../../modelApi/common/addBlock'; import { ContextStyles } from './formatContainerProcessor'; import { createParagraph } from '../../modelApi/creators/createParagraph'; import { parseFormat } from '../utils/parseFormat'; -import { +import type { ContentModelBlockGroup, ContentModelSegmentFormat, DomToModelContext, diff --git a/packages-content-model/roosterjs-content-model-dom/lib/domToModel/processors/brProcessor.ts b/packages-content-model/roosterjs-content-model-dom/lib/domToModel/processors/brProcessor.ts index 6646d690507..f5677434c77 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/domToModel/processors/brProcessor.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/domToModel/processors/brProcessor.ts @@ -1,6 +1,6 @@ import { addSegment } from '../../modelApi/common/addSegment'; import { createBr } from '../../modelApi/creators/createBr'; -import { ElementProcessor } from 'roosterjs-content-model-types'; +import type { ElementProcessor } from 'roosterjs-content-model-types'; /** * @internal @@ -12,5 +12,6 @@ export const brProcessor: ElementProcessor = (group, element, con br.isSelected = true; } - addSegment(group, br, context.blockFormat); + const paragraph = addSegment(group, br, context.blockFormat); + context.domIndexer?.onSegment(element, paragraph, [br]); }; diff --git a/packages-content-model/roosterjs-content-model-dom/lib/domToModel/processors/childProcessor.ts b/packages-content-model/roosterjs-content-model-dom/lib/domToModel/processors/childProcessor.ts index eb3f1add170..32d7c817f6c 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/domToModel/processors/childProcessor.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/domToModel/processors/childProcessor.ts @@ -1,8 +1,7 @@ import { addSelectionMarker } from '../utils/addSelectionMarker'; import { getRegularSelectionOffsets } from '../utils/getRegularSelectionOffsets'; import { isNodeOfType } from '../../domUtils/isNodeOfType'; -import { NodeType, SelectionRangeTypes } from 'roosterjs-editor-types'; -import { +import type { ContentModelBlockGroup, DomToModelContext, ElementProcessor, @@ -45,9 +44,9 @@ export function processChildNode( child: Node, context: DomToModelContext ) { - if (isNodeOfType(child, NodeType.Element) && child.style.display != 'none') { + if (isNodeOfType(child, 'ELEMENT_NODE') && child.style.display != 'none') { context.elementProcessors.element(group, child, context); - } else if (isNodeOfType(child, NodeType.Text)) { + } else if (isNodeOfType(child, 'TEXT_NODE')) { context.elementProcessors['#text'](group, child, context); } } @@ -73,8 +72,8 @@ export function handleRegularSelection( addSelectionMarker(group, context); } - if (index == nodeEndOffset && context.rangeEx?.type == SelectionRangeTypes.Normal) { - if (!context.rangeEx.areAllCollapsed) { + if (index == nodeEndOffset && context.selection?.type == 'range') { + if (!context.selection.range.collapsed) { addSelectionMarker(group, context); } context.isInSelection = false; diff --git a/packages-content-model/roosterjs-content-model-dom/lib/domToModel/processors/codeProcessor.ts b/packages-content-model/roosterjs-content-model-dom/lib/domToModel/processors/codeProcessor.ts index 1da64ae886e..51204df2223 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/domToModel/processors/codeProcessor.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/domToModel/processors/codeProcessor.ts @@ -1,7 +1,7 @@ -import { ElementProcessor } from 'roosterjs-content-model-types'; import { knownElementProcessor } from './knownElementProcessor'; import { parseFormat } from '../utils/parseFormat'; import { stackFormat } from '../utils/stackFormat'; +import type { ElementProcessor } from 'roosterjs-content-model-types'; /** * @internal diff --git a/packages-content-model/roosterjs-content-model-dom/lib/domToModel/processors/delimiterProcessor.ts b/packages-content-model/roosterjs-content-model-dom/lib/domToModel/processors/delimiterProcessor.ts index 24000ce0ef3..fd6052e09b3 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/domToModel/processors/delimiterProcessor.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/domToModel/processors/delimiterProcessor.ts @@ -1,6 +1,6 @@ -import { ElementProcessor } from 'roosterjs-content-model-types'; import { getRegularSelectionOffsets } from '../utils/getRegularSelectionOffsets'; import { handleRegularSelection } from './childProcessor'; +import type { ElementProcessor } from 'roosterjs-content-model-types'; /** * @internal diff --git a/packages-content-model/roosterjs-content-model-dom/lib/domToModel/processors/elementProcessor.ts b/packages-content-model/roosterjs-content-model-dom/lib/domToModel/processors/elementProcessor.ts index e6b793fee35..fc89676a175 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/domToModel/processors/elementProcessor.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/domToModel/processors/elementProcessor.ts @@ -1,5 +1,6 @@ -import { getDelimiterFromElement, getEntityFromElement } from 'roosterjs-editor-dom'; -import { +import { getDelimiterFromElement } from 'roosterjs-editor-dom'; +import { isEntityElement } from '../../domUtils/entityUtils'; +import type { DomToModelContext, ElementProcessor, ElementProcessorMap, @@ -22,8 +23,7 @@ export const elementProcessor: ElementProcessor = (group, element, }; function tryGetProcessorForEntity(element: HTMLElement, context: DomToModelContext) { - return (element.className && getEntityFromElement(element)) || - element.contentEditable == 'false' // For readonly element, treat as an entity + return isEntityElement(element) || element.contentEditable == 'false' // For readonly element, treat as an entity ? context.elementProcessors.entity : null; } diff --git a/packages-content-model/roosterjs-content-model-dom/lib/domToModel/processors/entityProcessor.ts b/packages-content-model/roosterjs-content-model-dom/lib/domToModel/processors/entityProcessor.ts index f3b8a842568..b404137b30e 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/domToModel/processors/entityProcessor.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/domToModel/processors/entityProcessor.ts @@ -1,10 +1,10 @@ import { addBlock } from '../../modelApi/common/addBlock'; import { addSegment } from '../../modelApi/common/addSegment'; import { createEntity } from '../../modelApi/creators/createEntity'; -import { ElementProcessor } from 'roosterjs-content-model-types'; -import { getEntityFromElement } from 'roosterjs-editor-dom'; import { isBlockElement } from '../utils/isBlockElement'; +import { parseFormat } from '../utils/parseFormat'; import { stackFormat } from '../utils/stackFormat'; +import type { ElementProcessor } from 'roosterjs-content-model-types'; /** * Content Model Element Processor for entity @@ -13,17 +13,15 @@ import { stackFormat } from '../utils/stackFormat'; * @param context DOM to Content Model context */ export const entityProcessor: ElementProcessor = (group, element, context) => { - const entity = getEntityFromElement(element); - - // In Content Model we also treat read only element as an entity since we cannot edit it - const { id, type, isReadonly } = entity || { isReadonly: true }; const isBlockEntity = isBlockElement(element, context); stackFormat( context, { segment: isBlockEntity ? 'empty' : undefined, paragraph: 'empty' }, () => { - const entityModel = createEntity(element, isReadonly, type, context.segmentFormat, id); + const entityModel = createEntity(element, true /*isReadonly*/, context.segmentFormat); + + parseFormat(element, context.formatParsers.entity, entityModel.entityFormat, context); // TODO: Need to handle selection for editable entity if (context.isInSelection) { @@ -33,7 +31,8 @@ export const entityProcessor: ElementProcessor = (group, element, c if (isBlockEntity) { addBlock(group, entityModel); } else { - addSegment(group, entityModel); + const paragraph = addSegment(group, entityModel); + context.domIndexer?.onSegment(element, paragraph, [entityModel]); } } ); diff --git a/packages-content-model/roosterjs-content-model-dom/lib/domToModel/processors/fontProcessor.ts b/packages-content-model/roosterjs-content-model-dom/lib/domToModel/processors/fontProcessor.ts index 2906d2a5673..7074e89fddd 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/domToModel/processors/fontProcessor.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/domToModel/processors/fontProcessor.ts @@ -1,7 +1,7 @@ -import { ElementProcessor } from 'roosterjs-content-model-types'; import { isBlockElement } from '../utils/isBlockElement'; import { parseFormat } from '../utils/parseFormat'; import { stackFormat } from '../utils/stackFormat'; +import type { ElementProcessor } from 'roosterjs-content-model-types'; const FontSizes = ['10px', '13px', '16px', '18px', '24px', '32px', '48px']; diff --git a/packages-content-model/roosterjs-content-model-dom/lib/domToModel/processors/formatContainerProcessor.ts b/packages-content-model/roosterjs-content-model-dom/lib/domToModel/processors/formatContainerProcessor.ts index ee5c8904f0c..64969c2e8d3 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/domToModel/processors/formatContainerProcessor.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/domToModel/processors/formatContainerProcessor.ts @@ -5,7 +5,7 @@ import { getDefaultStyle } from '../utils/getDefaultStyle'; import { parseFormat } from '../utils/parseFormat'; import { setParagraphNotImplicit } from '../../modelApi/block/setParagraphNotImplicit'; import { stackFormat } from '../utils/stackFormat'; -import { +import type { ContentModelFormatContainer, ContentModelFormatContainerFormat, ContentModelParagraph, diff --git a/packages-content-model/roosterjs-content-model-dom/lib/domToModel/processors/generalProcessor.ts b/packages-content-model/roosterjs-content-model-dom/lib/domToModel/processors/generalProcessor.ts index cf176003e3f..54bce7a6ea2 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/domToModel/processors/generalProcessor.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/domToModel/processors/generalProcessor.ts @@ -3,9 +3,9 @@ import { addDecorators } from '../../modelApi/common/addDecorators'; import { addSegment } from '../../modelApi/common/addSegment'; import { createGeneralBlock } from '../../modelApi/creators/createGeneralBlock'; import { createGeneralSegment } from '../../modelApi/creators/createGeneralSegment'; -import { ElementProcessor } from 'roosterjs-content-model-types'; import { isBlockElement } from '../utils/isBlockElement'; import { stackFormat } from '../utils/stackFormat'; +import type { ElementProcessor } from 'roosterjs-content-model-types'; const generalBlockProcessor: ElementProcessor = (group, element, context) => { const block = createGeneralBlock(element); @@ -35,7 +35,8 @@ const generalSegmentProcessor: ElementProcessor = (group, element, const isSelectedBefore = context.isInSelection; addDecorators(segment, context); - addSegment(group, segment); + const paragraph = addSegment(group, segment); + context.domIndexer?.onSegment(element, paragraph, [segment]); stackFormat( context, diff --git a/packages-content-model/roosterjs-content-model-dom/lib/domToModel/processors/headingProcessor.ts b/packages-content-model/roosterjs-content-model-dom/lib/domToModel/processors/headingProcessor.ts index 2183c717f8c..01ddb8a025d 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/domToModel/processors/headingProcessor.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/domToModel/processors/headingProcessor.ts @@ -1,11 +1,11 @@ import { addBlock } from '../../modelApi/common/addBlock'; import { blockProcessor } from './blockProcessor'; -import { ContentModelSegmentFormat, ElementProcessor } from 'roosterjs-content-model-types'; import { createParagraph } from '../../modelApi/creators/createParagraph'; import { createParagraphDecorator } from '../../modelApi/creators/createParagraphDecorator'; -import { getObjectKeys } from 'roosterjs-editor-dom'; +import { getObjectKeys } from '../../domUtils/getObjectKeys'; import { parseFormat } from '../utils/parseFormat'; import { stackFormat } from '../utils/stackFormat'; +import type { ContentModelSegmentFormat, ElementProcessor } from 'roosterjs-content-model-types'; /** * @internal diff --git a/packages-content-model/roosterjs-content-model-dom/lib/domToModel/processors/hrProcessor.ts b/packages-content-model/roosterjs-content-model-dom/lib/domToModel/processors/hrProcessor.ts index 0455dd99622..5e8676be4a2 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/domToModel/processors/hrProcessor.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/domToModel/processors/hrProcessor.ts @@ -1,8 +1,8 @@ import { addBlock } from '../../modelApi/common/addBlock'; import { createDivider } from '../../modelApi/creators/createDivider'; -import { ElementProcessor } from 'roosterjs-content-model-types'; import { parseFormat } from '../utils/parseFormat'; import { stackFormat } from '../utils/stackFormat'; +import type { ElementProcessor } from 'roosterjs-content-model-types'; /** * @internal diff --git a/packages-content-model/roosterjs-content-model-dom/lib/domToModel/processors/imageProcessor.ts b/packages-content-model/roosterjs-content-model-dom/lib/domToModel/processors/imageProcessor.ts index 5d1aae1ee7f..df62e8361af 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/domToModel/processors/imageProcessor.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/domToModel/processors/imageProcessor.ts @@ -1,10 +1,9 @@ import { addDecorators } from '../../modelApi/common/addDecorators'; import { addSegment } from '../../modelApi/common/addSegment'; -import { ContentModelImageFormat, ElementProcessor } from 'roosterjs-content-model-types'; import { createImage } from '../../modelApi/creators/createImage'; import { parseFormat } from '../utils/parseFormat'; -import { SelectionRangeTypes } from 'roosterjs-editor-types'; import { stackFormat } from '../utils/stackFormat'; +import type { ContentModelImageFormat, ElementProcessor } from 'roosterjs-content-model-types'; /** * @internal @@ -33,14 +32,12 @@ export const imageProcessor: ElementProcessor = (group, elemen if (context.isInSelection) { image.isSelected = true; } - if ( - context.rangeEx?.type == SelectionRangeTypes.ImageSelection && - context.rangeEx.image == element - ) { + if (context.selection?.type == 'image' && context.selection.image == element) { image.isSelectedAsImageSelection = true; image.isSelected = true; } - addSegment(group, image); + const paragraph = addSegment(group, image); + context.domIndexer?.onSegment(element, paragraph, [image]); }); }; diff --git a/packages-content-model/roosterjs-content-model-dom/lib/domToModel/processors/knownElementProcessor.ts b/packages-content-model/roosterjs-content-model-dom/lib/domToModel/processors/knownElementProcessor.ts index 7546d4a44c3..aee5c57366c 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/domToModel/processors/knownElementProcessor.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/domToModel/processors/knownElementProcessor.ts @@ -6,7 +6,7 @@ import { getDefaultStyle } from '../utils/getDefaultStyle'; import { isBlockElement } from '../utils/isBlockElement'; import { parseFormat } from '../utils/parseFormat'; import { stackFormat } from '../utils/stackFormat'; -import { +import type { ContentModelSegmentFormat, DomToModelContext, ElementProcessor, diff --git a/packages-content-model/roosterjs-content-model-dom/lib/domToModel/processors/linkProcessor.ts b/packages-content-model/roosterjs-content-model-dom/lib/domToModel/processors/linkProcessor.ts index 46606df52b7..bba3524b2cc 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/domToModel/processors/linkProcessor.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/domToModel/processors/linkProcessor.ts @@ -1,7 +1,7 @@ -import { ElementProcessor } from 'roosterjs-content-model-types'; import { knownElementProcessor } from './knownElementProcessor'; import { parseFormat } from '../utils/parseFormat'; import { stackFormat } from '../utils/stackFormat'; +import type { ElementProcessor } from 'roosterjs-content-model-types'; /** * @internal diff --git a/packages-content-model/roosterjs-content-model-dom/lib/domToModel/processors/listItemProcessor.ts b/packages-content-model/roosterjs-content-model-dom/lib/domToModel/processors/listItemProcessor.ts index 6365870b869..2feaef7f5d9 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/domToModel/processors/listItemProcessor.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/domToModel/processors/listItemProcessor.ts @@ -1,7 +1,7 @@ import { createListItem } from '../../modelApi/creators/createListItem'; -import { ElementProcessor } from 'roosterjs-content-model-types'; import { parseFormat } from '../utils/parseFormat'; import { stackFormat } from '../utils/stackFormat'; +import type { ElementProcessor } from 'roosterjs-content-model-types'; /** * @internal diff --git a/packages-content-model/roosterjs-content-model-dom/lib/domToModel/processors/listProcessor.ts b/packages-content-model/roosterjs-content-model-dom/lib/domToModel/processors/listProcessor.ts index f6b36840188..6c9bb6dc827 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/domToModel/processors/listProcessor.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/domToModel/processors/listProcessor.ts @@ -1,9 +1,9 @@ -import { ContentModelListLevel, ElementProcessor } from 'roosterjs-content-model-types'; import { createListLevel } from '../../modelApi/creators/createListLevel'; import { listLevelMetadataFormatHandler } from '../../formatHandlers/list/listLevelMetadataFormatHandler'; import { parseFormat } from '../utils/parseFormat'; import { stackFormat } from '../utils/stackFormat'; import { updateListMetadata } from '../../domUtils/metadata/updateListMetadata'; +import type { ContentModelListLevel, ElementProcessor } from 'roosterjs-content-model-types'; /** * @internal diff --git a/packages-content-model/roosterjs-content-model-dom/lib/domToModel/processors/pProcessor.ts b/packages-content-model/roosterjs-content-model-dom/lib/domToModel/processors/pProcessor.ts index a568ca8c505..a0deefaa683 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/domToModel/processors/pProcessor.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/domToModel/processors/pProcessor.ts @@ -1,10 +1,10 @@ import { addBlock } from '../../modelApi/common/addBlock'; import { blockProcessor } from './blockProcessor'; -import { ContentModelSegmentFormat, ElementProcessor } from 'roosterjs-content-model-types'; import { createParagraph } from '../../modelApi/creators/createParagraph'; import { createParagraphDecorator } from '../../modelApi/creators/createParagraphDecorator'; import { parseFormat } from '../utils/parseFormat'; import { stackFormat } from '../utils/stackFormat'; +import type { ContentModelSegmentFormat, ElementProcessor } from 'roosterjs-content-model-types'; /** * @internal diff --git a/packages-content-model/roosterjs-content-model-dom/lib/domToModel/processors/tableProcessor.ts b/packages-content-model/roosterjs-content-model-dom/lib/domToModel/processors/tableProcessor.ts index 80711b09a80..026a3ed51d9 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/domToModel/processors/tableProcessor.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/domToModel/processors/tableProcessor.ts @@ -2,11 +2,11 @@ import { addBlock } from '../../modelApi/common/addBlock'; import { createTable } from '../../modelApi/creators/createTable'; import { createTableCell } from '../../modelApi/creators/createTableCell'; import { getBoundingClientRect } from '../utils/getBoundingClientRect'; +import { isElementOfType } from '../../domUtils/isElementOfType'; +import { isNodeOfType } from '../../domUtils/isNodeOfType'; import { parseFormat } from '../utils/parseFormat'; -import { safeInstanceOf } from 'roosterjs-editor-dom'; -import { SelectionRangeTypes } from 'roosterjs-editor-types'; import { stackFormat } from '../utils/stackFormat'; -import { +import type { ContentModelTableCellFormat, DatasetFormat, ElementProcessor, @@ -41,21 +41,16 @@ export const tableProcessor: ElementProcessor = ( parseFormat(tableElement, context.formatParsers.block, context.blockFormat, context); const table = createTable(tableElement.rows.length, context.blockFormat); - const tableSelection = - context.rangeEx?.type == SelectionRangeTypes.TableSelection - ? context.rangeEx - : null; + const tableSelection = context.selection?.type == 'table' ? context.selection : null; const selectedTable = tableSelection?.table; - const coordinates = tableSelection?.coordinates; - const hasTableSelection = - selectedTable == tableElement && - !!coordinates?.firstCell && - !!coordinates?.lastCell; + const hasTableSelection = selectedTable == tableElement; if (context.allowCacheElement) { table.cachedElement = tableElement; } + context.domIndexer?.onTable(tableElement, table); + parseFormat(tableElement, context.formatParsers.table, table.format, context); parseFormat(tableElement, context.formatParsers.tableBorder, table.format, context); parseFormat( @@ -77,7 +72,12 @@ export const tableProcessor: ElementProcessor = ( const tbody = tr.parentNode; - if (safeInstanceOf(tbody, 'HTMLTableSectionElement')) { + if ( + isNodeOfType(tbody, 'ELEMENT_NODE') && + (isElementOfType(tbody, 'tbody') || + isElementOfType(tbody, 'thead') || + isElementOfType(tbody, 'tfoot')) + ) { parseFormat(tbody, context.formatParsers.tableRow, tableRow.format, context); } else if (context.allowCacheElement) { tableRow.cachedElement = tr; @@ -227,10 +227,11 @@ export const tableProcessor: ElementProcessor = ( if ( (hasSelectionBeforeCell && hasSelectionAfterCell) || (hasTableSelection && - row >= coordinates.firstCell.y && - row <= coordinates.lastCell.y && - targetCol >= coordinates.firstCell.x && - targetCol <= coordinates.lastCell.x) + tableSelection && + row >= tableSelection.firstRow && + row <= tableSelection.lastRow && + targetCol >= tableSelection.firstColumn && + targetCol <= tableSelection.lastColumn) ) { cell.isSelected = true; } diff --git a/packages-content-model/roosterjs-content-model-dom/lib/domToModel/processors/textProcessor.ts b/packages-content-model/roosterjs-content-model-dom/lib/domToModel/processors/textProcessor.ts index adda5bcc132..0b7f3515a39 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/domToModel/processors/textProcessor.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/domToModel/processors/textProcessor.ts @@ -1,12 +1,14 @@ import { addDecorators } from '../../modelApi/common/addDecorators'; import { addSegment } from '../../modelApi/common/addSegment'; import { addSelectionMarker } from '../utils/addSelectionMarker'; -import { areSameFormats } from '../utils/areSameFormats'; import { createText } from '../../modelApi/creators/createText'; +import { ensureParagraph } from '../../modelApi/common/ensureParagraph'; import { getRegularSelectionOffsets } from '../utils/getRegularSelectionOffsets'; import { hasSpacesOnly } from '../../modelApi/common/hasSpacesOnly'; -import { +import type { ContentModelBlockGroup, + ContentModelParagraph, + ContentModelText, DomToModelContext, ElementProcessor, } from 'roosterjs-content-model-types'; @@ -21,9 +23,12 @@ export const textProcessor: ElementProcessor = ( ) => { let txt = textNode.nodeValue || ''; let [txtStartOffset, txtEndOffset] = getRegularSelectionOffsets(context, textNode); + const segments: (ContentModelText | undefined)[] = []; + const paragraph = ensureParagraph(group, context.blockFormat); if (txtStartOffset >= 0) { - addTextSegment(group, txt.substring(0, txtStartOffset), context); + const subText = txt.substring(0, txtStartOffset); + segments.push(addTextSegment(group, subText, paragraph, context)); context.isInSelection = true; addSelectionMarker(group, context); @@ -33,9 +38,13 @@ export const textProcessor: ElementProcessor = ( } if (txtEndOffset >= 0) { - addTextSegment(group, txt.substring(0, txtEndOffset), context); + const subText = txt.substring(0, txtEndOffset); + segments.push(addTextSegment(group, subText, paragraph, context)); - if (context.rangeEx && !context.rangeEx.areAllCollapsed) { + if ( + context.selection && + (context.selection.type != 'range' || !context.selection.range.collapsed) + ) { addSelectionMarker(group, context); } @@ -43,32 +52,32 @@ export const textProcessor: ElementProcessor = ( txt = txt.substring(txtEndOffset); } - addTextSegment(group, txt, context); + segments.push(addTextSegment(group, txt, paragraph, context)); + context.domIndexer?.onSegment( + textNode, + paragraph, + segments.filter((x): x is ContentModelText => !!x) + ); }; // When we see these values of white-space style, need to preserve spaces and line-breaks and let browser handle it for us. const WhiteSpaceValuesNeedToHandle = ['pre', 'pre-wrap', 'pre-line', 'break-spaces']; -function addTextSegment(group: ContentModelBlockGroup, text: string, context: DomToModelContext) { - if (text) { - const lastBlock = group.blocks[group.blocks.length - 1]; - const paragraph = lastBlock?.blockType == 'Paragraph' ? lastBlock : null; - const lastSegment = paragraph?.segments[paragraph.segments.length - 1]; +function addTextSegment( + group: ContentModelBlockGroup, + text: string, + paragraph: ContentModelParagraph, + context: DomToModelContext +): ContentModelText | undefined { + let textModel: ContentModelText | undefined; + if (text) { if ( - lastSegment?.segmentType == 'Text' && - !!lastSegment.isSelected == !!context.isInSelection && - areSameFormats(lastSegment.format, context.segmentFormat) && - areSameFormats(lastSegment.link || {}, context.link.format || {}) && - areSameFormats(lastSegment.code || {}, context.code.format || {}) - ) { - lastSegment.text += text; - } else if ( !hasSpacesOnly(text) || - paragraph?.segments.length! > 0 || + (paragraph?.segments.length ?? 0) > 0 || WhiteSpaceValuesNeedToHandle.indexOf(paragraph?.format.whiteSpace || '') >= 0 ) { - const textModel = createText(text, context.segmentFormat); + textModel = createText(text, context.segmentFormat); if (context.isInSelection) { textModel.isSelected = true; @@ -79,4 +88,6 @@ function addTextSegment(group: ContentModelBlockGroup, text: string, context: Do addSegment(group, textModel, context.blockFormat); } } + + return textModel; } diff --git a/packages-content-model/roosterjs-content-model-dom/lib/domToModel/utils/addSelectionMarker.ts b/packages-content-model/roosterjs-content-model-dom/lib/domToModel/utils/addSelectionMarker.ts index c39c5c41152..86234b6faab 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/domToModel/utils/addSelectionMarker.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/domToModel/utils/addSelectionMarker.ts @@ -1,7 +1,7 @@ import { addDecorators } from '../../modelApi/common/addDecorators'; import { addSegment } from '../../modelApi/common/addSegment'; -import { ContentModelBlockGroup, DomToModelContext } from 'roosterjs-content-model-types'; import { createSelectionMarker } from '../../modelApi/creators/createSelectionMarker'; +import type { ContentModelBlockGroup, DomToModelContext } from 'roosterjs-content-model-types'; /** * @internal diff --git a/packages-content-model/roosterjs-content-model-dom/lib/domToModel/utils/areSameFormats.ts b/packages-content-model/roosterjs-content-model-dom/lib/domToModel/utils/areSameFormats.ts index 4d8c0f13970..af62fc6790a 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/domToModel/utils/areSameFormats.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/domToModel/utils/areSameFormats.ts @@ -1,5 +1,5 @@ -import { ContentModelFormatBase } from 'roosterjs-content-model-types'; -import { getObjectKeys } from 'roosterjs-editor-dom'; +import { getObjectKeys } from '../../domUtils/getObjectKeys'; +import type { ContentModelFormatBase } from 'roosterjs-content-model-types'; /** * Check if the two given formats object are equal. This is a check to value but not to reference diff --git a/packages-content-model/roosterjs-content-model-dom/lib/domToModel/utils/getDefaultStyle.ts b/packages-content-model/roosterjs-content-model-dom/lib/domToModel/utils/getDefaultStyle.ts index 2bd50107c05..dfb6beade5f 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/domToModel/utils/getDefaultStyle.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/domToModel/utils/getDefaultStyle.ts @@ -1,5 +1,5 @@ import { defaultHTMLStyleMap } from '../../config/defaultHTMLStyleMap'; -import { DefaultStyleMap, DomToModelContext } from 'roosterjs-content-model-types'; +import type { DefaultStyleMap, DomToModelContext } from 'roosterjs-content-model-types'; /** * @internal diff --git a/packages-content-model/roosterjs-content-model-dom/lib/domToModel/utils/getRegularSelectionOffsets.ts b/packages-content-model/roosterjs-content-model-dom/lib/domToModel/utils/getRegularSelectionOffsets.ts index dfd7a125443..a6e43eac12e 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/domToModel/utils/getRegularSelectionOffsets.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/domToModel/utils/getRegularSelectionOffsets.ts @@ -1,5 +1,4 @@ -import { DomToModelContext } from 'roosterjs-content-model-types'; -import { SelectionRangeTypes } from 'roosterjs-editor-types'; +import type { DomToModelContext } from 'roosterjs-content-model-types'; /** * Get offset numbers of a regular (range based) selection. @@ -12,8 +11,7 @@ export function getRegularSelectionOffsets( context: DomToModelContext, currentContainer: Node ): [number, number] { - const range = - context.rangeEx?.type == SelectionRangeTypes.Normal ? context.rangeEx.ranges[0] : null; + const range = context.selection?.type == 'range' ? context.selection.range : null; let startOffset = range?.startContainer == currentContainer ? range.startOffset : -1; let endOffset = range?.endContainer == currentContainer ? range.endOffset! : -1; diff --git a/packages-content-model/roosterjs-content-model-dom/lib/domToModel/utils/isBlockElement.ts b/packages-content-model/roosterjs-content-model-dom/lib/domToModel/utils/isBlockElement.ts index 5f9b4d10a04..33464757f26 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/domToModel/utils/isBlockElement.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/domToModel/utils/isBlockElement.ts @@ -1,5 +1,5 @@ -import { DomToModelContext } from 'roosterjs-content-model-types'; import { getDefaultStyle } from './getDefaultStyle'; +import type { DomToModelContext } from 'roosterjs-content-model-types'; const BLOCK_DISPLAY_STYLES = ['block', 'list-item', 'table', 'table-cell', 'flex']; diff --git a/packages-content-model/roosterjs-content-model-dom/lib/domToModel/utils/parseFormat.ts b/packages-content-model/roosterjs-content-model-dom/lib/domToModel/utils/parseFormat.ts index 9d9a1719ef4..e5ab93d642e 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/domToModel/utils/parseFormat.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/domToModel/utils/parseFormat.ts @@ -1,5 +1,5 @@ import { getDefaultStyle } from './getDefaultStyle'; -import { +import type { ContentModelFormatBase, DomToModelContext, FormatParser, diff --git a/packages-content-model/roosterjs-content-model-dom/lib/domToModel/utils/stackFormat.ts b/packages-content-model/roosterjs-content-model-dom/lib/domToModel/utils/stackFormat.ts index 5c4bbe36ff7..a23f21fa24c 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/domToModel/utils/stackFormat.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/domToModel/utils/stackFormat.ts @@ -1,5 +1,5 @@ -import { getObjectKeys } from 'roosterjs-editor-dom'; -import { +import { getObjectKeys } from '../../domUtils/getObjectKeys'; +import type { ContentModelBlockFormat, ContentModelCode, ContentModelFormatBase, diff --git a/packages-content-model/roosterjs-content-model-dom/lib/domUtils/entityUtils.ts b/packages-content-model/roosterjs-content-model-dom/lib/domUtils/entityUtils.ts new file mode 100644 index 00000000000..7efd8fad0b0 --- /dev/null +++ b/packages-content-model/roosterjs-content-model-dom/lib/domUtils/entityUtils.ts @@ -0,0 +1,43 @@ +import { isNodeOfType } from './isNodeOfType'; +import type { ContentModelEntityFormat } from 'roosterjs-content-model-types'; + +const ENTITY_INFO_NAME = '_Entity'; +const ENTITY_TYPE_PREFIX = '_EType_'; +const ENTITY_ID_PREFIX = '_EId_'; +const ENTITY_READONLY_PREFIX = '_EReadonly_'; + +/** + * @internal + */ +export function isEntityElement(node: Node): boolean { + return isNodeOfType(node, 'ELEMENT_NODE') && node.classList.contains(ENTITY_INFO_NAME); +} + +/** + * @internal + */ +export function parseEntityClassName( + className: string, + format: ContentModelEntityFormat +): boolean | undefined { + if (className == ENTITY_INFO_NAME) { + return true; + } else if (className.indexOf(ENTITY_TYPE_PREFIX) == 0) { + format.entityType = className.substring(ENTITY_TYPE_PREFIX.length); + } else if (className.indexOf(ENTITY_ID_PREFIX) == 0) { + format.id = className.substring(ENTITY_ID_PREFIX.length); + } else if (className.indexOf(ENTITY_READONLY_PREFIX) == 0) { + format.isReadonly = className.substring(ENTITY_READONLY_PREFIX.length) == '1'; + } +} + +/** + * @internal + */ +export function generateEntityClassNames(format: ContentModelEntityFormat): string { + return format.isFakeEntity + ? '' + : `${ENTITY_INFO_NAME} ${ENTITY_TYPE_PREFIX}${format.entityType ?? ''} ${ + format.id ? `${ENTITY_ID_PREFIX}${format.id} ` : '' + }${ENTITY_READONLY_PREFIX}${format.isReadonly ? '1' : '0'}`; +} diff --git a/packages-content-model/roosterjs-content-model-dom/lib/domUtils/getObjectKeys.ts b/packages-content-model/roosterjs-content-model-dom/lib/domUtils/getObjectKeys.ts new file mode 100644 index 00000000000..ba99a57bdee --- /dev/null +++ b/packages-content-model/roosterjs-content-model-dom/lib/domUtils/getObjectKeys.ts @@ -0,0 +1,10 @@ +/** + * Provide a strong-typed version of Object.keys() + * @param obj The source object + * @returns Array of keys + */ +export function getObjectKeys( + obj: Record | Partial> +): T[] { + return Object.keys(obj) as T[]; +} diff --git a/packages-content-model/roosterjs-content-model-dom/lib/domUtils/isElementOfType.ts b/packages-content-model/roosterjs-content-model-dom/lib/domUtils/isElementOfType.ts new file mode 100644 index 00000000000..2ae21cc313e --- /dev/null +++ b/packages-content-model/roosterjs-content-model-dom/lib/domUtils/isElementOfType.ts @@ -0,0 +1,12 @@ +/** + * Check if the given element is of the type that we are checking according to its tag name + * @param element The element to check + * @param tag The HTML tag name to check + * @returns True if the element has the given tag, otherwise false + */ +export function isElementOfType( + element: HTMLElement, + tag: Tag +): element is HTMLElementTagNameMap[Tag] { + return element?.tagName?.toLocaleLowerCase() == tag; +} diff --git a/packages-content-model/roosterjs-content-model-dom/lib/domUtils/isNodeOfType.ts b/packages-content-model/roosterjs-content-model-dom/lib/domUtils/isNodeOfType.ts index cef3488a8bc..d6a2e08462b 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/domUtils/isNodeOfType.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/domUtils/isNodeOfType.ts @@ -1,5 +1,3 @@ -import { NodeType } from 'roosterjs-editor-types'; - /** * A type map from node type number to its type declaration. This is used by utility function isNodeOfType() */ @@ -7,41 +5,42 @@ export interface NodeTypeMap { /** * Attribute node */ - [NodeType.Attribute]: Attr; + ATTRIBUTE_NODE: Attr; /** * Comment node */ - [NodeType.Comment]: Comment; + COMMENT_NODE: Comment; /** * DocumentFragment node */ - [NodeType.DocumentFragment]: DocumentFragment; + DOCUMENT_FRAGMENT_NODE: DocumentFragment; /** * Document node */ - [NodeType.Document]: Document; + DOCUMENT_NODE: Document; /** * DocumentType node */ - [NodeType.DocumentType]: DocumentType; + DOCUMENT_TYPE_NODE: DocumentType; /** * HTMLElement node */ - [NodeType.Element]: HTMLElement; + ELEMENT_NODE: HTMLElement; + /** * ProcessingInstruction node */ - [NodeType.ProcessingInstruction]: ProcessingInstruction; + PROCESSING_INSTRUCTION_NODE: ProcessingInstruction; /** * Text node */ - [NodeType.Text]: Text; + TEXT_NODE: Text; } /** @@ -49,9 +48,9 @@ export interface NodeTypeMap { * @param node The node to check * @param expectedType The type to check */ -export function isNodeOfType( +export function isNodeOfType( node: Node | null | undefined, expectedType: T ): node is NodeTypeMap[T] { - return !!node && node.nodeType == expectedType; + return !!node && node.nodeType == Node[expectedType]; } diff --git a/packages-content-model/roosterjs-content-model-dom/lib/domUtils/metadata/updateListMetadata.ts b/packages-content-model/roosterjs-content-model-dom/lib/domUtils/metadata/updateListMetadata.ts index ac9194fc2c1..766198eefbf 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/domUtils/metadata/updateListMetadata.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/domUtils/metadata/updateListMetadata.ts @@ -1,7 +1,7 @@ import { BulletListType, NumberingListType } from 'roosterjs-editor-types'; -import { ContentModelWithDataset, ListMetadataFormat } from 'roosterjs-content-model-types'; import { createNumberDefinition, createObjectDefinition } from 'roosterjs-editor-dom'; import { updateMetadata } from './updateMetadata'; +import type { ContentModelWithDataset, ListMetadataFormat } from 'roosterjs-content-model-types'; const ListStyleDefinitionMetadata = createObjectDefinition( { diff --git a/packages-content-model/roosterjs-content-model-dom/lib/domUtils/metadata/updateMetadata.ts b/packages-content-model/roosterjs-content-model-dom/lib/domUtils/metadata/updateMetadata.ts index f54cb7bda5a..9dbdc2d0df8 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/domUtils/metadata/updateMetadata.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/domUtils/metadata/updateMetadata.ts @@ -1,6 +1,6 @@ -import { ContentModelWithDataset } from 'roosterjs-content-model-types'; -import { Definition } from 'roosterjs-editor-types'; import { validate } from 'roosterjs-editor-dom'; +import type { ContentModelWithDataset } from 'roosterjs-content-model-types'; +import type { Definition } from 'roosterjs-editor-types'; const EditingInfoDatasetName = 'editingInfo'; diff --git a/packages-content-model/roosterjs-content-model-dom/lib/domUtils/moveChildNodes.ts b/packages-content-model/roosterjs-content-model-dom/lib/domUtils/moveChildNodes.ts new file mode 100644 index 00000000000..3202f06d1f1 --- /dev/null +++ b/packages-content-model/roosterjs-content-model-dom/lib/domUtils/moveChildNodes.ts @@ -0,0 +1,37 @@ +/** + * Replace all child nodes of the given target node to the child nodes of source node. + * @param target Target node, all child nodes of this node will be removed if keepExistingChildren is not set to true + * @param source (Optional) source node, all child nodes of this node will be move to target node + * @param keepExistingChildren (Optional) When set to true, all existing child nodes of target will be kept + */ +export function moveChildNodes(target: Node, source?: Node, keepExistingChildren?: boolean) { + if (!target) { + return; + } + + while (!keepExistingChildren && target.firstChild) { + target.removeChild(target.firstChild); + } + + while (source?.firstChild) { + target.appendChild(source.firstChild); + } +} + +/** + * Wrap all child nodes of the given parent element using a new element with the given tag name + * @param parent The parent element + * @param tagName The tag name of new wrapper + * @returns New wrapper element + */ +export function wrapAllChildNodes( + parent: HTMLElement, + tagName: T +): HTMLElementTagNameMap[T] { + const newElement = parent.ownerDocument.createElement(tagName); + + moveChildNodes(newElement, parent); + parent.appendChild(newElement); + + return newElement; +} diff --git a/packages-content-model/roosterjs-content-model-dom/lib/domUtils/toArray.ts b/packages-content-model/roosterjs-content-model-dom/lib/domUtils/toArray.ts new file mode 100644 index 00000000000..f14f2924149 --- /dev/null +++ b/packages-content-model/roosterjs-content-model-dom/lib/domUtils/toArray.ts @@ -0,0 +1,35 @@ +/** + * Convert a named node map to an array + * @param collection The map to convert + */ +export default function toArray(collection: NamedNodeMap): Attr[]; + +/** + * Convert a named node map to an array + * @param collection The map to convert + */ +export default function toArray(collection: DataTransferItemList): DataTransferItem[]; + +/** + * Convert a collection to an array + * @param collection The collection to convert + */ +export default function toArray(collection: NodeListOf): T[]; + +/** + * Convert a collection to an array + * @param collection The collection to convert + */ +export default function toArray(collection: HTMLCollectionOf): T[]; + +/** + * Convert an array to an array. + * This is to satisfy typescript compiler. For some cases the object can be a collection at runtime, + * but the declaration is an array. e.g. ClipboardData.types + * @param array The array to convert + */ +export default function toArray(array: readonly T[]): T[]; + +export default function toArray(collection: any): any[] { + return [].slice.call(collection); +} diff --git a/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/FormatHandler.ts b/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/FormatHandler.ts index b839644df4b..d62abea4d1e 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/FormatHandler.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/FormatHandler.ts @@ -1,4 +1,8 @@ -import { ContentModelFormatBase, FormatApplier, FormatParser } from 'roosterjs-content-model-types'; +import type { + ContentModelFormatBase, + FormatApplier, + FormatParser, +} from 'roosterjs-content-model-types'; /** * @internal diff --git a/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/block/directionFormatHandler.ts b/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/block/directionFormatHandler.ts index bd71b0bc6ff..eb136472a97 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/block/directionFormatHandler.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/block/directionFormatHandler.ts @@ -1,5 +1,5 @@ -import { DirectionFormat } from 'roosterjs-content-model-types'; -import { FormatHandler } from '../FormatHandler'; +import type { DirectionFormat } from 'roosterjs-content-model-types'; +import type { FormatHandler } from '../FormatHandler'; /** * @internal diff --git a/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/block/displayFormatHandler.ts b/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/block/displayFormatHandler.ts index 014c0a4abb4..129778db52a 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/block/displayFormatHandler.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/block/displayFormatHandler.ts @@ -1,5 +1,5 @@ -import { DisplayFormat } from 'roosterjs-content-model-types'; -import { FormatHandler } from '../FormatHandler'; +import type { DisplayFormat } from 'roosterjs-content-model-types'; +import type { FormatHandler } from '../FormatHandler'; /** * @internal diff --git a/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/block/htmlAlignFormatHandler.ts b/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/block/htmlAlignFormatHandler.ts index 90e29c8866e..e10603fccf9 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/block/htmlAlignFormatHandler.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/block/htmlAlignFormatHandler.ts @@ -1,7 +1,11 @@ import { calcAlign, ResultMap } from '../utils/dir'; -import { DirectionFormat, HtmlAlignFormat, TextAlignFormat } from 'roosterjs-content-model-types'; import { directionFormatHandler } from './directionFormatHandler'; -import { FormatHandler } from '../FormatHandler'; +import type { + DirectionFormat, + HtmlAlignFormat, + TextAlignFormat, +} from 'roosterjs-content-model-types'; +import type { FormatHandler } from '../FormatHandler'; /** * @internal diff --git a/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/block/lineHeightFormatHandler.ts b/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/block/lineHeightFormatHandler.ts index b5197467a91..1b587e0e8ec 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/block/lineHeightFormatHandler.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/block/lineHeightFormatHandler.ts @@ -1,5 +1,5 @@ -import { FormatHandler } from '../FormatHandler'; -import { LineHeightFormat } from 'roosterjs-content-model-types'; +import type { FormatHandler } from '../FormatHandler'; +import type { LineHeightFormat } from 'roosterjs-content-model-types'; /** * @internal diff --git a/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/block/marginFormatHandler.ts b/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/block/marginFormatHandler.ts index 2bf8ce329b0..823735279b9 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/block/marginFormatHandler.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/block/marginFormatHandler.ts @@ -1,6 +1,6 @@ -import { FormatHandler } from '../FormatHandler'; -import { MarginFormat } from 'roosterjs-content-model-types'; import { parseValueWithUnit } from '../utils/parseValueWithUnit'; +import type { FormatHandler } from '../FormatHandler'; +import type { MarginFormat } from 'roosterjs-content-model-types'; const MarginKeys: (keyof MarginFormat & keyof CSSStyleDeclaration)[] = [ 'marginTop', diff --git a/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/block/paddingFormatHandler.ts b/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/block/paddingFormatHandler.ts index 80aad99f2de..bbf96aa91ef 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/block/paddingFormatHandler.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/block/paddingFormatHandler.ts @@ -1,5 +1,5 @@ -import { FormatHandler } from '../FormatHandler'; -import { PaddingFormat } from 'roosterjs-content-model-types'; +import type { FormatHandler } from '../FormatHandler'; +import type { PaddingFormat } from 'roosterjs-content-model-types'; const PaddingKeys: (keyof PaddingFormat & keyof CSSStyleDeclaration)[] = [ 'paddingTop', diff --git a/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/block/textAlignFormatHandler.ts b/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/block/textAlignFormatHandler.ts index 2f2febaf73b..a083ea91967 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/block/textAlignFormatHandler.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/block/textAlignFormatHandler.ts @@ -1,7 +1,7 @@ import { calcAlign, ResultMap } from '../utils/dir'; -import { DirectionFormat, TextAlignFormat } from 'roosterjs-content-model-types'; import { directionFormatHandler } from './directionFormatHandler'; -import { FormatHandler } from '../FormatHandler'; +import type { DirectionFormat, TextAlignFormat } from 'roosterjs-content-model-types'; +import type { FormatHandler } from '../FormatHandler'; /** * @internal diff --git a/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/block/whiteSpaceFormatHandler.ts b/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/block/whiteSpaceFormatHandler.ts index 83b764139fd..bc3934869b1 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/block/whiteSpaceFormatHandler.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/block/whiteSpaceFormatHandler.ts @@ -1,5 +1,5 @@ -import { FormatHandler } from '../FormatHandler'; -import { WhiteSpaceFormat } from 'roosterjs-content-model-types'; +import type { FormatHandler } from '../FormatHandler'; +import type { WhiteSpaceFormat } from 'roosterjs-content-model-types'; /** * @internal diff --git a/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/common/backgroundColorFormatHandler.ts b/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/common/backgroundColorFormatHandler.ts index 1b04cf9d11a..408bca73edd 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/common/backgroundColorFormatHandler.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/common/backgroundColorFormatHandler.ts @@ -1,6 +1,6 @@ -import { BackgroundColorFormat } from 'roosterjs-content-model-types'; -import { FormatHandler } from '../FormatHandler'; import { getColor, setColor } from '../utils/color'; +import type { BackgroundColorFormat } from 'roosterjs-content-model-types'; +import type { FormatHandler } from '../FormatHandler'; /** * @internal diff --git a/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/common/borderBoxFormatHandler.ts b/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/common/borderBoxFormatHandler.ts index 8b151faf408..131384aeb44 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/common/borderBoxFormatHandler.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/common/borderBoxFormatHandler.ts @@ -1,5 +1,5 @@ -import { BorderBoxFormat } from 'roosterjs-content-model-types'; -import { FormatHandler } from '../FormatHandler'; +import type { BorderBoxFormat } from 'roosterjs-content-model-types'; +import type { FormatHandler } from '../FormatHandler'; /** * @internal diff --git a/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/common/borderFormatHandler.ts b/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/common/borderFormatHandler.ts index 59a79d48751..5301c31c358 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/common/borderFormatHandler.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/common/borderFormatHandler.ts @@ -1,5 +1,5 @@ -import { BorderFormat } from 'roosterjs-content-model-types'; -import { FormatHandler } from '../FormatHandler'; +import type { BorderFormat } from 'roosterjs-content-model-types'; +import type { FormatHandler } from '../FormatHandler'; /** * Keys of border items diff --git a/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/common/boxShadowFormatHandler.ts b/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/common/boxShadowFormatHandler.ts index ef45d9c1a93..583490b73d1 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/common/boxShadowFormatHandler.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/common/boxShadowFormatHandler.ts @@ -1,5 +1,5 @@ -import { BoxShadowFormat } from 'roosterjs-content-model-types'; -import { FormatHandler } from '../FormatHandler'; +import type { BoxShadowFormat } from 'roosterjs-content-model-types'; +import type { FormatHandler } from '../FormatHandler'; /** * @internal diff --git a/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/common/datasetFormatHandler.ts b/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/common/datasetFormatHandler.ts index acbfff34735..10356e7093a 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/common/datasetFormatHandler.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/common/datasetFormatHandler.ts @@ -1,6 +1,6 @@ -import { DatasetFormat } from 'roosterjs-content-model-types'; -import { FormatHandler } from '../FormatHandler'; -import { getObjectKeys } from 'roosterjs-editor-dom'; +import { getObjectKeys } from '../../domUtils/getObjectKeys'; +import type { DatasetFormat } from 'roosterjs-content-model-types'; +import type { FormatHandler } from '../FormatHandler'; /** * @internal diff --git a/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/common/floatFormatHandler.ts b/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/common/floatFormatHandler.ts index 538e174ab89..cfcd84d20b2 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/common/floatFormatHandler.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/common/floatFormatHandler.ts @@ -1,5 +1,5 @@ -import { FloatFormat } from 'roosterjs-content-model-types'; -import { FormatHandler } from '../FormatHandler'; +import type { FloatFormat } from 'roosterjs-content-model-types'; +import type { FormatHandler } from '../FormatHandler'; /** * @internal diff --git a/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/common/idFormatHandler.ts b/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/common/idFormatHandler.ts index 9f2ce0002d8..8fca6b7c4d4 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/common/idFormatHandler.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/common/idFormatHandler.ts @@ -1,5 +1,5 @@ -import { FormatHandler } from '../FormatHandler'; -import { IdFormat } from 'roosterjs-content-model-types'; +import type { FormatHandler } from '../FormatHandler'; +import type { IdFormat } from 'roosterjs-content-model-types'; /** * @internal diff --git a/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/common/sizeFormatHandler.ts b/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/common/sizeFormatHandler.ts index 4a140b7a4a3..3a43767b51e 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/common/sizeFormatHandler.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/common/sizeFormatHandler.ts @@ -1,5 +1,5 @@ -import { FormatHandler } from '../FormatHandler'; -import { SizeFormat } from 'roosterjs-content-model-types'; +import type { FormatHandler } from '../FormatHandler'; +import type { SizeFormat } from 'roosterjs-content-model-types'; const PercentageRegex = /[\d\.]+%/; diff --git a/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/common/verticalAlignFormatHandler.ts b/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/common/verticalAlignFormatHandler.ts index 8a47f8455e4..15d0659baf4 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/common/verticalAlignFormatHandler.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/common/verticalAlignFormatHandler.ts @@ -1,5 +1,5 @@ -import { FormatHandler } from '../FormatHandler'; -import { VerticalAlignFormat } from 'roosterjs-content-model-types'; +import type { FormatHandler } from '../FormatHandler'; +import type { VerticalAlignFormat } from 'roosterjs-content-model-types'; /** * @internal diff --git a/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/common/wordBreakFormatHandler.ts b/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/common/wordBreakFormatHandler.ts index 331ce6602c5..c9718a8d768 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/common/wordBreakFormatHandler.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/common/wordBreakFormatHandler.ts @@ -1,5 +1,5 @@ -import { FormatHandler } from '../FormatHandler'; -import { WordBreakFormat } from 'roosterjs-content-model-types'; +import type { FormatHandler } from '../FormatHandler'; +import type { WordBreakFormat } from 'roosterjs-content-model-types'; /** * @internal diff --git a/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/defaultFormatHandlers.ts b/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/defaultFormatHandlers.ts index e4557daafb7..91d83de7c04 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/defaultFormatHandlers.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/defaultFormatHandlers.ts @@ -6,11 +6,11 @@ import { boxShadowFormatHandler } from './common/boxShadowFormatHandler'; import { datasetFormatHandler } from './common/datasetFormatHandler'; import { directionFormatHandler } from './block/directionFormatHandler'; import { displayFormatHandler } from './block/displayFormatHandler'; +import { entityFormatHandler } from './entity/entityFormatHandler'; import { floatFormatHandler } from './common/floatFormatHandler'; import { fontFamilyFormatHandler } from './segment/fontFamilyFormatHandler'; import { fontSizeFormatHandler } from './segment/fontSizeFormatHandler'; -import { FormatHandler } from './FormatHandler'; -import { getObjectKeys } from 'roosterjs-editor-dom'; +import { getObjectKeys } from '../domUtils/getObjectKeys'; import { htmlAlignFormatHandler } from './block/htmlAlignFormatHandler'; import { idFormatHandler } from './common/idFormatHandler'; import { italicFormatHandler } from './segment/italicFormatHandler'; @@ -34,7 +34,8 @@ import { underlineFormatHandler } from './segment/underlineFormatHandler'; import { verticalAlignFormatHandler } from './common/verticalAlignFormatHandler'; import { whiteSpaceFormatHandler } from './block/whiteSpaceFormatHandler'; import { wordBreakFormatHandler } from './common/wordBreakFormatHandler'; -import { +import type { FormatHandler } from './FormatHandler'; +import type { ContentModelFormatMap, FormatApplier, FormatAppliers, @@ -60,6 +61,7 @@ const defaultFormatHandlerMap: FormatHandlers = { float: floatFormatHandler, fontFamily: fontFamilyFormatHandler, fontSize: fontSizeFormatHandler, + entity: entityFormatHandler, htmlAlign: htmlAlignFormatHandler, id: idFormatHandler, italic: italicFormatHandler, @@ -196,6 +198,7 @@ export const defaultFormatKeysPerCategory: { dataset: ['dataset'], divider: [...sharedBlockFormats, ...sharedContainerFormats, 'display', 'size', 'htmlAlign'], container: [...sharedContainerFormats, 'htmlAlign', 'size', 'display'], + entity: ['entity'], }; /** diff --git a/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/entity/entityFormatHandler.ts b/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/entity/entityFormatHandler.ts new file mode 100644 index 00000000000..2c360c3f243 --- /dev/null +++ b/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/entity/entityFormatHandler.ts @@ -0,0 +1,33 @@ +import { generateEntityClassNames, parseEntityClassName } from '../../domUtils/entityUtils'; +import type { EntityInfoFormat, IdFormat } from 'roosterjs-content-model-types'; +import type { FormatHandler } from '../FormatHandler'; + +/** + * @internal + */ +export const entityFormatHandler: FormatHandler = { + parse: (format, element) => { + let isEntity = false; + + element.classList.forEach(name => { + isEntity = parseEntityClassName(name, format) || isEntity; + }); + + if (!isEntity) { + format.isFakeEntity = true; + format.isReadonly = !element.isContentEditable; + } + }, + + apply: (format, element) => { + if (!format.isFakeEntity) { + element.className = generateEntityClassNames(format); + } + + if (format.isReadonly) { + element.contentEditable = 'false'; + } else { + element.removeAttribute('contenteditable'); + } + }, +}; diff --git a/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/list/listItemMetadataFormatHandler.ts b/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/list/listItemMetadataFormatHandler.ts index f2c11520573..10089d5b94e 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/list/listItemMetadataFormatHandler.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/list/listItemMetadataFormatHandler.ts @@ -1,9 +1,8 @@ -import { FormatHandler } from '../FormatHandler'; -import { getObjectKeys, getTagOfNode } from 'roosterjs-editor-dom'; +import { getObjectKeys } from '../../domUtils/getObjectKeys'; import { isNodeOfType } from '../../domUtils/isNodeOfType'; -import { ListMetadataFormat } from 'roosterjs-content-model-types'; -import { NodeType } from 'roosterjs-editor-types'; import { OrderedMap, UnorderedMap } from './listLevelMetadataFormatHandler'; +import type { FormatHandler } from '../FormatHandler'; +import type { ListMetadataFormat } from 'roosterjs-content-model-types'; const OrderedMapPlaceholderRegex = /\$\{(\w+)\}/; const DefaultOrderedListStyles = ['decimal', 'lower-alpha', 'lower-roman']; @@ -36,8 +35,8 @@ export const listItemMetadataFormatHandler: FormatHandler = const parent = element.parentNode; const depth = context.listFormat.nodeStack.length - 2; // Minus two for the parent element and convert length to index - if (depth >= 0 && isNodeOfType(parent, NodeType.Element) && !parent.style.listStyleType) { - const parentTag = getTagOfNode(parent); + if (depth >= 0 && isNodeOfType(parent, 'ELEMENT_NODE') && !parent.style.listStyleType) { + const parentTag = parent.tagName; const style = parentTag == 'OL' ? getOrderedListStyleValue( diff --git a/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/list/listItemThreadFormatHandler.ts b/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/list/listItemThreadFormatHandler.ts index f9d65ebf20a..3797828d691 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/list/listItemThreadFormatHandler.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/list/listItemThreadFormatHandler.ts @@ -1,6 +1,7 @@ -import { FormatHandler } from '../FormatHandler'; -import { ListThreadFormat } from 'roosterjs-content-model-types'; -import { safeInstanceOf } from 'roosterjs-editor-dom'; +import { isElementOfType } from '../../domUtils/isElementOfType'; +import { isNodeOfType } from '../../domUtils/isNodeOfType'; +import type { FormatHandler } from '../FormatHandler'; +import type { ListThreadFormat } from 'roosterjs-content-model-types'; /** * @internal @@ -41,7 +42,8 @@ export const listItemThreadFormatHandler: FormatHandler = { function isLiUnderOl(element: HTMLElement) { return ( - safeInstanceOf(element, 'HTMLLIElement') && - safeInstanceOf(element.parentNode, 'HTMLOListElement') + isElementOfType(element, 'li') && + isNodeOfType(element.parentNode, 'ELEMENT_NODE') && + isElementOfType(element.parentNode, 'ol') ); } diff --git a/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/list/listLevelMetadataFormatHandler.ts b/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/list/listLevelMetadataFormatHandler.ts index be629dd2353..d7538d8fc8d 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/list/listLevelMetadataFormatHandler.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/list/listLevelMetadataFormatHandler.ts @@ -1,7 +1,8 @@ import { BulletListType, NumberingListType } from 'roosterjs-editor-types'; -import { FormatHandler } from '../FormatHandler'; -import { getObjectKeys, getTagOfNode, safeInstanceOf } from 'roosterjs-editor-dom'; -import { ListMetadataFormat } from 'roosterjs-content-model-types'; +import { getObjectKeys } from '../../domUtils/getObjectKeys'; +import { isElementOfType } from '../../domUtils/isElementOfType'; +import type { FormatHandler } from '../FormatHandler'; +import type { ListMetadataFormat } from 'roosterjs-content-model-types'; /** * @internal @@ -59,8 +60,8 @@ export const listLevelMetadataFormatHandler: FormatHandler = parse: (format, element) => { const listStyle = element.style.listStyleType || - (safeInstanceOf(element, 'HTMLOListElement') && OLTypeToStyleMap[element.type]); - const tag = getTagOfNode(element); + (isElementOfType(element, 'ol') && OLTypeToStyleMap[element.type]); + const tag = element.tagName; if (listStyle) { if (tag == 'OL' && format.orderedStyleType === undefined) { @@ -75,7 +76,7 @@ export const listLevelMetadataFormatHandler: FormatHandler = } }, apply: (format, element) => { - const tag = getTagOfNode(element); + const tag = element.tagName; const listType = tag == 'OL' ? OrderedMap[format.orderedStyleType!] diff --git a/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/list/listLevelThreadFormatHandler.ts b/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/list/listLevelThreadFormatHandler.ts index dbbbed17b69..3f49489e723 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/list/listLevelThreadFormatHandler.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/list/listLevelThreadFormatHandler.ts @@ -1,13 +1,13 @@ -import { FormatHandler } from '../FormatHandler'; -import { ListThreadFormat } from 'roosterjs-content-model-types'; -import { safeInstanceOf } from 'roosterjs-editor-dom'; +import { isElementOfType } from '../../domUtils/isElementOfType'; +import type { FormatHandler } from '../FormatHandler'; +import type { ListThreadFormat } from 'roosterjs-content-model-types'; /** * @internal */ export const listLevelThreadFormatHandler: FormatHandler = { parse: (format, element, context) => { - if (safeInstanceOf(element, 'HTMLOListElement')) { + if (isElementOfType(element, 'ol')) { const { listFormat } = context; const { threadItemCounts, levels } = listFormat; const depth = levels.length; @@ -28,7 +28,7 @@ export const listLevelThreadFormatHandler: FormatHandler = { } = context; const depth = nodeStack.length - 1; // The first one is always the parent of list - if (depth >= 0 && safeInstanceOf(element, 'HTMLOListElement')) { + if (depth >= 0 && isElementOfType(element, 'ol')) { const startNumber = format.startNumberOverride; if (typeof startNumber === 'number') { diff --git a/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/list/listStylePositionFormatHandler.ts b/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/list/listStylePositionFormatHandler.ts index 7da86db8b40..c0e293afbda 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/list/listStylePositionFormatHandler.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/list/listStylePositionFormatHandler.ts @@ -1,5 +1,5 @@ -import { FormatHandler } from '../FormatHandler'; -import { ListStylePositionFormat } from 'roosterjs-content-model-types'; +import type { FormatHandler } from '../FormatHandler'; +import type { ListStylePositionFormat } from 'roosterjs-content-model-types'; /** * @internal diff --git a/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/segment/boldFormatHandler.ts b/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/segment/boldFormatHandler.ts index 15689483e71..ecabe2b8705 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/segment/boldFormatHandler.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/segment/boldFormatHandler.ts @@ -1,6 +1,6 @@ -import { BoldFormat } from 'roosterjs-content-model-types'; -import { FormatHandler } from '../FormatHandler'; -import { moveChildNodes } from 'roosterjs-editor-dom'; +import { wrapAllChildNodes } from '../../domUtils/moveChildNodes'; +import type { BoldFormat } from 'roosterjs-content-model-types'; +import type { FormatHandler } from '../FormatHandler'; /** * @internal @@ -25,9 +25,7 @@ export const boldFormatHandler: FormatHandler = { (!blockFontWeight && format.fontWeight && format.fontWeight != 'normal') ) { if (format.fontWeight == 'bold') { - const b = element.ownerDocument.createElement('b'); - moveChildNodes(b, element); - element.appendChild(b); + wrapAllChildNodes(element, 'b'); } else { element.style.fontWeight = format.fontWeight || 'normal'; } diff --git a/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/segment/fontFamilyFormatHandler.ts b/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/segment/fontFamilyFormatHandler.ts index a4b93b3d6de..3c04a51dc2a 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/segment/fontFamilyFormatHandler.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/segment/fontFamilyFormatHandler.ts @@ -1,5 +1,5 @@ -import { FontFamilyFormat } from 'roosterjs-content-model-types'; -import { FormatHandler } from '../FormatHandler'; +import type { FontFamilyFormat } from 'roosterjs-content-model-types'; +import type { FormatHandler } from '../FormatHandler'; /** * @internal diff --git a/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/segment/fontSizeFormatHandler.ts b/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/segment/fontSizeFormatHandler.ts index bccfa63fd0e..4c4b8bffecc 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/segment/fontSizeFormatHandler.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/segment/fontSizeFormatHandler.ts @@ -1,7 +1,7 @@ -import { FontSizeFormat } from 'roosterjs-content-model-types'; -import { FormatHandler } from '../FormatHandler'; import { isSuperOrSubScript } from './superOrSubScriptFormatHandler'; import { parseValueWithUnit } from '../utils/parseValueWithUnit'; +import type { FontSizeFormat } from 'roosterjs-content-model-types'; +import type { FormatHandler } from '../FormatHandler'; /** * @internal diff --git a/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/segment/italicFormatHandler.ts b/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/segment/italicFormatHandler.ts index a2756daf00e..39c31b9c680 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/segment/italicFormatHandler.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/segment/italicFormatHandler.ts @@ -1,6 +1,6 @@ -import { FormatHandler } from '../FormatHandler'; -import { ItalicFormat } from 'roosterjs-content-model-types'; -import { moveChildNodes } from 'roosterjs-editor-dom'; +import { wrapAllChildNodes } from '../../domUtils/moveChildNodes'; +import type { FormatHandler } from '../FormatHandler'; +import type { ItalicFormat } from 'roosterjs-content-model-types'; /** * @internal @@ -24,9 +24,7 @@ export const italicFormatHandler: FormatHandler = { if (!!implicitItalic != !!format.italic) { if (format.italic) { - const i = element.ownerDocument.createElement('i'); - moveChildNodes(i, element); - element.appendChild(i); + wrapAllChildNodes(element, 'i'); } else { element.style.fontStyle = 'normal'; } diff --git a/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/segment/letterSpacingFormatHandler.ts b/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/segment/letterSpacingFormatHandler.ts index f0dcb15a821..314da333469 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/segment/letterSpacingFormatHandler.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/segment/letterSpacingFormatHandler.ts @@ -1,5 +1,5 @@ -import { FormatHandler } from '../FormatHandler'; -import { LetterSpacingFormat } from 'roosterjs-content-model-types'; +import type { FormatHandler } from '../FormatHandler'; +import type { LetterSpacingFormat } from 'roosterjs-content-model-types'; /** * @internal diff --git a/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/segment/linkFormatHandler.ts b/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/segment/linkFormatHandler.ts index b02825fef0b..a93d78c3cf3 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/segment/linkFormatHandler.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/segment/linkFormatHandler.ts @@ -1,13 +1,13 @@ -import { FormatHandler } from '../FormatHandler'; -import { LinkFormat } from 'roosterjs-content-model-types'; -import { safeInstanceOf } from 'roosterjs-editor-dom'; +import { isElementOfType } from '../../domUtils/isElementOfType'; +import type { FormatHandler } from '../FormatHandler'; +import type { LinkFormat } from 'roosterjs-content-model-types'; /** * @internal */ export const linkFormatHandler: FormatHandler = { parse: (format, element) => { - if (safeInstanceOf(element, 'HTMLAnchorElement')) { + if (isElementOfType(element, 'a')) { const name = element.name; const href = element.getAttribute('href'); // Use getAttribute to get original HREF but not the resolved absolute url const target = element.target; @@ -46,7 +46,7 @@ export const linkFormatHandler: FormatHandler = { } }, apply: (format, element) => { - if (safeInstanceOf(element, 'HTMLAnchorElement') && format.href) { + if (isElementOfType(element, 'a') && format.href) { element.href = format.href; if (format.name) { diff --git a/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/segment/strikeFormatHandler.ts b/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/segment/strikeFormatHandler.ts index 390595ae6fb..6796cbe4bb6 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/segment/strikeFormatHandler.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/segment/strikeFormatHandler.ts @@ -1,6 +1,6 @@ -import { FormatHandler } from '../FormatHandler'; -import { moveChildNodes } from 'roosterjs-editor-dom'; -import { StrikeFormat } from 'roosterjs-content-model-types'; +import { wrapAllChildNodes } from '../../domUtils/moveChildNodes'; +import type { FormatHandler } from '../FormatHandler'; +import type { StrikeFormat } from 'roosterjs-content-model-types'; /** * @internal @@ -15,9 +15,7 @@ export const strikeFormatHandler: FormatHandler = { }, apply: (format, element) => { if (format.strikethrough) { - const strike = element.ownerDocument.createElement('s'); - moveChildNodes(strike, element); - element.appendChild(strike); + wrapAllChildNodes(element, 's'); } }, }; diff --git a/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/segment/superOrSubScriptFormatHandler.ts b/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/segment/superOrSubScriptFormatHandler.ts index 6e0519ba40f..b71fa1b8b96 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/segment/superOrSubScriptFormatHandler.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/segment/superOrSubScriptFormatHandler.ts @@ -1,6 +1,6 @@ -import { FormatHandler } from '../FormatHandler'; -import { moveChildNodes } from 'roosterjs-editor-dom'; -import { SuperOrSubScriptFormat } from 'roosterjs-content-model-types'; +import { wrapAllChildNodes } from '../../domUtils/moveChildNodes'; +import type { FormatHandler } from '../FormatHandler'; +import type { SuperOrSubScriptFormat } from 'roosterjs-content-model-types'; /** * @internal @@ -27,9 +27,7 @@ export const superOrSubScriptFormatHandler: FormatHandler = { if (!!blockUnderline != !!format.underline) { if (format.underline) { - const u = element.ownerDocument.createElement('u'); - moveChildNodes(u, element); - element.appendChild(u); + wrapAllChildNodes(element, 'u'); } else { element.style.textDecoration = 'none'; } diff --git a/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/table/tableLayoutFormatHandler.ts b/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/table/tableLayoutFormatHandler.ts index 7acdd8bea69..c6fac8c8d83 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/table/tableLayoutFormatHandler.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/table/tableLayoutFormatHandler.ts @@ -1,5 +1,5 @@ -import { FormatHandler } from '../FormatHandler'; -import { TableLayoutFormat } from 'roosterjs-content-model-types'; +import type { FormatHandler } from '../FormatHandler'; +import type { TableLayoutFormat } from 'roosterjs-content-model-types'; /** * @internal diff --git a/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/table/tableSpacingFormatHandler.ts b/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/table/tableSpacingFormatHandler.ts index e08cdeb58b7..37ee6c87d6e 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/table/tableSpacingFormatHandler.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/table/tableSpacingFormatHandler.ts @@ -1,5 +1,5 @@ -import { FormatHandler } from '../FormatHandler'; -import { SpacingFormat } from 'roosterjs-content-model-types'; +import type { FormatHandler } from '../FormatHandler'; +import type { SpacingFormat } from 'roosterjs-content-model-types'; const BorderCollapsed = 'collapse'; diff --git a/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/table/textColorOnTableCellFormatHandler.ts b/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/table/textColorOnTableCellFormatHandler.ts index 29e35b4ebba..581a44f3945 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/table/textColorOnTableCellFormatHandler.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/table/textColorOnTableCellFormatHandler.ts @@ -1,5 +1,5 @@ -import { FormatHandler } from '../FormatHandler'; -import { TextColorFormat } from 'roosterjs-content-model-types'; +import type { FormatHandler } from '../FormatHandler'; +import type { TextColorFormat } from 'roosterjs-content-model-types'; /** * @internal diff --git a/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/utils/color.ts b/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/utils/color.ts index ed9e2ec522d..88a2e0f9364 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/utils/color.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/utils/color.ts @@ -1,5 +1,4 @@ -import { DarkColorHandler } from 'roosterjs-editor-types'; -import { getTagOfNode } from 'roosterjs-editor-dom'; +import type { DarkColorHandler } from 'roosterjs-editor-types'; /** * List of deprecated colors @@ -97,7 +96,7 @@ function tryGetFontColor( ) { let darkColor: string | null; - return getTagOfNode(element) == 'FONT' && + return element.tagName == 'FONT' && !element.style.getPropertyValue(isBackground ? 'background-color' : 'color') && isDarkMode && (darkColor = element.getAttribute(isBackground ? 'bgcolor' : 'color')) diff --git a/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/utils/parseValueWithUnit.ts b/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/utils/parseValueWithUnit.ts index ab12c0710ec..81a4f4d9091 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/utils/parseValueWithUnit.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/utils/parseValueWithUnit.ts @@ -1,5 +1,3 @@ -import { getComputedStyle } from 'roosterjs-editor-dom'; - const MarginValueRegex = /(-?\d+(\.\d+)?)([a-z]+|%)/; /** @@ -55,7 +53,9 @@ function getFontSize(currentSizeOrElement?: number | HTMLElement): number { } else if (typeof currentSizeOrElement === 'number') { return currentSizeOrElement; } else { - const styleInPt = getComputedStyle(currentSizeOrElement, 'font-size'); + const styleInPt = + currentSizeOrElement.ownerDocument.defaultView?.getComputedStyle(currentSizeOrElement) + .fontSize ?? ''; const floatInPt = parseFloat(styleInPt); const floatInPx = ptToPx(floatInPt); diff --git a/packages-content-model/roosterjs-content-model-dom/lib/index.ts b/packages-content-model/roosterjs-content-model-dom/lib/index.ts index f40d65322a4..dd90196f9a0 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/index.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/index.ts @@ -15,6 +15,10 @@ export { areSameFormats } from './domToModel/utils/areSameFormats'; export { updateMetadata, hasMetadata } from './domUtils/metadata/updateMetadata'; export { updateListMetadata } from './domUtils/metadata/updateListMetadata'; export { isNodeOfType, NodeTypeMap } from './domUtils/isNodeOfType'; +export { isElementOfType } from './domUtils/isElementOfType'; +export { getObjectKeys } from './domUtils/getObjectKeys'; +export { default as toArray } from './domUtils/toArray'; +export { moveChildNodes, wrapAllChildNodes } from './domUtils/moveChildNodes'; export { createBr } from './modelApi/creators/createBr'; export { createListItem } from './modelApi/creators/createListItem'; @@ -36,6 +40,8 @@ export { createListLevel } from './modelApi/creators/createListLevel'; export { addBlock } from './modelApi/common/addBlock'; export { addCode } from './modelApi/common/addDecorators'; export { addLink } from './modelApi/common/addDecorators'; +export { ensureParagraph } from './modelApi/common/ensureParagraph'; + export { normalizeContentModel } from './modelApi/common/normalizeContentModel'; export { isGeneralSegment } from './modelApi/common/isGeneralSegment'; export { unwrapBlock } from './modelApi/common/unwrapBlock'; diff --git a/packages-content-model/roosterjs-content-model-dom/lib/modelApi/block/setParagraphNotImplicit.ts b/packages-content-model/roosterjs-content-model-dom/lib/modelApi/block/setParagraphNotImplicit.ts index fc1bbe5d53b..949e3cb3b50 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/modelApi/block/setParagraphNotImplicit.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/modelApi/block/setParagraphNotImplicit.ts @@ -1,4 +1,4 @@ -import { ContentModelBlock } from 'roosterjs-content-model-types'; +import type { ContentModelBlock } from 'roosterjs-content-model-types'; /** * For a given block, if it is a paragraph, set it to be not-implicit diff --git a/packages-content-model/roosterjs-content-model-dom/lib/modelApi/common/addDecorators.ts b/packages-content-model/roosterjs-content-model-dom/lib/modelApi/common/addDecorators.ts index 559ac7fa9e1..25ba00b0703 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/modelApi/common/addDecorators.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/modelApi/common/addDecorators.ts @@ -1,4 +1,4 @@ -import { +import type { ContentModelCode, ContentModelLink, ContentModelSegment, diff --git a/packages-content-model/roosterjs-content-model-dom/lib/modelApi/common/addSegment.ts b/packages-content-model/roosterjs-content-model-dom/lib/modelApi/common/addSegment.ts index 12c0364e29b..35389ed6e25 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/modelApi/common/addSegment.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/modelApi/common/addSegment.ts @@ -1,5 +1,4 @@ -import { addBlock } from './addBlock'; -import { createParagraph } from '../creators/createParagraph'; +import { ensureParagraph } from './ensureParagraph'; import type { ContentModelBlockFormat, ContentModelBlockGroup, @@ -12,22 +11,14 @@ import type { * @param group The parent block group of the paragraph to add segment into * @param newSegment The segment to add * @param blockFormat The block format used for creating a new paragraph when need + * @returns The parent paragraph where the segment is added to */ export function addSegment( group: ContentModelBlockGroup, newSegment: ContentModelSegment, blockFormat?: ContentModelBlockFormat -) { - const lastBlock = group.blocks[group.blocks.length - 1]; - let paragraph: ContentModelParagraph; - - if (lastBlock?.blockType == 'Paragraph') { - paragraph = lastBlock; - } else { - paragraph = createParagraph(true, blockFormat); - addBlock(group, paragraph); - } - +): ContentModelParagraph { + const paragraph = ensureParagraph(group, blockFormat); const lastSegment = paragraph.segments[paragraph.segments.length - 1]; if (newSegment.segmentType == 'SelectionMarker') { @@ -41,4 +32,6 @@ export function addSegment( paragraph.segments.push(newSegment); } + + return paragraph; } diff --git a/packages-content-model/roosterjs-content-model-dom/lib/modelApi/common/applySegmentFormatToElement.ts b/packages-content-model/roosterjs-content-model-dom/lib/modelApi/common/applySegmentFormatToElement.ts index a3a78e44d00..7a736fc6a5e 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/modelApi/common/applySegmentFormatToElement.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/modelApi/common/applySegmentFormatToElement.ts @@ -1,6 +1,6 @@ import { applyFormat } from '../../modelToDom/utils/applyFormat'; -import { ContentModelSegmentFormat } from 'roosterjs-content-model-types'; import { createModelToDomContext } from '../../modelToDom/context/createModelToDomContext'; +import type { ContentModelSegmentFormat } from 'roosterjs-content-model-types'; /** * Format an existing HTML element using Segment Format diff --git a/packages-content-model/roosterjs-content-model-dom/lib/modelApi/common/ensureParagraph.ts b/packages-content-model/roosterjs-content-model-dom/lib/modelApi/common/ensureParagraph.ts new file mode 100644 index 00000000000..9e22bda96c2 --- /dev/null +++ b/packages-content-model/roosterjs-content-model-dom/lib/modelApi/common/ensureParagraph.ts @@ -0,0 +1,28 @@ +import { addBlock } from './addBlock'; +import { createParagraph } from '../creators/createParagraph'; +import type { + ContentModelBlockFormat, + ContentModelBlockGroup, + ContentModelParagraph, +} from 'roosterjs-content-model-types'; + +/** + * Ensure there is a Paragraph that can insert segments in a Content Model Block Group + * @param group The parent block group of the target paragraph + * @param blockFormat Format of the paragraph. This is only used if we need to create a new paragraph + */ +export function ensureParagraph( + group: ContentModelBlockGroup, + blockFormat?: ContentModelBlockFormat +): ContentModelParagraph { + const lastBlock = group.blocks[group.blocks.length - 1]; + + if (lastBlock?.blockType == 'Paragraph') { + return lastBlock; + } else { + const paragraph = createParagraph(true, blockFormat); + addBlock(group, paragraph); + + return paragraph; + } +} diff --git a/packages-content-model/roosterjs-content-model-dom/lib/modelApi/common/isEmpty.ts b/packages-content-model/roosterjs-content-model-dom/lib/modelApi/common/isEmpty.ts index 7e4587367c6..37efdb34e39 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/modelApi/common/isEmpty.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/modelApi/common/isEmpty.ts @@ -1,4 +1,4 @@ -import { +import type { ContentModelBlock, ContentModelBlockGroup, ContentModelSegment, diff --git a/packages-content-model/roosterjs-content-model-dom/lib/modelApi/common/isGeneralSegment.ts b/packages-content-model/roosterjs-content-model-dom/lib/modelApi/common/isGeneralSegment.ts index 3a91aff4aad..56163aa0f8b 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/modelApi/common/isGeneralSegment.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/modelApi/common/isGeneralSegment.ts @@ -1,4 +1,7 @@ -import { ContentModelBlockGroup, ContentModelGeneralSegment } from 'roosterjs-content-model-types'; +import type { + ContentModelBlockGroup, + ContentModelGeneralSegment, +} from 'roosterjs-content-model-types'; /** * Check if the given block group is a general segment diff --git a/packages-content-model/roosterjs-content-model-dom/lib/modelApi/common/isWhiteSpacePreserved.ts b/packages-content-model/roosterjs-content-model-dom/lib/modelApi/common/isWhiteSpacePreserved.ts index 0bb8aff3365..b3122e11d17 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/modelApi/common/isWhiteSpacePreserved.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/modelApi/common/isWhiteSpacePreserved.ts @@ -1,4 +1,4 @@ -import { ContentModelParagraph } from 'roosterjs-content-model-types'; +import type { ContentModelParagraph } from 'roosterjs-content-model-types'; // According to https://developer.mozilla.org/en-US/docs/Web/CSS/white-space, these style values will need to preserve white spaces const WHITESPACE_PRE_VALUES = ['pre', 'pre-wrap', 'break-spaces']; diff --git a/packages-content-model/roosterjs-content-model-dom/lib/modelApi/common/normalizeContentModel.ts b/packages-content-model/roosterjs-content-model-dom/lib/modelApi/common/normalizeContentModel.ts index 692186b843a..a808e975a4c 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/modelApi/common/normalizeContentModel.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/modelApi/common/normalizeContentModel.ts @@ -1,7 +1,7 @@ -import { ContentModelBlockGroup } from 'roosterjs-content-model-types'; import { isBlockEmpty } from './isEmpty'; import { normalizeParagraph } from './normalizeParagraph'; import { unwrapBlock } from './unwrapBlock'; +import type { ContentModelBlockGroup } from 'roosterjs-content-model-types'; /** * For a given content model, normalize it to make the model be consistent. diff --git a/packages-content-model/roosterjs-content-model-dom/lib/modelApi/common/normalizeParagraph.ts b/packages-content-model/roosterjs-content-model-dom/lib/modelApi/common/normalizeParagraph.ts index 031b49a95c1..7853f801ce0 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/modelApi/common/normalizeParagraph.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/modelApi/common/normalizeParagraph.ts @@ -1,9 +1,9 @@ import { areSameFormats } from '../../domToModel/utils/areSameFormats'; -import { ContentModelParagraph } from 'roosterjs-content-model-types'; import { createBr } from '../creators/createBr'; import { isSegmentEmpty } from './isEmpty'; import { isWhiteSpacePreserved } from './isWhiteSpacePreserved'; import { normalizeAllSegments } from './normalizeSegment'; +import type { ContentModelParagraph } from 'roosterjs-content-model-types'; /** * @internal */ diff --git a/packages-content-model/roosterjs-content-model-dom/lib/modelApi/common/normalizeSegment.ts b/packages-content-model/roosterjs-content-model-dom/lib/modelApi/common/normalizeSegment.ts index 4a4b0031a33..59bb9be59e8 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/modelApi/common/normalizeSegment.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/modelApi/common/normalizeSegment.ts @@ -1,5 +1,5 @@ import { hasSpacesOnly } from './hasSpacesOnly'; -import { +import type { ContentModelParagraph, ContentModelSegment, ContentModelText, diff --git a/packages-content-model/roosterjs-content-model-dom/lib/modelApi/common/unwrapBlock.ts b/packages-content-model/roosterjs-content-model-dom/lib/modelApi/common/unwrapBlock.ts index dc385fc8316..312f007bfab 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/modelApi/common/unwrapBlock.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/modelApi/common/unwrapBlock.ts @@ -1,5 +1,5 @@ -import { ContentModelBlock, ContentModelBlockGroup } from 'roosterjs-content-model-types'; import { setParagraphNotImplicit } from '../block/setParagraphNotImplicit'; +import type { ContentModelBlock, ContentModelBlockGroup } from 'roosterjs-content-model-types'; /** * Unwrap a given block group, move its child blocks to be under its parent group diff --git a/packages-content-model/roosterjs-content-model-dom/lib/modelApi/creators/createBr.ts b/packages-content-model/roosterjs-content-model-dom/lib/modelApi/creators/createBr.ts index 1af119d4e8c..f5c907211b7 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/modelApi/creators/createBr.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/modelApi/creators/createBr.ts @@ -1,4 +1,4 @@ -import { ContentModelBr, ContentModelSegmentFormat } from 'roosterjs-content-model-types'; +import type { ContentModelBr, ContentModelSegmentFormat } from 'roosterjs-content-model-types'; /** * Create a ContentModelBr model diff --git a/packages-content-model/roosterjs-content-model-dom/lib/modelApi/creators/createContentModelDocument.ts b/packages-content-model/roosterjs-content-model-dom/lib/modelApi/creators/createContentModelDocument.ts index 31954c7b5da..d4ef4a4d539 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/modelApi/creators/createContentModelDocument.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/modelApi/creators/createContentModelDocument.ts @@ -1,4 +1,7 @@ -import { ContentModelDocument, ContentModelSegmentFormat } from 'roosterjs-content-model-types'; +import type { + ContentModelDocument, + ContentModelSegmentFormat, +} from 'roosterjs-content-model-types'; /** * Create a ContentModelDocument model diff --git a/packages-content-model/roosterjs-content-model-dom/lib/modelApi/creators/createDivider.ts b/packages-content-model/roosterjs-content-model-dom/lib/modelApi/creators/createDivider.ts index ad070e1705e..f6d058dd4ea 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/modelApi/creators/createDivider.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/modelApi/creators/createDivider.ts @@ -1,4 +1,4 @@ -import { ContentModelBlockFormat, ContentModelDivider } from 'roosterjs-content-model-types'; +import type { ContentModelBlockFormat, ContentModelDivider } from 'roosterjs-content-model-types'; /** * Create a ContentModelDivider model diff --git a/packages-content-model/roosterjs-content-model-dom/lib/modelApi/creators/createEntity.ts b/packages-content-model/roosterjs-content-model-dom/lib/modelApi/creators/createEntity.ts index a02dbed9899..da66859a67b 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/modelApi/creators/createEntity.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/modelApi/creators/createEntity.ts @@ -1,27 +1,29 @@ -import { ContentModelEntity, ContentModelSegmentFormat } from 'roosterjs-content-model-types'; +import type { ContentModelEntity, ContentModelSegmentFormat } from 'roosterjs-content-model-types'; /** * Create a ContentModelEntity model * @param wrapper Wrapper element of this entity - * @param isReadonly Whether this is a readonly entity - * @param type @optional Type of this entity + * @param isReadonly Whether this is a readonly entity @default true * @param segmentFormat @optional Segment format of this entity + * @param type @optional Type of this entity * @param id @optional Id of this entity */ export function createEntity( wrapper: HTMLElement, - isReadonly: boolean, - type?: string, + isReadonly: boolean = true, segmentFormat?: ContentModelSegmentFormat, + type?: string, id?: string ): ContentModelEntity { return { segmentType: 'Entity', blockType: 'Entity', format: { ...segmentFormat }, - id, - type, - isReadonly, + entityFormat: { + id, + entityType: type, + isReadonly, + }, wrapper, }; } diff --git a/packages-content-model/roosterjs-content-model-dom/lib/modelApi/creators/createFormatContainer.ts b/packages-content-model/roosterjs-content-model-dom/lib/modelApi/creators/createFormatContainer.ts index 6889e4651d7..9bf73a25b2c 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/modelApi/creators/createFormatContainer.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/modelApi/creators/createFormatContainer.ts @@ -1,4 +1,4 @@ -import { +import type { ContentModelFormatContainer, ContentModelFormatContainerFormat, } from 'roosterjs-content-model-types'; diff --git a/packages-content-model/roosterjs-content-model-dom/lib/modelApi/creators/createGeneralBlock.ts b/packages-content-model/roosterjs-content-model-dom/lib/modelApi/creators/createGeneralBlock.ts index 9889ffcf3dd..d8310f0c779 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/modelApi/creators/createGeneralBlock.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/modelApi/creators/createGeneralBlock.ts @@ -1,4 +1,4 @@ -import { ContentModelGeneralBlock } from 'roosterjs-content-model-types'; +import type { ContentModelGeneralBlock } from 'roosterjs-content-model-types'; /** * Create a ContentModelGeneralBlock model diff --git a/packages-content-model/roosterjs-content-model-dom/lib/modelApi/creators/createGeneralSegment.ts b/packages-content-model/roosterjs-content-model-dom/lib/modelApi/creators/createGeneralSegment.ts index 8809e2f684d..e92400efd87 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/modelApi/creators/createGeneralSegment.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/modelApi/creators/createGeneralSegment.ts @@ -1,4 +1,4 @@ -import { +import type { ContentModelGeneralSegment, ContentModelSegmentFormat, } from 'roosterjs-content-model-types'; diff --git a/packages-content-model/roosterjs-content-model-dom/lib/modelApi/creators/createImage.ts b/packages-content-model/roosterjs-content-model-dom/lib/modelApi/creators/createImage.ts index aafb81c25eb..44c3d744d6d 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/modelApi/creators/createImage.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/modelApi/creators/createImage.ts @@ -1,4 +1,4 @@ -import { ContentModelImage, ContentModelImageFormat } from 'roosterjs-content-model-types'; +import type { ContentModelImage, ContentModelImageFormat } from 'roosterjs-content-model-types'; /** * Create a ContentModelImage model diff --git a/packages-content-model/roosterjs-content-model-dom/lib/modelApi/creators/createListItem.ts b/packages-content-model/roosterjs-content-model-dom/lib/modelApi/creators/createListItem.ts index 025401e96c5..34cfd2332da 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/modelApi/creators/createListItem.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/modelApi/creators/createListItem.ts @@ -1,6 +1,6 @@ import { createListLevel } from './createListLevel'; import { createSelectionMarker } from './createSelectionMarker'; -import { +import type { ContentModelListItem, ContentModelListLevel, ContentModelSegmentFormat, diff --git a/packages-content-model/roosterjs-content-model-dom/lib/modelApi/creators/createListLevel.ts b/packages-content-model/roosterjs-content-model-dom/lib/modelApi/creators/createListLevel.ts index ea877aada1a..4355b02171c 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/modelApi/creators/createListLevel.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/modelApi/creators/createListLevel.ts @@ -1,4 +1,4 @@ -import { +import type { ContentModelListItemLevelFormat, ContentModelListLevel, DatasetFormat, diff --git a/packages-content-model/roosterjs-content-model-dom/lib/modelApi/creators/createParagraphDecorator.ts b/packages-content-model/roosterjs-content-model-dom/lib/modelApi/creators/createParagraphDecorator.ts index 5b546cf8c19..fb5e5835ed6 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/modelApi/creators/createParagraphDecorator.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/modelApi/creators/createParagraphDecorator.ts @@ -1,4 +1,4 @@ -import { +import type { ContentModelParagraphDecorator, ContentModelSegmentFormat, } from 'roosterjs-content-model-types'; diff --git a/packages-content-model/roosterjs-content-model-dom/lib/modelApi/creators/createSelectionMarker.ts b/packages-content-model/roosterjs-content-model-dom/lib/modelApi/creators/createSelectionMarker.ts index 8dd76852d38..88f5ac2e86a 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/modelApi/creators/createSelectionMarker.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/modelApi/creators/createSelectionMarker.ts @@ -1,4 +1,4 @@ -import { +import type { ContentModelSegmentFormat, ContentModelSelectionMarker, } from 'roosterjs-content-model-types'; diff --git a/packages-content-model/roosterjs-content-model-dom/lib/modelApi/creators/createTable.ts b/packages-content-model/roosterjs-content-model-dom/lib/modelApi/creators/createTable.ts index ec5162ef7d9..7a549dac31f 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/modelApi/creators/createTable.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/modelApi/creators/createTable.ts @@ -1,4 +1,4 @@ -import { +import type { ContentModelTable, ContentModelTableFormat, ContentModelTableRow, diff --git a/packages-content-model/roosterjs-content-model-dom/lib/modelApi/creators/createTableCell.ts b/packages-content-model/roosterjs-content-model-dom/lib/modelApi/creators/createTableCell.ts index 7547937cde9..f75e5e3435c 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/modelApi/creators/createTableCell.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/modelApi/creators/createTableCell.ts @@ -1,4 +1,7 @@ -import { ContentModelTableCell, ContentModelTableCellFormat } from 'roosterjs-content-model-types'; +import type { + ContentModelTableCell, + ContentModelTableCellFormat, +} from 'roosterjs-content-model-types'; /** * Create a ContentModelTableCell model diff --git a/packages-content-model/roosterjs-content-model-dom/lib/modelApi/creators/createText.ts b/packages-content-model/roosterjs-content-model-dom/lib/modelApi/creators/createText.ts index 3e4f1f63b0f..6f73d09c662 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/modelApi/creators/createText.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/modelApi/creators/createText.ts @@ -1,5 +1,5 @@ import { addCode, addLink } from '../common/addDecorators'; -import { +import type { ContentModelCode, ContentModelLink, ContentModelSegmentFormat, diff --git a/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/contentModelToDom.ts b/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/contentModelToDom.ts index e247153b549..876518c0fe1 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/contentModelToDom.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/contentModelToDom.ts @@ -1,17 +1,12 @@ -import { createRange, Position, toArray } from 'roosterjs-editor-dom'; +import toArray from '../domUtils/toArray'; import { isNodeOfType } from '../domUtils/isNodeOfType'; -import { +import type { ContentModelDocument, + DOMSelection, ModelToDomBlockAndSegmentNode, ModelToDomContext, OnNodeCreated, } from 'roosterjs-content-model-types'; -import { - NodePosition, - NodeType, - SelectionRangeEx, - SelectionRangeTypes, -} from 'roosterjs-editor-types'; /** * Create DOM tree fragment from Content Model document @@ -30,82 +25,87 @@ export function contentModelToDom( model: ContentModelDocument, context: ModelToDomContext, onNodeCreated?: OnNodeCreated -): SelectionRangeEx | null { +): DOMSelection | null { context.onNodeCreated = onNodeCreated; context.modelHandlers.blockGroupChildren(doc, root, model, context); - const range = extractSelectionRange(context); + const range = extractSelectionRange(doc, context); root.normalize(); return range; } -function extractSelectionRange(context: ModelToDomContext): SelectionRangeEx | null { +function extractSelectionRange(doc: Document, context: ModelToDomContext): DOMSelection | null { const { regularSelection: { start, end }, tableSelection, imageSelection, } = context; - let startPosition: NodePosition | undefined; - let endPosition: NodePosition | undefined; + let startPosition: { container: Node; offset: number } | undefined; + let endPosition: { container: Node; offset: number } | undefined; - if (imageSelection?.image) { - return { - type: SelectionRangeTypes.ImageSelection, - ranges: [createRange(imageSelection.image)], - areAllCollapsed: false, - image: imageSelection.image, - }; + if (imageSelection) { + return imageSelection; } else if ( (startPosition = start && calcPosition(start)) && (endPosition = end && calcPosition(end)) ) { - const range = createRange(startPosition, endPosition); + const range = doc.createRange(); + + range.setStart(startPosition.container, startPosition.offset); + range.setEnd(endPosition.container, endPosition.offset); return { - type: SelectionRangeTypes.Normal, - ranges: [createRange(startPosition, endPosition)], - areAllCollapsed: range.collapsed, - }; - } else if (tableSelection?.table) { - return { - type: SelectionRangeTypes.TableSelection, - ranges: [], - areAllCollapsed: false, - table: tableSelection.table, - coordinates: { - firstCell: tableSelection.firstCell, - lastCell: tableSelection.lastCell, - }, + type: 'range', + range, }; + } else if (tableSelection) { + return tableSelection; } else { return null; } } -function calcPosition(pos: ModelToDomBlockAndSegmentNode): NodePosition | undefined { - let result: NodePosition | undefined; +function calcPosition( + pos: ModelToDomBlockAndSegmentNode +): { container: Node; offset: number } | undefined { + let result: { container: Node; offset: number } | undefined; if (pos.block) { if (!pos.segment) { - result = new Position(pos.block, 0); - } else if (isNodeOfType(pos.segment, NodeType.Text)) { - result = new Position(pos.segment, pos.segment.nodeValue?.length || 0); - } else { - result = new Position( - pos.segment.parentNode!, - toArray(pos.segment.parentNode!.childNodes as NodeListOf).indexOf( - pos.segment! - ) + 1 - ); + result = { container: pos.block, offset: 0 }; + } else if (isNodeOfType(pos.segment, 'TEXT_NODE')) { + result = { container: pos.segment, offset: pos.segment.nodeValue?.length || 0 }; + } else if (pos.segment.parentNode) { + result = { + container: pos.segment.parentNode, + offset: + toArray(pos.segment.parentNode.childNodes as NodeListOf).indexOf( + pos.segment + ) + 1, + }; } } - if (isNodeOfType(result?.node, NodeType.DocumentFragment)) { - result = result?.normalize(); + if (result && isNodeOfType(result.container, 'DOCUMENT_FRAGMENT_NODE')) { + const childNodes = result.container.childNodes; + + if (childNodes.length > result.offset) { + result = { container: childNodes[result.offset], offset: 0 }; + } else if (result.container.lastChild) { + const container = result.container.lastChild; + result = { + container, + offset: isNodeOfType(container, 'TEXT_NODE') + ? container.nodeValue?.length ?? 0 + : container.childNodes.length, + }; + } else { + result = undefined; + } } return result; diff --git a/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/context/createModelToDomContext.ts b/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/context/createModelToDomContext.ts index f94061dc0b4..887dd62c2dd 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/context/createModelToDomContext.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/context/createModelToDomContext.ts @@ -1,10 +1,10 @@ import { defaultContentModelHandlers } from './defaultContentModelHandlers'; -import { getObjectKeys } from 'roosterjs-editor-dom'; +import { getObjectKeys } from '../../domUtils/getObjectKeys'; import { defaultFormatAppliers, defaultFormatKeysPerCategory, } from '../../formatHandlers/defaultFormatHandlers'; -import { +import type { EditorContext, FormatApplier, FormatAppliers, diff --git a/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/context/defaultContentModelHandlers.ts b/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/context/defaultContentModelHandlers.ts index b43cf1c7af3..3e89f31b82a 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/context/defaultContentModelHandlers.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/context/defaultContentModelHandlers.ts @@ -1,4 +1,3 @@ -import { ContentModelHandlerMap } from 'roosterjs-content-model-types'; import { handleBlock } from '../handlers/handleBlock'; import { handleBlockGroupChildren } from '../handlers/handleBlockGroupChildren'; import { handleBr } from '../handlers/handleBr'; @@ -14,6 +13,7 @@ import { handleSegment } from '../handlers/handleSegment'; import { handleSegmentDecorator } from '../handlers/handleSegmentDecorator'; import { handleTable } from '../handlers/handleTable'; import { handleText } from '../handlers/handleText'; +import type { ContentModelHandlerMap } from 'roosterjs-content-model-types'; /** * @internal diff --git a/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/handlers/handleBlock.ts b/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/handlers/handleBlock.ts index b45e9bb05cb..bb1ac4be393 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/handlers/handleBlock.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/handlers/handleBlock.ts @@ -1,4 +1,4 @@ -import { +import type { ContentModelBlock, ContentModelBlockHandler, ModelToDomContext, diff --git a/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/handlers/handleBlockGroupChildren.ts b/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/handlers/handleBlockGroupChildren.ts index e6708e9dc88..75cd3723db9 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/handlers/handleBlockGroupChildren.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/handlers/handleBlockGroupChildren.ts @@ -1,4 +1,4 @@ -import { +import type { ContentModelBlockGroup, ContentModelHandler, ModelToDomContext, diff --git a/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/handlers/handleBr.ts b/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/handlers/handleBr.ts index e3decb24b96..6a3783b10cf 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/handlers/handleBr.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/handlers/handleBr.ts @@ -1,5 +1,5 @@ -import { ContentModelBr, ContentModelSegmentHandler } from 'roosterjs-content-model-types'; import { handleSegmentCommon } from '../utils/handleSegmentCommon'; +import type { ContentModelBr, ContentModelSegmentHandler } from 'roosterjs-content-model-types'; /** * @internal diff --git a/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/handlers/handleDivider.ts b/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/handlers/handleDivider.ts index 1f0ad2817c7..01021e53608 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/handlers/handleDivider.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/handlers/handleDivider.ts @@ -1,6 +1,6 @@ import { applyFormat } from '../utils/applyFormat'; import { reuseCachedElement } from '../utils/reuseCachedElement'; -import { +import type { ContentModelBlockHandler, ContentModelDivider, ModelToDomContext, diff --git a/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/handlers/handleEntity.ts b/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/handlers/handleEntity.ts index 0b48eaf731a..d3507f1ffdf 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/handlers/handleEntity.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/handlers/handleEntity.ts @@ -1,12 +1,11 @@ -import { addDelimiters, commitEntity, getObjectKeys, wrap } from 'roosterjs-editor-dom'; +import { addDelimiters, wrap } from 'roosterjs-editor-dom'; import { applyFormat } from '../utils/applyFormat'; -import { Entity } from 'roosterjs-editor-types'; +import { getObjectKeys } from '../../domUtils/getObjectKeys'; import { reuseCachedElement } from '../utils/reuseCachedElement'; -import { +import type { ContentModelBlockHandler, ContentModelEntity, ContentModelSegmentHandler, - ModelToDomContext, } from 'roosterjs-content-model-types'; /** @@ -19,7 +18,9 @@ export const handleEntityBlock: ContentModelBlockHandler = ( context, refNode ) => { - const wrapper = preprocessEntity(entityModel, context); + let { entityFormat, wrapper } = entityModel; + + applyFormat(wrapper, context.formatAppliers.entity, entityFormat, context); refNode = reuseCachedElement(parent, wrapper, refNode); context.onNodeCreated?.(entityModel, wrapper); @@ -37,8 +38,7 @@ export const handleEntitySegment: ContentModelSegmentHandler context, newSegments ) => { - const wrapper = preprocessEntity(entityModel, context); - const { format, isReadonly } = entityModel; + let { entityFormat, wrapper, format } = entityModel; parent.appendChild(wrapper); newSegments?.push(wrapper); @@ -49,7 +49,9 @@ export const handleEntitySegment: ContentModelSegmentHandler applyFormat(span, context.formatAppliers.segment, format, context); } - if (context.addDelimiterForEntity && isReadonly) { + applyFormat(wrapper, context.formatAppliers.entity, entityFormat, context); + + if (context.addDelimiterForEntity && entityFormat.isReadonly) { const [after, before] = addDelimiters(wrapper); newSegments?.push(after, before); @@ -60,29 +62,3 @@ export const handleEntitySegment: ContentModelSegmentHandler context.onNodeCreated?.(entityModel, wrapper); }; - -function preprocessEntity(entityModel: ContentModelEntity, context: ModelToDomContext) { - let { id, type, isReadonly, wrapper } = entityModel; - - if (!context.allowCacheElement) { - wrapper = wrapper.cloneNode(true /*deep*/) as HTMLElement; - wrapper.style.color = wrapper.style.color || 'inherit'; - wrapper.style.backgroundColor = wrapper.style.backgroundColor || 'inherit'; - } - - const entity: Entity | null = - id && type - ? { - wrapper, - id, - type, - isReadonly: !!isReadonly, - } - : null; - - if (entity) { - // Commit the entity attributes in case there is any change - commitEntity(wrapper, entity.type, entity.isReadonly, entity.id); - } - return wrapper; -} diff --git a/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/handlers/handleFormatContainer.ts b/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/handlers/handleFormatContainer.ts index 5eddc15464a..cd6e87cb3df 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/handlers/handleFormatContainer.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/handlers/handleFormatContainer.ts @@ -2,7 +2,7 @@ import { applyFormat } from '../utils/applyFormat'; import { isBlockGroupEmpty } from '../../modelApi/common/isEmpty'; import { reuseCachedElement } from '../utils/reuseCachedElement'; import { stackFormat } from '../utils/stackFormat'; -import { +import type { ContentModelBlockFormat, ContentModelBlockHandler, ContentModelFormatContainer, diff --git a/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/handlers/handleGeneralModel.ts b/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/handlers/handleGeneralModel.ts index 30f04d2200d..7d671f66b4d 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/handlers/handleGeneralModel.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/handlers/handleGeneralModel.ts @@ -1,9 +1,8 @@ import { handleSegmentCommon } from '../utils/handleSegmentCommon'; import { isNodeOfType } from '../../domUtils/isNodeOfType'; -import { NodeType } from 'roosterjs-editor-types'; import { reuseCachedElement } from '../utils/reuseCachedElement'; import { wrap } from 'roosterjs-editor-dom'; -import { +import type { ContentModelBlockHandler, ContentModelGeneralBlock, ContentModelGeneralSegment, @@ -51,7 +50,7 @@ export const handleGeneralSegment: ContentModelSegmentHandler = ( if (imageModel.isSelectedAsImageSelection) { context.imageSelection = { + type: 'image', image: img, }; } diff --git a/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/handlers/handleList.ts b/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/handlers/handleList.ts index 1dad56af8e3..bde1e520674 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/handlers/handleList.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/handlers/handleList.ts @@ -1,7 +1,7 @@ import { applyFormat } from '../utils/applyFormat'; import { listLevelMetadataFormatHandler } from '../../formatHandlers/list/listLevelMetadataFormatHandler'; import { updateListMetadata } from '../../domUtils/metadata/updateListMetadata'; -import { +import type { ContentModelBlockHandler, ContentModelListItem, ModelToDomContext, diff --git a/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/handlers/handleListItem.ts b/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/handlers/handleListItem.ts index 6dc94926c28..8f4d4a4456d 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/handlers/handleListItem.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/handlers/handleListItem.ts @@ -3,7 +3,7 @@ import { listItemMetadataFormatHandler } from '../../formatHandlers/list/listIte import { setParagraphNotImplicit } from '../../modelApi/block/setParagraphNotImplicit'; import { unwrap } from 'roosterjs-editor-dom'; import { updateListMetadata } from '../../domUtils/metadata/updateListMetadata'; -import { +import type { ContentModelBlockHandler, ContentModelListItem, ModelToDomContext, diff --git a/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/handlers/handleParagraph.ts b/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/handlers/handleParagraph.ts index eca8d501e7c..6ddf0b7754f 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/handlers/handleParagraph.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/handlers/handleParagraph.ts @@ -1,9 +1,10 @@ import { applyFormat } from '../utils/applyFormat'; -import { getObjectKeys, unwrap } from 'roosterjs-editor-dom'; +import { getObjectKeys } from '../../domUtils/getObjectKeys'; import { optimize } from '../optimizers/optimize'; import { reuseCachedElement } from '../utils/reuseCachedElement'; import { stackFormat } from '../utils/stackFormat'; -import { +import { unwrap } from 'roosterjs-editor-dom'; +import type { ContentModelBlockHandler, ContentModelParagraph, ModelToDomContext, @@ -74,6 +75,10 @@ export const handleParagraph: ContentModelBlockHandler = paragraph.segments.forEach(segment => { const newSegments: Node[] = []; context.modelHandlers.segment(doc, parent, segment, context, newSegments); + + newSegments.forEach(node => { + context.domIndexer?.onSegment(node, paragraph, [segment]); + }); }); } }; @@ -103,19 +108,21 @@ export const handleParagraph: ContentModelBlockHandler = // to make sure the value is correct. refNode = container.nextSibling; + if (container) { + context.onNodeCreated?.(paragraph, container); + context.domIndexer?.onParagraph(container); + } + if (needParagraphWrapper) { if (context.allowCacheElement) { paragraph.cachedElement = container; } } else { unwrap(container); + container = undefined; } }); } - if (container) { - context.onNodeCreated?.(paragraph, container); - } - return refNode; }; diff --git a/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/handlers/handleSegment.ts b/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/handlers/handleSegment.ts index 4376dd6f750..b3863eb9ce6 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/handlers/handleSegment.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/handlers/handleSegment.ts @@ -1,4 +1,7 @@ -import { ContentModelSegment, ContentModelSegmentHandler } from 'roosterjs-content-model-types'; +import type { + ContentModelSegment, + ContentModelSegmentHandler, +} from 'roosterjs-content-model-types'; /** * @internal diff --git a/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/handlers/handleSegmentDecorator.ts b/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/handlers/handleSegmentDecorator.ts index 933b8b6eac1..d2dcc7706aa 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/handlers/handleSegmentDecorator.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/handlers/handleSegmentDecorator.ts @@ -1,7 +1,11 @@ import { applyFormat } from '../utils/applyFormat'; -import { ContentModelSegment, ContentModelSegmentHandler } from 'roosterjs-content-model-types'; -import { moveChildNodes } from 'roosterjs-editor-dom'; +import { isNodeOfType } from '../../domUtils/isNodeOfType'; import { stackFormat } from '../utils/stackFormat'; +import { wrapAllChildNodes } from '../../domUtils/moveChildNodes'; +import type { + ContentModelSegment, + ContentModelSegmentHandler, +} from 'roosterjs-content-model-types'; /** * @internal @@ -15,32 +19,28 @@ export const handleSegmentDecorator: ContentModelSegmentHandler { const { code, link } = segment; - if (link) { - stackFormat(context, 'a', () => { - const a = document.createElement('a'); + if (isNodeOfType(parent, 'ELEMENT_NODE')) { + if (link) { + stackFormat(context, 'a', () => { + const a = wrapAllChildNodes(parent, 'a'); - moveChildNodes(a, parent); - parent.appendChild(a); + applyFormat(a, context.formatAppliers.link, link.format, context); + applyFormat(a, context.formatAppliers.dataset, link.dataset, context); - applyFormat(a, context.formatAppliers.link, link.format, context); - applyFormat(a, context.formatAppliers.dataset, link.dataset, context); + segmentNodes?.push(a); + context.onNodeCreated?.(link, a); + }); + } - segmentNodes?.push(a); - context.onNodeCreated?.(link, a); - }); - } - - if (code) { - stackFormat(context, 'code', () => { - const codeNode = document.createElement('code'); - - moveChildNodes(codeNode, parent); - parent.appendChild(codeNode); + if (code) { + stackFormat(context, 'code', () => { + const codeNode = wrapAllChildNodes(parent, 'code'); - applyFormat(codeNode, context.formatAppliers.code, code.format, context); + applyFormat(codeNode, context.formatAppliers.code, code.format, context); - segmentNodes?.push(codeNode); - context.onNodeCreated?.(code, codeNode); - }); + segmentNodes?.push(codeNode); + context.onNodeCreated?.(code, codeNode); + }); + } } }; diff --git a/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/handlers/handleTable.ts b/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/handlers/handleTable.ts index b8a4e64658c..36deb00ea0e 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/handlers/handleTable.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/handlers/handleTable.ts @@ -1,12 +1,13 @@ import { applyFormat } from '../utils/applyFormat'; import { hasMetadata } from '../../domUtils/metadata/updateMetadata'; import { isBlockEmpty } from '../../modelApi/common/isEmpty'; -import { moveChildNodes } from 'roosterjs-editor-dom'; +import { moveChildNodes } from '../../domUtils/moveChildNodes'; import { reuseCachedElement } from '../utils/reuseCachedElement'; -import { +import type { ContentModelBlockHandler, ContentModelTable, ModelToDomContext, + TableSelection, } from 'roosterjs-content-model-types'; /** @@ -76,18 +77,21 @@ export const handleTable: ContentModelBlockHandler = ( const cell = tableRow.cells[col]; if (cell.isSelected) { - context.tableSelection = context.tableSelection || { + const tableSelection: TableSelection = context.tableSelection || { + type: 'table', table: tableNode, - firstCell: { x: col, y: row }, - lastCell: { x: col, y: row }, + firstColumn: col, + lastColumn: col, + firstRow: row, + lastRow: row, }; - if (context.tableSelection.table == tableNode) { - const lastCell = context.tableSelection.lastCell; - - lastCell.x = Math.max(lastCell.x, col); - lastCell.y = Math.max(lastCell.y, row); + if (tableSelection.table == tableNode) { + tableSelection.lastColumn = Math.max(tableSelection.lastColumn, col); + tableSelection.lastRow = Math.max(tableSelection.lastRow, row); } + + context.tableSelection = tableSelection; } if (!cell.spanAbove && !cell.spanLeft) { @@ -145,5 +149,7 @@ export const handleTable: ContentModelBlockHandler = ( } } + context.domIndexer?.onTable(tableNode, table); + return refNode; }; diff --git a/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/handlers/handleText.ts b/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/handlers/handleText.ts index 5974db1cb0a..c4d3e262601 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/handlers/handleText.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/handlers/handleText.ts @@ -1,5 +1,5 @@ -import { ContentModelSegmentHandler, ContentModelText } from 'roosterjs-content-model-types'; import { handleSegmentCommon } from '../utils/handleSegmentCommon'; +import type { ContentModelSegmentHandler, ContentModelText } from 'roosterjs-content-model-types'; /** * @internal diff --git a/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/optimizers/mergeNode.ts b/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/optimizers/mergeNode.ts index 8e0875088ba..a7c7f34990b 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/optimizers/mergeNode.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/optimizers/mergeNode.ts @@ -1,5 +1,4 @@ import { isNodeOfType } from '../../domUtils/isNodeOfType'; -import { NodeType } from 'roosterjs-editor-types'; const OptimizeTags = ['SPAN', 'B', 'EM', 'I', 'U', 'SUB', 'SUP', 'STRIKE', 'S', 'A', 'CODE']; @@ -12,8 +11,8 @@ export function mergeNode(root: Node) { if ( next && - isNodeOfType(child, NodeType.Element) && - isNodeOfType(next, NodeType.Element) && + isNodeOfType(child, 'ELEMENT_NODE') && + isNodeOfType(next, 'ELEMENT_NODE') && child.tagName == next.tagName && OptimizeTags.indexOf(child.tagName) >= 0 && hasSameAttributes(child, next) diff --git a/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/optimizers/optimize.ts b/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/optimizers/optimize.ts index 31e8ba9bc41..eed720aa157 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/optimizers/optimize.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/optimizers/optimize.ts @@ -1,5 +1,4 @@ -import { EntityClasses, NodeType } from 'roosterjs-editor-types'; -import { isNodeOfType } from '../../domUtils/isNodeOfType'; +import { isEntityElement } from '../../domUtils/entityUtils'; import { mergeNode } from './mergeNode'; import { removeUnnecessarySpan } from './removeUnnecessarySpan'; @@ -10,10 +9,7 @@ export function optimize(root: Node) { /** * Do no do any optimization to entity */ - if ( - isNodeOfType(root, NodeType.Element) && - root.classList.contains(EntityClasses.ENTITY_INFO_NAME) - ) { + if (isEntityElement(root)) { return; } diff --git a/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/optimizers/removeUnnecessarySpan.ts b/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/optimizers/removeUnnecessarySpan.ts index 6f317237c00..ee048a395bb 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/optimizers/removeUnnecessarySpan.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/optimizers/removeUnnecessarySpan.ts @@ -1,5 +1,4 @@ import { isNodeOfType } from '../../domUtils/isNodeOfType'; -import { NodeType } from 'roosterjs-editor-types'; /** * @internal @@ -7,7 +6,7 @@ import { NodeType } from 'roosterjs-editor-types'; export function removeUnnecessarySpan(root: Node) { for (let child = root.firstChild; child; ) { if ( - isNodeOfType(child, NodeType.Element) && + isNodeOfType(child, 'ELEMENT_NODE') && child.tagName == 'SPAN' && child.attributes.length == 0 ) { diff --git a/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/utils/applyFormat.ts b/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/utils/applyFormat.ts index b8ea655d636..11a791ff453 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/utils/applyFormat.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/utils/applyFormat.ts @@ -1,4 +1,4 @@ -import { +import type { ContentModelFormatBase, FormatApplier, ModelToDomContext, diff --git a/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/utils/handleSegmentCommon.ts b/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/utils/handleSegmentCommon.ts index 46d5ad698bd..ccf24a5c70b 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/utils/handleSegmentCommon.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/utils/handleSegmentCommon.ts @@ -1,5 +1,5 @@ import { applyFormat } from './applyFormat'; -import { ContentModelSegment, ModelToDomContext } from 'roosterjs-content-model-types'; +import type { ContentModelSegment, ModelToDomContext } from 'roosterjs-content-model-types'; /** * @internal diff --git a/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/utils/reuseCachedElement.ts b/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/utils/reuseCachedElement.ts index 6e1e7c56aae..58827aeaa3d 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/utils/reuseCachedElement.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/utils/reuseCachedElement.ts @@ -1,6 +1,4 @@ -import { getEntityFromElement } from 'roosterjs-editor-dom'; -import { isNodeOfType } from '../../domUtils/isNodeOfType'; -import { NodeType } from 'roosterjs-editor-types'; +import { isEntityElement } from '../../domUtils/entityUtils'; /** * @internal @@ -10,7 +8,7 @@ export function reuseCachedElement(parent: Node, element: Node, refNode: Node | // Remove nodes before the one we are hitting since they don't appear in Content Model at this position. // But we don't want to touch entity since it would better to keep entity at its place unless it is removed // In that case we will remove it after we have handled all other nodes - while (refNode && refNode != element && !isEntity(refNode)) { + while (refNode && refNode != element && !isEntityElement(refNode)) { const next = refNode.nextSibling; refNode.parentNode?.removeChild(refNode); @@ -38,7 +36,3 @@ export function removeNode(node: Node): Node | null { return next; } - -function isEntity(node: Node) { - return isNodeOfType(node, NodeType.Element) && !!getEntityFromElement(node); -} diff --git a/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/utils/stackFormat.ts b/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/utils/stackFormat.ts index 666eef9047d..ba2a7974a59 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/utils/stackFormat.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/modelToDom/utils/stackFormat.ts @@ -1,5 +1,5 @@ import { defaultContentModelFormatMap } from '../../config/defaultContentModelFormatMap'; -import { +import type { ContentModelBlockFormat, ContentModelSegmentFormat, ModelToDomContext, diff --git a/packages-content-model/roosterjs-content-model-dom/test/domToModel/processors/brProcessorTest.ts b/packages-content-model/roosterjs-content-model-dom/test/domToModel/processors/brProcessorTest.ts index eccb6309bb0..4ff587c7ffd 100644 --- a/packages-content-model/roosterjs-content-model-dom/test/domToModel/processors/brProcessorTest.ts +++ b/packages-content-model/roosterjs-content-model-dom/test/domToModel/processors/brProcessorTest.ts @@ -1,7 +1,12 @@ import { brProcessor } from '../../../lib/domToModel/processors/brProcessor'; import { createContentModelDocument } from '../../../lib/modelApi/creators/createContentModelDocument'; import { createDomToModelContext } from '../../../lib/domToModel/context/createDomToModelContext'; -import { DomToModelContext } from 'roosterjs-content-model-types'; +import { + ContentModelBr, + ContentModelDomIndexer, + ContentModelParagraph, + DomToModelContext, +} from 'roosterjs-content-model-types'; describe('brProcessor', () => { let context: DomToModelContext; @@ -59,4 +64,37 @@ describe('brProcessor', () => { ], }); }); + + it('Br with domIndexer', () => { + const doc = createContentModelDocument(); + const br = document.createElement('br'); + const onSegmentSpy = jasmine.createSpy('onSegment'); + const domIndexer: ContentModelDomIndexer = { + onParagraph: null!, + onSegment: onSegmentSpy, + onTable: null!, + reconcileSelection: null!, + }; + + context.domIndexer = domIndexer; + + brProcessor(doc, br, context); + + const brModel: ContentModelBr = { + segmentType: 'Br', + format: {}, + }; + const paragraphModel: ContentModelParagraph = { + blockType: 'Paragraph', + isImplicit: true, + segments: [brModel], + format: {}, + }; + + expect(doc).toEqual({ + blockGroupType: 'Document', + blocks: [paragraphModel], + }); + expect(onSegmentSpy).toHaveBeenCalledWith(br, paragraphModel, [brModel]); + }); }); diff --git a/packages-content-model/roosterjs-content-model-dom/test/domToModel/processors/childProcessorTest.ts b/packages-content-model/roosterjs-content-model-dom/test/domToModel/processors/childProcessorTest.ts index 4a92fb4a3c8..4be880b0ffa 100644 --- a/packages-content-model/roosterjs-content-model-dom/test/domToModel/processors/childProcessorTest.ts +++ b/packages-content-model/roosterjs-content-model-dom/test/domToModel/processors/childProcessorTest.ts @@ -2,7 +2,6 @@ import { childProcessor } from '../../../lib/domToModel/processors/childProcesso import { createContentModelDocument } from '../../../lib/modelApi/creators/createContentModelDocument'; import { createDomToModelContext } from '../../../lib/domToModel/context/createDomToModelContext'; import { generalProcessor } from '../../../lib/domToModel/processors/generalProcessor'; -import { SelectionRangeTypes } from 'roosterjs-editor-types'; import { ContentModelDocument, DomToModelContext, @@ -121,18 +120,15 @@ describe('childProcessor', () => { it('Process a DIV with element selection', () => { const div = document.createElement('div'); div.innerHTML = 'test1test2test3'; - context.rangeEx = { - type: SelectionRangeTypes.Normal, - ranges: [ - { - startContainer: div, - startOffset: 1, - endContainer: div, - endOffset: 2, - collapsed: false, - } as any, - ], - areAllCollapsed: false, + context.selection = { + type: 'range', + range: { + startContainer: div, + startOffset: 1, + endContainer: div, + endOffset: 2, + collapsed: false, + } as any, }; childProcessor(doc, div, context); @@ -159,18 +155,15 @@ describe('childProcessor', () => { const div = document.createElement('div'); div.innerHTML = 'test1test2test3'; - context.rangeEx = { - type: SelectionRangeTypes.Normal, - ranges: [ - { - startContainer: div, - startOffset: 1, - endContainer: div, - endOffset: 1, - collapsed: true, - } as any, - ], - areAllCollapsed: true, + context.selection = { + type: 'range', + range: { + startContainer: div, + startOffset: 1, + endContainer: div, + endOffset: 1, + collapsed: true, + } as any, }; childProcessor(doc, div, context); @@ -185,7 +178,8 @@ describe('childProcessor', () => { isSelected: true, format: {}, }, - { segmentType: 'Text', text: 'test2test3', format: {} }, + { segmentType: 'Text', text: 'test2', format: {} }, + { segmentType: 'Text', text: 'test3', format: {} }, ], isImplicit: true, format: {}, @@ -196,18 +190,15 @@ describe('childProcessor', () => { const div = document.createElement('div'); div.innerHTML = 'test1test2test3'; - context.rangeEx = { - type: SelectionRangeTypes.Normal, - ranges: [ - { - startContainer: div.firstChild!, - startOffset: 5, - endContainer: div.firstChild!, - endOffset: 10, - collapsed: false, - } as any, - ], - areAllCollapsed: false, + context.selection = { + type: 'range', + range: { + startContainer: div.firstChild!, + startOffset: 5, + endContainer: div.firstChild!, + endOffset: 10, + collapsed: false, + } as any, }; childProcessor(doc, div, context); @@ -234,18 +225,15 @@ describe('childProcessor', () => { const div = document.createElement('div'); div.innerHTML = 'test1test2test3'; - context.rangeEx = { - type: SelectionRangeTypes.Normal, - ranges: [ - { - startContainer: div.firstChild!, - startOffset: 5, - endContainer: div.firstChild!, - endOffset: 5, - collapsed: true, - } as any, - ], - areAllCollapsed: true, + context.selection = { + type: 'range', + range: { + startContainer: div.firstChild!, + startOffset: 5, + endContainer: div.firstChild!, + endOffset: 5, + collapsed: true, + } as any, }; childProcessor(doc, div, context); @@ -271,18 +259,15 @@ describe('childProcessor', () => { const div = document.createElement('div'); div.innerHTML = 'test1test2test3'; - context.rangeEx = { - type: SelectionRangeTypes.Normal, - ranges: [ - { - startContainer: div.firstChild!, - startOffset: 1, - endContainer: div.lastChild!, - endOffset: 5, - collapsed: false, - } as any, - ], - areAllCollapsed: false, + context.selection = { + type: 'range', + range: { + startContainer: div.firstChild!, + startOffset: 1, + endContainer: div.lastChild!, + endOffset: 5, + collapsed: false, + } as any, }; childProcessor(doc, div, context); @@ -320,18 +305,15 @@ describe('childProcessor', () => { const div = document.createElement('div'); context.segmentFormat = { a: 'b' } as any; - context.rangeEx = { - type: SelectionRangeTypes.Normal, - ranges: [ - { - startContainer: div, - startOffset: 0, - endContainer: div, - endOffset: 0, - collapsed: true, - } as any, - ], - areAllCollapsed: true, + context.selection = { + type: 'range', + range: { + startContainer: div, + startOffset: 0, + endContainer: div, + endOffset: 0, + collapsed: true, + } as any, }; childProcessor(doc, div, context); diff --git a/packages-content-model/roosterjs-content-model-dom/test/domToModel/processors/delimiterProcessorTest.ts b/packages-content-model/roosterjs-content-model-dom/test/domToModel/processors/delimiterProcessorTest.ts index 310d3d984c8..d63a35989fd 100644 --- a/packages-content-model/roosterjs-content-model-dom/test/domToModel/processors/delimiterProcessorTest.ts +++ b/packages-content-model/roosterjs-content-model-dom/test/domToModel/processors/delimiterProcessorTest.ts @@ -4,7 +4,6 @@ import { createDomToModelContext } from '../../../lib/domToModel/context/createD import { createRange } from 'roosterjs-editor-dom'; import { delimiterProcessor } from '../../../lib/domToModel/processors/delimiterProcessor'; import { DomToModelContext } from 'roosterjs-content-model-types'; -import { SelectionRangeTypes } from 'roosterjs-editor-types'; describe('delimiterProcessor', () => { let context: DomToModelContext; @@ -40,10 +39,9 @@ describe('delimiterProcessor', () => { div.appendChild(span); div.appendChild(span2); - context.rangeEx = { - type: SelectionRangeTypes.Normal, - ranges: [createRange(text, 0, span2, 0)], - areAllCollapsed: false, + context.selection = { + type: 'range', + range: createRange(text, 0, span2, 0), }; delimiterProcessor(doc, span, context); diff --git a/packages-content-model/roosterjs-content-model-dom/test/domToModel/processors/elementProcessorTest.ts b/packages-content-model/roosterjs-content-model-dom/test/domToModel/processors/elementProcessorTest.ts index 219b3804f45..83d95b35df4 100644 --- a/packages-content-model/roosterjs-content-model-dom/test/domToModel/processors/elementProcessorTest.ts +++ b/packages-content-model/roosterjs-content-model-dom/test/domToModel/processors/elementProcessorTest.ts @@ -1,8 +1,8 @@ import * as getDelimiterFromElement from 'roosterjs-editor-dom/lib/delimiter/getDelimiterFromElement'; -import { commitEntity } from 'roosterjs-editor-dom'; import { createContentModelDocument } from '../../../lib/modelApi/creators/createContentModelDocument'; import { createDomToModelContext } from '../../../lib/domToModel/context/createDomToModelContext'; import { elementProcessor } from '../../../lib/domToModel/processors/elementProcessor'; +import { setEntityElementClasses } from '../../domUtils/entityUtilTest'; import { ContentModelDocument, DomToModelContext, @@ -59,7 +59,7 @@ describe('elementProcessor', () => { it('Entity', () => { const div = document.createElement('div'); - commitEntity(div, 'entity', true, 'entity_1'); + setEntityElementClasses(div, 'entity', true, 'entity_1'); elementProcessor(group, div, context); diff --git a/packages-content-model/roosterjs-content-model-dom/test/domToModel/processors/entityProcessorTest.ts b/packages-content-model/roosterjs-content-model-dom/test/domToModel/processors/entityProcessorTest.ts index 72ee02b35a1..26826c063a4 100644 --- a/packages-content-model/roosterjs-content-model-dom/test/domToModel/processors/entityProcessorTest.ts +++ b/packages-content-model/roosterjs-content-model-dom/test/domToModel/processors/entityProcessorTest.ts @@ -1,8 +1,13 @@ -import { commitEntity } from 'roosterjs-editor-dom'; import { createContentModelDocument } from '../../../lib/modelApi/creators/createContentModelDocument'; import { createDomToModelContext } from '../../../lib/domToModel/context/createDomToModelContext'; -import { DomToModelContext } from 'roosterjs-content-model-types'; import { entityProcessor } from '../../../lib/domToModel/processors/entityProcessor'; +import { setEntityElementClasses } from '../../domUtils/entityUtilTest'; +import { + ContentModelDomIndexer, + ContentModelEntity, + ContentModelParagraph, + DomToModelContext, +} from 'roosterjs-content-model-types'; describe('entityProcessor', () => { let context: DomToModelContext; @@ -25,9 +30,12 @@ describe('entityProcessor', () => { blockType: 'Entity', segmentType: 'Entity', format: {}, - id: undefined, - type: undefined, - isReadonly: true, + entityFormat: { + isFakeEntity: true, + id: undefined, + entityType: undefined, + isReadonly: true, + }, wrapper: div, }, ], @@ -38,7 +46,7 @@ describe('entityProcessor', () => { const group = createContentModelDocument(); const div = document.createElement('div'); - commitEntity(div, 'entity', true, 'entity_1'); + setEntityElementClasses(div, 'entity', true, 'entity_1'); entityProcessor(group, div, context); @@ -50,9 +58,11 @@ describe('entityProcessor', () => { blockType: 'Entity', segmentType: 'Entity', format: {}, - id: 'entity_1', - type: 'entity', - isReadonly: true, + entityFormat: { + id: 'entity_1', + entityType: 'entity', + isReadonly: true, + }, wrapper: div, }, ], @@ -63,7 +73,7 @@ describe('entityProcessor', () => { const group = createContentModelDocument(); const span = document.createElement('span'); - commitEntity(span, 'entity', true, 'entity_1'); + setEntityElementClasses(span, 'entity', true, 'entity_1'); entityProcessor(group, span, context); @@ -79,9 +89,11 @@ describe('entityProcessor', () => { blockType: 'Entity', segmentType: 'Entity', format: {}, - id: 'entity_1', - type: 'entity', - isReadonly: true, + entityFormat: { + id: 'entity_1', + entityType: 'entity', + isReadonly: true, + }, wrapper: span, }, ], @@ -101,7 +113,40 @@ describe('entityProcessor', () => { expect(group).toEqual({ blockGroupType: 'Document', + blocks: [ + { + blockType: 'Paragraph', + isImplicit: true, + segments: [ + { + blockType: 'Entity', + segmentType: 'Entity', + format: {}, + entityFormat: { + isFakeEntity: true, + id: undefined, + entityType: undefined, + isReadonly: true, + }, + wrapper: span, + }, + ], + format: {}, + }, + ], + }); + }); + + it('Readonly element (editable fake entity)', () => { + const group = createContentModelDocument(); + const span = document.createElement('span'); + + span.contentEditable = 'true'; + entityProcessor(group, span, context); + + expect(group).toEqual({ + blockGroupType: 'Document', blocks: [ { blockType: 'Paragraph', @@ -111,9 +156,12 @@ describe('entityProcessor', () => { blockType: 'Entity', segmentType: 'Entity', format: {}, - id: undefined, - type: undefined, - isReadonly: true, + entityFormat: { + isFakeEntity: true, + id: undefined, + entityType: undefined, + isReadonly: false, + }, wrapper: span, }, ], @@ -127,7 +175,7 @@ describe('entityProcessor', () => { const group = createContentModelDocument(); const span = document.createElement('span'); - commitEntity(span, 'entity', true, 'entity_1'); + setEntityElementClasses(span, 'entity', true, 'entity_1'); context.isInSelection = true; entityProcessor(group, span, context); @@ -144,9 +192,11 @@ describe('entityProcessor', () => { blockType: 'Entity', segmentType: 'Entity', format: {}, - id: 'entity_1', - type: 'entity', - isReadonly: true, + entityFormat: { + id: 'entity_1', + entityType: 'entity', + isReadonly: true, + }, wrapper: span, isSelected: true, }, @@ -161,7 +211,7 @@ describe('entityProcessor', () => { const group = createContentModelDocument(); const div = document.createElement('div'); - commitEntity(div, 'entity', true, 'entity_1'); + setEntityElementClasses(div, 'entity', true, 'entity_1'); context.isInSelection = true; context.segmentFormat = { fontFamily: 'Arial', @@ -180,9 +230,7 @@ describe('entityProcessor', () => { segmentType: 'Entity', blockType: 'Entity', format: {}, - id: 'entity_1', - type: 'entity', - isReadonly: true, + entityFormat: { id: 'entity_1', entityType: 'entity', isReadonly: true }, wrapper: div, isSelected: true, }, @@ -197,4 +245,43 @@ describe('entityProcessor', () => { lineHeight: '20px', }); }); + + it('Inline Entity with domIndexer', () => { + const group = createContentModelDocument(); + const span = document.createElement('span'); + + setEntityElementClasses(span, 'entity', true, 'entity_1'); + + const onSegmentSpy = jasmine.createSpy('onSegment'); + const domIndexer: ContentModelDomIndexer = { + onParagraph: null!, + onSegment: onSegmentSpy, + onTable: null!, + reconcileSelection: null!, + }; + + context.domIndexer = domIndexer; + + entityProcessor(group, span, context); + + const entityModel: ContentModelEntity = { + segmentType: 'Entity', + blockType: 'Entity', + format: {}, + wrapper: span, + entityFormat: { entityType: 'entity', id: 'entity_1', isReadonly: true }, + }; + const paragraphModel: ContentModelParagraph = { + blockType: 'Paragraph', + isImplicit: true, + segments: [entityModel], + format: {}, + }; + + expect(group).toEqual({ + blockGroupType: 'Document', + blocks: [paragraphModel], + }); + expect(onSegmentSpy).toHaveBeenCalledWith(span, paragraphModel, [entityModel]); + }); }); diff --git a/packages-content-model/roosterjs-content-model-dom/test/domToModel/processors/generalProcessorTest.ts b/packages-content-model/roosterjs-content-model-dom/test/domToModel/processors/generalProcessorTest.ts index 7e72033bc69..bb215908c18 100644 --- a/packages-content-model/roosterjs-content-model-dom/test/domToModel/processors/generalProcessorTest.ts +++ b/packages-content-model/roosterjs-content-model-dom/test/domToModel/processors/generalProcessorTest.ts @@ -4,10 +4,11 @@ import { childProcessor as originalChildProcessor } from '../../../lib/domToMode import { createContentModelDocument } from '../../../lib/modelApi/creators/createContentModelDocument'; import { createDomToModelContext } from '../../../lib/domToModel/context/createDomToModelContext'; import { generalProcessor } from '../../../lib/domToModel/processors/generalProcessor'; -import { SelectionRangeTypes } from 'roosterjs-editor-types'; import { + ContentModelDomIndexer, ContentModelGeneralBlock, ContentModelGeneralSegment, + ContentModelParagraph, DomToModelContext, ElementProcessor, } from 'roosterjs-content-model-types'; @@ -150,18 +151,15 @@ describe('generalProcessor', () => { const text = document.createTextNode('test'); span.appendChild(text); - context.rangeEx = { - type: SelectionRangeTypes.Normal, - ranges: [ - { - startContainer: text, - startOffset: 1, - endContainer: text, - endOffset: 3, - collapsed: false, - } as any, - ], - areAllCollapsed: false, + context.selection = { + type: 'range', + range: { + startContainer: text, + startOffset: 1, + endContainer: text, + endOffset: 3, + collapsed: false, + } as any, }; childProcessor.and.callFake(originalChildProcessor); @@ -220,18 +218,15 @@ describe('generalProcessor', () => { const text = document.createTextNode('test'); span.appendChild(text); - context.rangeEx = { - type: SelectionRangeTypes.Normal, - ranges: [ - { - startContainer: text, - startOffset: 1, - endContainer: text, - endOffset: 3, - collapsed: false, - } as any, - ], - areAllCollapsed: false, + context.selection = { + type: 'range', + range: { + startContainer: text, + startOffset: 1, + endContainer: text, + endOffset: 3, + collapsed: false, + } as any, }; context.isInSelection = true; @@ -257,7 +252,13 @@ describe('generalProcessor', () => { segments: [ { segmentType: 'Text', - text: 'tes', + text: 't', + format: {}, + isSelected: true, + }, + { + segmentType: 'Text', + text: 'es', format: {}, isSelected: true, }, @@ -310,4 +311,49 @@ describe('generalProcessor', () => { ], }); }); + + it('Process a SPAN element with domIndexer', () => { + const doc = createContentModelDocument(); + const span = document.createElement('span'); + const segment: ContentModelGeneralSegment = { + segmentType: 'General', + blockType: 'BlockGroup', + blockGroupType: 'General', + element: span, + blocks: [], + format: {}, + }; + + spyOn(createGeneralSegment, 'createGeneralSegment').and.returnValue(segment); + + const onSegmentSpy = jasmine.createSpy('onSegment'); + const domIndexer: ContentModelDomIndexer = { + onParagraph: null!, + onSegment: onSegmentSpy, + onTable: null!, + reconcileSelection: null!, + }; + + context.domIndexer = domIndexer; + + const paragraph: ContentModelParagraph = { + blockType: 'Paragraph', + isImplicit: true, + segments: [segment], + format: {}, + }; + + generalProcessor(doc, span, context); + + expect(doc).toEqual({ + blockGroupType: 'Document', + blocks: [paragraph], + }); + + expect(createGeneralSegment.createGeneralSegment).toHaveBeenCalledTimes(1); + expect(createGeneralSegment.createGeneralSegment).toHaveBeenCalledWith(span, {}); + expect(childProcessor).toHaveBeenCalledTimes(1); + expect(childProcessor).toHaveBeenCalledWith(segment, span, context); + expect(onSegmentSpy).toHaveBeenCalledWith(span, paragraph, [segment]); + }); }); diff --git a/packages-content-model/roosterjs-content-model-dom/test/domToModel/processors/imageProcessorTest.ts b/packages-content-model/roosterjs-content-model-dom/test/domToModel/processors/imageProcessorTest.ts index 0ad7ef4521f..cf591255318 100644 --- a/packages-content-model/roosterjs-content-model-dom/test/domToModel/processors/imageProcessorTest.ts +++ b/packages-content-model/roosterjs-content-model-dom/test/domToModel/processors/imageProcessorTest.ts @@ -1,8 +1,12 @@ import { createContentModelDocument } from '../../../lib/modelApi/creators/createContentModelDocument'; import { createDomToModelContext } from '../../../lib/domToModel/context/createDomToModelContext'; -import { DomToModelContext } from 'roosterjs-content-model-types'; import { imageProcessor } from '../../../lib/domToModel/processors/imageProcessor'; -import { SelectionRangeTypes } from 'roosterjs-editor-types'; +import { + ContentModelDomIndexer, + ContentModelImage, + ContentModelParagraph, + DomToModelContext, +} from 'roosterjs-content-model-types'; describe('imageProcessor', () => { let context: DomToModelContext; @@ -100,8 +104,8 @@ describe('imageProcessor', () => { const doc = createContentModelDocument(); const img = document.createElement('img'); - context.rangeEx = { - type: SelectionRangeTypes.ImageSelection, + context.selection = { + type: 'image', image: img, } as any; @@ -136,8 +140,8 @@ describe('imageProcessor', () => { img.id = 'id1'; img.style.display = 'block'; - context.rangeEx = { - type: SelectionRangeTypes.ImageSelection, + context.selection = { + type: 'image', image: img, } as any; @@ -281,4 +285,39 @@ describe('imageProcessor', () => { ], }); }); + + it('Image with domIndexer', () => { + const doc = createContentModelDocument(); + const img = document.createElement('img'); + const onSegmentSpy = jasmine.createSpy('onSegment'); + const domIndexer: ContentModelDomIndexer = { + onParagraph: null!, + onSegment: onSegmentSpy, + onTable: null!, + reconcileSelection: null!, + }; + + context.domIndexer = domIndexer; + + imageProcessor(doc, img, context); + + const segment: ContentModelImage = { + segmentType: 'Image', + format: {}, + src: '', + dataset: {}, + }; + const paragraph: ContentModelParagraph = { + blockType: 'Paragraph', + format: {}, + isImplicit: true, + segments: [segment], + }; + + expect(doc).toEqual({ + blockGroupType: 'Document', + blocks: [paragraph], + }); + expect(onSegmentSpy).toHaveBeenCalledWith(img, paragraph, [segment]); + }); }); diff --git a/packages-content-model/roosterjs-content-model-dom/test/domToModel/processors/tableProcessorTest.ts b/packages-content-model/roosterjs-content-model-dom/test/domToModel/processors/tableProcessorTest.ts index 41ed7ca04b0..0f422c2b555 100644 --- a/packages-content-model/roosterjs-content-model-dom/test/domToModel/processors/tableProcessorTest.ts +++ b/packages-content-model/roosterjs-content-model-dom/test/domToModel/processors/tableProcessorTest.ts @@ -5,10 +5,11 @@ import { childProcessor as originalChildProcessor } from '../../../lib/domToMode import { createContentModelDocument } from '../../../lib/modelApi/creators/createContentModelDocument'; import { createDomToModelContext } from '../../../lib/domToModel/context/createDomToModelContext'; import { createTableCell } from '../../../lib/modelApi/creators/createTableCell'; -import { SelectionRangeTypes } from 'roosterjs-editor-types'; import { tableProcessor } from '../../../lib/domToModel/processors/tableProcessor'; import { ContentModelBlock, + ContentModelDomIndexer, + ContentModelTable, DomToModelContext, ElementProcessor, } from 'roosterjs-content-model-types'; @@ -244,19 +245,13 @@ describe('tableProcessor', () => { const div = document.createElement('div'); div.innerHTML = tableHTML; - context.rangeEx = { - type: SelectionRangeTypes.TableSelection, + context.selection = { + type: 'table', table: div.firstChild as HTMLTableElement, - coordinates: { - firstCell: { - x: 1, - y: 0, - }, - lastCell: { - x: 1, - y: 1, - }, - }, + firstRow: 0, + lastRow: 1, + firstColumn: 1, + lastColumn: 1, } as any; tdModel2.isSelected = true; @@ -285,6 +280,52 @@ describe('tableProcessor', () => { expect(childProcessor).toHaveBeenCalledTimes(4); }); + + it('Table with domIndexer', () => { + const doc = createContentModelDocument(); + const div = document.createElement('div'); + const onTableSpy = jasmine.createSpy('onTable'); + const domIndexer: ContentModelDomIndexer = { + onParagraph: null!, + onSegment: null!, + onTable: onTableSpy, + reconcileSelection: null!, + }; + + context.domIndexer = domIndexer; + + div.innerHTML = '
'; + + tableProcessor(doc, div.firstChild as HTMLTableElement, context); + + const tableModel: ContentModelTable = { + blockType: 'Table', + rows: [ + { + format: {}, + height: 200, + cells: [ + { + blockGroupType: 'TableCell', + spanAbove: false, + spanLeft: false, + isHeader: false, + blocks: [], + format: {}, + dataset: {}, + }, + ], + }, + ], + format: {}, + widths: [100], + dataset: {}, + }; + + expect(doc.blocks[0]).toEqual(tableModel); + + expect(onTableSpy).toHaveBeenCalledWith(div.firstChild, tableModel); + }); }); describe('tableProcessor with format', () => { diff --git a/packages-content-model/roosterjs-content-model-dom/test/domToModel/processors/textProcessorTest.ts b/packages-content-model/roosterjs-content-model-dom/test/domToModel/processors/textProcessorTest.ts index 5217d221f35..c1e0534d2a4 100644 --- a/packages-content-model/roosterjs-content-model-dom/test/domToModel/processors/textProcessorTest.ts +++ b/packages-content-model/roosterjs-content-model-dom/test/domToModel/processors/textProcessorTest.ts @@ -3,10 +3,16 @@ import { addSegment } from '../../../lib/modelApi/common/addSegment'; import { createContentModelDocument } from '../../../lib/modelApi/creators/createContentModelDocument'; import { createDomToModelContext } from '../../../lib/domToModel/context/createDomToModelContext'; import { createParagraph } from '../../../lib/modelApi/creators/createParagraph'; +import { createRange } from 'roosterjs-editor-dom'; +import { createSelectionMarker } from '../../../lib/modelApi/creators/createSelectionMarker'; import { createText } from '../../../lib/modelApi/creators/createText'; -import { DomToModelContext } from 'roosterjs-content-model-types'; -import { SelectionRangeTypes } from 'roosterjs-editor-types'; import { textProcessor } from '../../../lib/domToModel/processors/textProcessor'; +import { + ContentModelDomIndexer, + ContentModelParagraph, + ContentModelText, + DomToModelContext, +} from 'roosterjs-content-model-types'; describe('textProcessor', () => { let context: DomToModelContext; @@ -95,7 +101,12 @@ describe('textProcessor', () => { segments: [ { segmentType: 'Text', - text: 'test0test1', + text: 'test0', + format: {}, + }, + { + segmentType: 'Text', + text: 'test1', format: {}, }, ], @@ -255,7 +266,13 @@ describe('textProcessor', () => { segments: [ { segmentType: 'Text', - text: 'test1test2', + text: 'test1', + isSelected: true, + format: {}, + }, + { + segmentType: 'Text', + text: 'test2', isSelected: true, format: {}, }, @@ -357,18 +374,15 @@ describe('textProcessor', () => { const text = document.createTextNode('test'); context.link = { format: { href: '/test' }, dataset: {} }; - context.rangeEx = { - type: SelectionRangeTypes.Normal, - ranges: [ - { - startContainer: text, - startOffset: 2, - endContainer: text, - endOffset: 2, - collapsed: true, - } as any, - ], - areAllCollapsed: true, + context.selection = { + type: 'range', + range: { + startContainer: text, + startOffset: 2, + endContainer: text, + endOffset: 2, + collapsed: true, + } as any, }; textProcessor(doc, text, context); @@ -441,7 +455,14 @@ describe('textProcessor', () => { expect(doc).toEqual({ blockGroupType: 'Document', - blocks: [], + blocks: [ + { + blockType: 'Paragraph', + segments: [], + format: {}, + isImplicit: true, + }, + ], }); }); @@ -453,7 +474,14 @@ describe('textProcessor', () => { expect(doc).toEqual({ blockGroupType: 'Document', - blocks: [], + blocks: [ + { + blockType: 'Paragraph', + segments: [], + format: {}, + isImplicit: true, + }, + ], }); }); @@ -494,7 +522,12 @@ describe('textProcessor', () => { { segmentType: 'Text', format: {}, - text: 'test ', + text: 'test', + }, + { + segmentType: 'Text', + format: {}, + text: ' ', }, ], }, @@ -532,4 +565,131 @@ describe('textProcessor', () => { ], }); }); + + it('Empty group with domIndexer', () => { + const doc = createContentModelDocument(); + const text = document.createTextNode('test'); + const onSegmentSpy = jasmine.createSpy('onSegment'); + const domIndexer: ContentModelDomIndexer = { + onParagraph: null!, + onSegment: onSegmentSpy, + onTable: null!, + reconcileSelection: null!, + }; + + context.domIndexer = domIndexer; + + textProcessor(doc, text, context); + + const segment: ContentModelText = { + segmentType: 'Text', + text: 'test', + format: {}, + }; + const paragraph: ContentModelParagraph = { + blockType: 'Paragraph', + isImplicit: true, + segments: [segment], + format: {}, + }; + + expect(doc).toEqual({ + blockGroupType: 'Document', + blocks: [paragraph], + }); + expect(onSegmentSpy).toHaveBeenCalledWith(text, paragraph, [segment]); + }); + + it('Empty group with domIndexer and collapsed selection', () => { + const doc = createContentModelDocument(); + const text = document.createTextNode('test'); + const onSegmentSpy = jasmine.createSpy('onSegment'); + const domIndexer: ContentModelDomIndexer = { + onParagraph: null!, + onSegment: onSegmentSpy, + onTable: null!, + reconcileSelection: null!, + }; + + context.domIndexer = domIndexer; + context.selection = { + type: 'range', + range: createRange(text, 2), + }; + + textProcessor(doc, text, context); + + const segment1: ContentModelText = { + segmentType: 'Text', + text: 'te', + format: {}, + }; + const segment2: ContentModelText = { + segmentType: 'Text', + text: 'st', + format: {}, + }; + const marker = createSelectionMarker(); + const paragraph: ContentModelParagraph = { + blockType: 'Paragraph', + isImplicit: true, + segments: [segment1, marker, segment2], + format: {}, + }; + + expect(doc).toEqual({ + blockGroupType: 'Document', + blocks: [paragraph], + }); + expect(onSegmentSpy).toHaveBeenCalledWith(text, paragraph, [segment1, segment2]); + }); + + it('Empty group with domIndexer and expanded selection', () => { + const doc = createContentModelDocument(); + const text = document.createTextNode('test'); + const onSegmentSpy = jasmine.createSpy('onSegment'); + const domIndexer: ContentModelDomIndexer = { + onParagraph: null!, + onSegment: onSegmentSpy, + onTable: null!, + reconcileSelection: null!, + }; + + context.domIndexer = domIndexer; + context.selection = { + type: 'range', + range: createRange(text, 1, text, 3), + }; + + textProcessor(doc, text, context); + + const segment1: ContentModelText = { + segmentType: 'Text', + text: 't', + format: {}, + }; + const segment2: ContentModelText = { + segmentType: 'Text', + text: 'es', + format: {}, + isSelected: true, + }; + const segment3: ContentModelText = { + segmentType: 'Text', + text: 't', + format: {}, + }; + const paragraph: ContentModelParagraph = { + blockType: 'Paragraph', + isImplicit: true, + segments: [segment1, segment2, segment3], + format: {}, + }; + + expect(doc).toEqual({ + blockGroupType: 'Document', + blocks: [paragraph], + }); + expect(onSegmentSpy).toHaveBeenCalledWith(text, paragraph, [segment1, segment2, segment3]); + }); }); diff --git a/packages-content-model/roosterjs-content-model-dom/test/domUtils/entityUtilTest.ts b/packages-content-model/roosterjs-content-model-dom/test/domUtils/entityUtilTest.ts new file mode 100644 index 00000000000..7cdcac33cd7 --- /dev/null +++ b/packages-content-model/roosterjs-content-model-dom/test/domUtils/entityUtilTest.ts @@ -0,0 +1,153 @@ +import { ContentModelEntityFormat } from 'roosterjs-content-model-types'; +import { + generateEntityClassNames, + isEntityElement, + parseEntityClassName, +} from '../../lib/domUtils/entityUtils'; + +export function setEntityElementClasses( + wrapper: HTMLElement, + type: string, + isReadonly: boolean, + id?: string +) { + wrapper.className = `_Entity _EType_${type} ${id ? `_EId_${id} ` : ''}_EReadonly_${ + isReadonly ? '1' : '0' + }`; + + if (isReadonly) { + wrapper.contentEditable = 'false'; + } +} + +describe('isEntityElement', () => { + it('Not an entity', () => { + const div = document.createElement('div'); + + const result = isEntityElement(div); + + expect(result).toBeFalse(); + }); + + it('Is an entity', () => { + const div = document.createElement('div'); + + div.className = '_Entity'; + + const result = isEntityElement(div); + + expect(result).toBeTrue(); + }); +}); + +describe('parseEntityClassName', () => { + it('No entity class', () => { + const format: ContentModelEntityFormat = {}; + + const result = parseEntityClassName('test', format); + + expect(result).toBeFalsy(); + expect(format).toEqual({}); + }); + + it('Entity class', () => { + const format: ContentModelEntityFormat = {}; + + const result = parseEntityClassName('_Entity', format); + + expect(result).toBeTrue(); + expect(format).toEqual({}); + }); + + it('EntityId class', () => { + const format: ContentModelEntityFormat = {}; + + const result = parseEntityClassName('_EId_A', format); + + expect(result).toBeFalsy(); + expect(format).toEqual({ + id: 'A', + }); + }); + + it('EntityType class', () => { + const format: ContentModelEntityFormat = {}; + + const result = parseEntityClassName('_EType_B', format); + + expect(result).toBeFalsy(); + expect(format).toEqual({ + entityType: 'B', + }); + }); + + it('Entity readonly class', () => { + const format: ContentModelEntityFormat = {}; + + const result = parseEntityClassName('_EReadonly_1', format); + + expect(result).toBeFalsy(); + expect(format).toEqual({ + isReadonly: true, + }); + }); + + it('Parse class on existing format', () => { + const format: ContentModelEntityFormat = { + id: 'A', + }; + + const result = parseEntityClassName('_EType_B', format); + + expect(result).toBeFalsy(); + expect(format).toEqual({ + id: 'A', + entityType: 'B', + }); + }); +}); + +describe('generateEntityClassNames', () => { + it('Empty format', () => { + const format: ContentModelEntityFormat = {}; + + const className = generateEntityClassNames(format); + + expect(className).toBe('_Entity _EType_ _EReadonly_0'); + }); + + it('Format with type', () => { + const format: ContentModelEntityFormat = { + entityType: 'A', + }; + + const className = generateEntityClassNames(format); + + expect(className).toBe('_Entity _EType_A _EReadonly_0'); + }); + + it('Format with type and id and readonly', () => { + const format: ContentModelEntityFormat = { + entityType: 'A', + id: 'B', + isReadonly: true, + }; + + const className = generateEntityClassNames(format); + + expect(className).toBe('_Entity _EType_A _EId_B _EReadonly_1'); + }); + + it('Fake entity format with type and id and readonly', () => { + const format: ContentModelEntityFormat = { + entityType: 'A', + id: 'B', + isReadonly: true, + isFakeEntity: true, + }; + + const className = generateEntityClassNames(format); + + expect(className).toBe(''); + }); +}); diff --git a/packages-content-model/roosterjs-content-model-dom/test/domUtils/moveChildNodesTest.ts b/packages-content-model/roosterjs-content-model-dom/test/domUtils/moveChildNodesTest.ts new file mode 100644 index 00000000000..c0760ed9d3d --- /dev/null +++ b/packages-content-model/roosterjs-content-model-dom/test/domUtils/moveChildNodesTest.ts @@ -0,0 +1,81 @@ +import { moveChildNodes, wrapAllChildNodes } from '../../lib/domUtils/moveChildNodes'; + +describe('moveChildNodes', () => { + function htmlToDom(html: string) { + let element = document.createElement('DIV'); + element.innerHTML = html; + + return element.firstChild as HTMLElement; + } + + function runTest( + targetHtml: string, + sourceHtml: string, + keepExisting: boolean, + expectedTargetHtml: string + ) { + const source = htmlToDom(sourceHtml); + const target = htmlToDom(targetHtml); + + moveChildNodes(target, source, keepExisting); + + const targetResult = target ? target.outerHTML : ''; + + expect(targetResult).toBe(expectedTargetHtml); + } + + it('null input', () => { + runTest(null!, null!, true, ''); + runTest(null!, null!, false, ''); + }); + + it('null target input', () => { + runTest(null!, '
test
', true, ''); + runTest(null!, '
test
', false, ''); + }); + + it('null source input', () => { + runTest('
test
', null!, true, '
test
'); + runTest('
test
', null!, false, '
'); + }); + + it('null source input', () => { + runTest('
test
', null!, true, '
test
'); + runTest('
test
', null!, false, '
'); + }); + + it('regular case', () => { + runTest( + '
test1test2
', + '
test3test4
', + false, + '
test3test4
' + ); + runTest( + '
test1test2
', + '
test3test4
', + true, + '
test1test2test3test4
' + ); + }); +}); + +describe('wrapAllChildNodes', () => { + it('Single element, no child', () => { + const div = document.createElement('div'); + const result = wrapAllChildNodes(div, 'span'); + + expect(div.innerHTML).toBe(''); + expect(result).toBe(div.firstChild as HTMLElement); + }); + + it('Single element, with child nodes', () => { + const div = document.createElement('div'); + + div.innerHTML = 'testtest2test3'; + const result = wrapAllChildNodes(div, 'span'); + + expect(div.innerHTML).toBe('testtest2test3'); + expect(result).toBe(div.firstChild as HTMLElement); + }); +}); diff --git a/packages-content-model/roosterjs-content-model-dom/test/formatHandlers/entity/entityFormatHandlerTest.ts b/packages-content-model/roosterjs-content-model-dom/test/formatHandlers/entity/entityFormatHandlerTest.ts new file mode 100644 index 00000000000..5bbce49a3fe --- /dev/null +++ b/packages-content-model/roosterjs-content-model-dom/test/formatHandlers/entity/entityFormatHandlerTest.ts @@ -0,0 +1,84 @@ +import { createDomToModelContext } from '../../../lib/domToModel/context/createDomToModelContext'; +import { createModelToDomContext } from '../../../lib/modelToDom/context/createModelToDomContext'; +import { entityFormatHandler } from '../../../lib/formatHandlers/entity/entityFormatHandler'; +import { + DomToModelContext, + EntityInfoFormat, + IdFormat, + ModelToDomContext, +} from 'roosterjs-content-model-types'; + +describe('entityFormatHandler.parse', () => { + let div: HTMLElement; + let format: EntityInfoFormat & IdFormat; + let context: DomToModelContext; + + beforeEach(() => { + div = document.createElement('div'); + format = {}; + context = createDomToModelContext(); + }); + + it('Not an entity', () => { + entityFormatHandler.parse(format, div, context, {}); + expect(format).toEqual({ + isFakeEntity: true, + isReadonly: true, + }); + }); + + it('Not an entity, content editable', () => { + div.contentEditable = 'true'; + entityFormatHandler.parse(format, div, context, {}); + expect(format).toEqual({ + isFakeEntity: true, + isReadonly: false, + }); + }); + + it('Real entity', () => { + div.className = '_Entity _EId_A _EType_B _EReadonly_1'; + entityFormatHandler.parse(format, div, context, {}); + expect(format).toEqual({ + id: 'A', + entityType: 'B', + isReadonly: true, + }); + }); +}); + +describe('entityFormatHandler.apply', () => { + let div: HTMLElement; + let format: EntityInfoFormat & IdFormat; + let context: ModelToDomContext; + + beforeEach(() => { + div = document.createElement('div'); + format = {}; + context = createModelToDomContext(); + }); + + it('No format', () => { + entityFormatHandler.apply(format, div, context); + expect(div.outerHTML).toBe('
'); + }); + + it('Fake entity with entity info', () => { + format.isFakeEntity = true; + format.id = 'A'; + format.entityType = 'B'; + format.isReadonly = true; + entityFormatHandler.apply(format, div, context); + expect(div.outerHTML).toBe('
'); + }); + + it('Real entity with entity info', () => { + format.id = 'A'; + format.entityType = 'B'; + format.isReadonly = true; + entityFormatHandler.apply(format, div, context); + expect(div.outerHTML).toBe( + '
' + ); + }); +}); diff --git a/packages-content-model/roosterjs-content-model-dom/test/formatHandlers/utils/parseValueWithUnitTest.ts b/packages-content-model/roosterjs-content-model-dom/test/formatHandlers/utils/parseValueWithUnitTest.ts index 50fca53d4e1..29e3a4d05e3 100644 --- a/packages-content-model/roosterjs-content-model-dom/test/formatHandlers/utils/parseValueWithUnitTest.ts +++ b/packages-content-model/roosterjs-content-model-dom/test/formatHandlers/utils/parseValueWithUnitTest.ts @@ -1,11 +1,17 @@ -import * as getComputedStyles from 'roosterjs-editor-dom/lib/utils/getComputedStyles'; import { parseValueWithUnit } from '../../../lib/formatHandlers/utils/parseValueWithUnit'; describe('parseValueWithUnit with element', () => { function runTest(unit: string, results: number[]) { - const mockedElement = { + const mockedElement = ({ + ownerDocument: { + defaultView: { + getComputedStyle: () => ({ + fontSize: '15pt', + }), + }, + }, offsetWidth: 1000, - } as HTMLElement; + } as any) as HTMLElement; ['0', '1', '1.1', '-1.1'].forEach((value, i) => { const input = value + unit; @@ -15,10 +21,6 @@ describe('parseValueWithUnit with element', () => { }); } - beforeEach(() => { - spyOn(getComputedStyles, 'getComputedStyle').and.returnValue('15pt'); - }); - it('empty', () => { expect(parseValueWithUnit()).toBe(0); expect(parseValueWithUnit('')).toBe(0); diff --git a/packages-content-model/roosterjs-content-model-dom/test/modelApi/common/addSegmentTest.ts b/packages-content-model/roosterjs-content-model-dom/test/modelApi/common/addSegmentTest.ts index e108b6b2a8e..8d0688c8b07 100644 --- a/packages-content-model/roosterjs-content-model-dom/test/modelApi/common/addSegmentTest.ts +++ b/packages-content-model/roosterjs-content-model-dom/test/modelApi/common/addSegmentTest.ts @@ -9,35 +9,36 @@ describe('addSegment', () => { it('Add segment to empty document', () => { const doc = createContentModelDocument(); const segment = createText('test'); + const result = addSegment(doc, segment); - addSegment(doc, segment); - - expect(doc).toEqual({ - blockGroupType: 'Document', - blocks: [ + const paragraph: ContentModelParagraph = { + blockType: 'Paragraph', + isImplicit: true, + segments: [ { - blockType: 'Paragraph', - isImplicit: true, - segments: [ - { - segmentType: 'Text', - text: 'test', - format: {}, - }, - ], + segmentType: 'Text', + text: 'test', format: {}, }, ], + format: {}, + }; + + expect(doc).toEqual({ + blockGroupType: 'Document', + blocks: [paragraph], }); + expect(result).toEqual(paragraph); }); it('Add segment to document contains an empty paragraph', () => { const doc = createContentModelDocument(); - addBlock(doc, createParagraph(false)); + const para = createParagraph(false); + addBlock(doc, para); const segment = createText('test'); - addSegment(doc, segment); + const result = addSegment(doc, segment); expect(doc).toEqual({ blockGroupType: 'Document', @@ -55,6 +56,7 @@ describe('addSegment', () => { }, ], }); + expect(result).toBe(para); }); it('Add segment to document contains a paragraph with existing text', () => { diff --git a/packages-content-model/roosterjs-content-model-dom/test/modelApi/common/ensureParagraphTest.ts b/packages-content-model/roosterjs-content-model-dom/test/modelApi/common/ensureParagraphTest.ts new file mode 100644 index 00000000000..5d4c57b8f81 --- /dev/null +++ b/packages-content-model/roosterjs-content-model-dom/test/modelApi/common/ensureParagraphTest.ts @@ -0,0 +1,111 @@ +import { ContentModelBlockFormat } from 'roosterjs-content-model-types'; +import { createContentModelDocument } from '../../../lib/modelApi/creators/createContentModelDocument'; +import { createDivider } from '../../../lib/modelApi/creators/createDivider'; +import { createParagraph } from '../../../lib/modelApi/creators/createParagraph'; +import { ensureParagraph } from '../../../lib/modelApi/common/ensureParagraph'; + +describe('ensureParagraph', () => { + it('Empty group', () => { + const doc = createContentModelDocument(); + const result = ensureParagraph(doc); + + expect(doc).toEqual({ + blockGroupType: 'Document', + blocks: [result], + }); + expect(result).toEqual({ + blockType: 'Paragraph', + format: {}, + segments: [], + isImplicit: true, + }); + }); + + it('Empty group with format', () => { + const doc = createContentModelDocument(); + const format: ContentModelBlockFormat = { + backgroundColor: 'red', + }; + const result = ensureParagraph(doc, format); + + expect(doc).toEqual({ + blockGroupType: 'Document', + blocks: [result], + }); + expect(result).toEqual({ + blockType: 'Paragraph', + format: { + backgroundColor: 'red', + }, + segments: [], + isImplicit: true, + }); + }); + + it('Last block is not paragraph', () => { + const doc = createContentModelDocument(); + const divider = createDivider('hr'); + + doc.blocks.push(divider); + + const result = ensureParagraph(doc); + + expect(doc).toEqual({ + blockGroupType: 'Document', + blocks: [divider, result], + }); + expect(result).toEqual({ + blockType: 'Paragraph', + format: {}, + segments: [], + isImplicit: true, + }); + }); + + it('Last block is paragraph', () => { + const doc = createContentModelDocument(); + const paragraph = createParagraph(); + + doc.blocks.push(paragraph); + + const result = ensureParagraph(doc); + + expect(doc).toEqual({ + blockGroupType: 'Document', + blocks: [result], + }); + expect(result).toBe(paragraph); + expect(result).toEqual({ + blockType: 'Paragraph', + format: {}, + segments: [], + }); + }); + + it('Last block is paragraph, do not overwrite format', () => { + const doc = createContentModelDocument(); + const format: ContentModelBlockFormat = { + backgroundColor: 'red', + }; + const paragraph = createParagraph(false, { + backgroundColor: 'green', + }); + + doc.blocks.push(paragraph); + + const result = ensureParagraph(doc, format); + + expect(doc).toEqual({ + blockGroupType: 'Document', + blocks: [result], + }); + expect(result).toBe(paragraph); + expect(result).toEqual({ + blockType: 'Paragraph', + format: { + backgroundColor: 'green', + }, + segments: [], + }); + }); +}); diff --git a/packages-content-model/roosterjs-content-model-dom/test/modelApi/common/isEmptyTest.ts b/packages-content-model/roosterjs-content-model-dom/test/modelApi/common/isEmptyTest.ts index be7e70e3d27..f5bf8275abb 100644 --- a/packages-content-model/roosterjs-content-model-dom/test/modelApi/common/isEmptyTest.ts +++ b/packages-content-model/roosterjs-content-model-dom/test/modelApi/common/isEmptyTest.ts @@ -197,10 +197,12 @@ describe('isEmpty', () => { blockType: 'Entity', segmentType: 'Entity', format: {}, - type: 'Test', - id: 'Test', + entityFormat: { + entityType: 'Test', + id: 'Test', + isReadonly: false, + }, wrapper: document.createElement('div'), - isReadonly: false, }); expect(result).toBeFalse(); diff --git a/packages-content-model/roosterjs-content-model-dom/test/modelApi/creators/creatorsTest.ts b/packages-content-model/roosterjs-content-model-dom/test/modelApi/creators/creatorsTest.ts index 42f484fae4c..b86ef0684eb 100644 --- a/packages-content-model/roosterjs-content-model-dom/test/modelApi/creators/creatorsTest.ts +++ b/packages-content-model/roosterjs-content-model-dom/test/modelApi/creators/creatorsTest.ts @@ -472,34 +472,36 @@ describe('Creators', () => { it('createEntity', () => { const id = 'entity_1'; - const type = 'entity'; + const entityType = 'entity'; const isReadonly = true; const wrapper = document.createElement('div'); - const entityModel = createEntity(wrapper, isReadonly, type, undefined, id); + const entityModel = createEntity(wrapper, isReadonly, undefined, entityType, id); expect(entityModel).toEqual({ blockType: 'Entity', segmentType: 'Entity', format: {}, - id, - type, - isReadonly, + entityFormat: { + id, + entityType, + isReadonly, + }, wrapper, }); }); it('createEntity with format', () => { const id = 'entity_1'; - const type = 'entity'; + const entityType = 'entity'; const isReadonly = true; const wrapper = document.createElement('div'); const entityModel = createEntity( wrapper, isReadonly, - type, { fontSize: '10pt', }, + entityType, id ); @@ -507,9 +509,11 @@ describe('Creators', () => { blockType: 'Entity', segmentType: 'Entity', format: { fontSize: '10pt' }, - id, - type, - isReadonly, + entityFormat: { + id, + entityType, + isReadonly, + }, wrapper, }); }); diff --git a/packages-content-model/roosterjs-content-model-dom/test/modelToDom/contentModelToDomTest.ts b/packages-content-model/roosterjs-content-model-dom/test/modelToDom/contentModelToDomTest.ts new file mode 100644 index 00000000000..3593c544292 --- /dev/null +++ b/packages-content-model/roosterjs-content-model-dom/test/modelToDom/contentModelToDomTest.ts @@ -0,0 +1,404 @@ +import { contentModelToDom } from '../../lib/modelToDom/contentModelToDom'; +import { createBr } from '../../lib/modelApi/creators/createBr'; +import { createContentModelDocument } from '../../lib/modelApi/creators/createContentModelDocument'; +import { createModelToDomContext } from '../../lib/modelToDom/context/createModelToDomContext'; +import { createParagraph } from '../../lib/modelApi/creators/createParagraph'; +import { createSelectionMarker } from '../../lib/modelApi/creators/createSelectionMarker'; +import { ImageSelection, RangeSelection } from 'roosterjs-content-model-types'; + +describe('contentModelToDom', () => { + it('Empty model, no selection', () => { + const parent = document.createElement('div'); + const model = createContentModelDocument(); + const context = createModelToDomContext(); + + const range = contentModelToDom(document, parent, model, context); + + expect(range).toBeNull(); + expect(parent.innerHTML).toBe(''); + expect(context.regularSelection).toEqual({ + current: { + block: null, + segment: null, + }, + }); + }); + + it('With Model, no selection', () => { + const parent = document.createElement('div'); + const model = createContentModelDocument(); + const para = createParagraph(); + const br = createBr(); + const context = createModelToDomContext(); + + para.segments.push(br); + model.blocks.push(para); + + const range = contentModelToDom(document, parent, model, context); + + expect(range).toBeNull(); + expect(parent.innerHTML).toBe('

'); + expect(context.regularSelection).toEqual({ + current: { + block: parent.firstChild, + segment: parent.firstChild!.firstChild, + }, + }); + }); + + it('With Model, with collapsed selection', () => { + const parent = document.createElement('div'); + const model = createContentModelDocument(); + const para = createParagraph(); + const br = createBr(); + const marker = createSelectionMarker(); + const context = createModelToDomContext(); + + para.segments.push(marker, br); + model.blocks.push(para); + + const range = contentModelToDom(document, parent, model, context); + + expect(range!.type).toBe('range'); + expect((range as RangeSelection).range.startContainer).toBe( + parent.firstChild as HTMLElement + ); + expect((range as RangeSelection).range.startOffset).toBe(0); + expect((range as RangeSelection).range.endContainer).toBe(parent.firstChild as HTMLElement); + expect((range as RangeSelection).range.endOffset).toBe(0); + expect(parent.innerHTML).toBe('

'); + }); + + it('Extract selection range - normal collapsed range', () => { + const mockedHandler = jasmine.createSpy('blockGroupChildren'); + const context = createModelToDomContext(undefined, { + modelHandlerOverride: { + blockGroupChildren: mockedHandler, + }, + }); + + const root = document.createElement('div'); + const div = document.createElement('div'); + const br = document.createElement('br'); + + div.appendChild(br); + root.appendChild(div); + + context.regularSelection.start = { + block: div, + segment: br, + }; + context.regularSelection.end = { + block: div, + segment: br, + }; + + const range = contentModelToDom(document, root, null!, context); + + expect(range!.type).toBe('range'); + expect((range as RangeSelection).range.startContainer).toBe(div); + expect((range as RangeSelection).range.startOffset).toBe(1); + expect((range as RangeSelection).range.endContainer).toBe(div); + expect((range as RangeSelection).range.endOffset).toBe(1); + }); + + it('Extract selection range - normal collapsed range with empty text', () => { + const mockedHandler = jasmine.createSpy('blockGroupChildren'); + const context = createModelToDomContext(undefined, { + modelHandlerOverride: { + blockGroupChildren: mockedHandler, + }, + }); + + const root = document.createElement('div'); + const div = document.createElement('div'); + const txt = document.createTextNode(''); + const br = document.createElement('br'); + + div.appendChild(txt); + div.appendChild(br); + root.appendChild(div); + + context.regularSelection.start = { + block: div, + segment: txt, + }; + context.regularSelection.end = { + block: div, + segment: txt, + }; + + const range = contentModelToDom(document, root, null!, context); + + expect(range!.type).toBe('range'); + expect((range as RangeSelection).range.startContainer).toBe(div); + expect((range as RangeSelection).range.startOffset).toBe(0); + expect((range as RangeSelection).range.endContainer).toBe(div); + expect((range as RangeSelection).range.endOffset).toBe(0); + }); + + it('Extract selection range - normal collapsed range in side text', () => { + const mockedHandler = jasmine.createSpy('blockGroupChildren'); + const context = createModelToDomContext(undefined, { + modelHandlerOverride: { + blockGroupChildren: mockedHandler, + }, + }); + + const root = document.createElement('div'); + const div = document.createElement('div'); + const txt1 = document.createTextNode('test1'); + const txt2 = document.createTextNode('test2'); + + div.appendChild(txt1); + div.appendChild(txt2); + root.appendChild(div); + + context.regularSelection.start = { + block: div, + segment: txt1, + }; + context.regularSelection.end = { + block: div, + segment: txt1, + }; + + const range = contentModelToDom(document, root, null!, context); + + expect(range!.type).toBe('range'); + expect((range as RangeSelection).range.startContainer).toBe(txt1); + expect((range as RangeSelection).range.startOffset).toBe(5); + expect((range as RangeSelection).range.endContainer).toBe(txt1); + expect((range as RangeSelection).range.endOffset).toBe(5); + expect(txt1.nodeValue).toBe('test1test2'); + }); + + it('Extract selection range - no block', () => { + const mockedHandler = jasmine.createSpy('blockGroupChildren'); + const context = createModelToDomContext(undefined, { + modelHandlerOverride: { + blockGroupChildren: mockedHandler, + }, + }); + + const root = document.createElement('div'); + const div = document.createElement('div'); + const txt1 = document.createTextNode('test1'); + + div.appendChild(txt1); + root.appendChild(div); + + context.regularSelection.start = { + block: null, + segment: txt1, + }; + context.regularSelection.end = { + block: null, + segment: txt1, + }; + + const range = contentModelToDom(document, root, null!, context); + + expect(range).toBeNull(); + }); + + it('Extract selection range - no segment', () => { + const mockedHandler = jasmine.createSpy('blockGroupChildren'); + const context = createModelToDomContext(undefined, { + modelHandlerOverride: { + blockGroupChildren: mockedHandler, + }, + }); + + const root = document.createElement('div'); + const div = document.createElement('div'); + const txt1 = document.createTextNode('test1'); + + div.appendChild(txt1); + root.appendChild(div); + + context.regularSelection.start = { + block: div, + segment: null, + }; + context.regularSelection.end = { + block: div, + segment: null, + }; + + const range = contentModelToDom(document, root, null!, context); + + expect(range!.type).toBe('range'); + expect((range as RangeSelection).range.startContainer).toBe(div); + expect((range as RangeSelection).range.startOffset).toBe(0); + expect((range as RangeSelection).range.endContainer).toBe(div); + expect((range as RangeSelection).range.endOffset).toBe(0); + }); + + it('Extract selection range - no end', () => { + const mockedHandler = jasmine.createSpy('blockGroupChildren'); + const context = createModelToDomContext(undefined, { + modelHandlerOverride: { + blockGroupChildren: mockedHandler, + }, + }); + + const root = document.createElement('div'); + const div = document.createElement('div'); + const txt1 = document.createTextNode('test1'); + + div.appendChild(txt1); + root.appendChild(div); + + context.regularSelection.start = { + block: div, + segment: txt1, + }; + + const range = contentModelToDom(document, root, null!, context); + + expect(range).toBeNull(); + }); + + it('Extract selection range - root is fragment - 1', () => { + const mockedHandler = jasmine.createSpy('blockGroupChildren'); + const context = createModelToDomContext(undefined, { + modelHandlerOverride: { + blockGroupChildren: mockedHandler, + }, + }); + + const root = document.createDocumentFragment(); + const txt1 = document.createTextNode('test1'); + + root.appendChild(txt1); + + context.regularSelection.start = { + block: root, + segment: txt1, + }; + context.regularSelection.end = { + block: root, + segment: txt1, + }; + + const range = contentModelToDom(document, root, null!, context); + + expect(range!.type).toBe('range'); + expect((range as RangeSelection).range.startContainer).toBe(txt1); + expect((range as RangeSelection).range.startOffset).toBe(5); + expect((range as RangeSelection).range.endContainer).toBe(txt1); + expect((range as RangeSelection).range.endOffset).toBe(5); + }); + + it('Extract selection range - root is fragment - 2', () => { + const mockedHandler = jasmine.createSpy('blockGroupChildren'); + const context = createModelToDomContext(undefined, { + modelHandlerOverride: { + blockGroupChildren: mockedHandler, + }, + }); + + const root = document.createDocumentFragment(); + const span = document.createElement('span'); + const txt1 = document.createTextNode('test1'); + + root.appendChild(span); + span.appendChild(txt1); + + context.regularSelection.start = { + block: root, + segment: span, + }; + context.regularSelection.end = { + block: root, + segment: span, + }; + + const range = contentModelToDom(document, root, null!, context); + + expect(range!.type).toBe('range'); + expect((range as RangeSelection).range.startContainer).toBe(span); + expect((range as RangeSelection).range.startOffset).toBe(1); + expect((range as RangeSelection).range.endContainer).toBe(span); + expect((range as RangeSelection).range.endOffset).toBe(1); + }); + + it('Extract selection range - expanded range', () => { + const mockedHandler = jasmine.createSpy('blockGroupChildren'); + const context = createModelToDomContext(undefined, { + modelHandlerOverride: { + blockGroupChildren: mockedHandler, + }, + }); + + const root = document.createElement('div'); + const span = document.createElement('span'); + const txt1 = document.createTextNode('test1'); + const txt2 = document.createTextNode('test2'); + const txt3 = document.createTextNode('test3'); + + root.appendChild(span); + span.appendChild(txt1); + span.appendChild(txt2); + span.appendChild(txt3); + + context.regularSelection.start = { + block: span, + segment: txt1, + }; + context.regularSelection.end = { + block: span, + segment: txt2, + }; + + const range = contentModelToDom(document, root, null!, context); + + expect(range!.type).toBe('range'); + expect((range as RangeSelection).range.startContainer).toBe(txt1); + expect((range as RangeSelection).range.startOffset).toBe(5); + expect((range as RangeSelection).range.endContainer).toBe(txt1); + expect((range as RangeSelection).range.endOffset).toBe(10); + expect(txt1.nodeValue).toEqual('test1test2test3'); + }); + + it('Extract selection range - image range', () => { + const mockedHandler = jasmine.createSpy('blockGroupChildren'); + const context = createModelToDomContext(undefined, { + modelHandlerOverride: { + blockGroupChildren: mockedHandler, + }, + }); + + const root = document.createElement('div'); + const image = document.createElement('img'); + + context.imageSelection = { + type: 'image', + image: image, + }; + + const range = contentModelToDom(document, root, null!, context); + + expect(range!.type).toBe('image'); + expect((range as ImageSelection).image).toBe(image); + }); + + it('Extract selection range - table range', () => { + const mockedHandler = jasmine.createSpy('blockGroupChildren'); + const context = createModelToDomContext(undefined, { + modelHandlerOverride: { + blockGroupChildren: mockedHandler, + }, + }); + + const root = document.createElement('div'); + const mockedSelection = 'Selection' as any; + + context.tableSelection = mockedSelection; + + const range = contentModelToDom(document, root, null!, context); + + expect(range).toBe(mockedSelection); + }); +}); diff --git a/packages-content-model/roosterjs-content-model-dom/test/modelToDom/handlers/handleBlockGroupChildrenTest.ts b/packages-content-model/roosterjs-content-model-dom/test/modelToDom/handlers/handleBlockGroupChildrenTest.ts index 08c8857d360..489c5d9c097 100644 --- a/packages-content-model/roosterjs-content-model-dom/test/modelToDom/handlers/handleBlockGroupChildrenTest.ts +++ b/packages-content-model/roosterjs-content-model-dom/test/modelToDom/handlers/handleBlockGroupChildrenTest.ts @@ -1,10 +1,10 @@ -import { commitEntity } from 'roosterjs-editor-dom'; import { createContentModelDocument } from '../../../lib/modelApi/creators/createContentModelDocument'; import { createListItem } from '../../../lib/modelApi/creators/createListItem'; import { createModelToDomContext } from '../../../lib/modelToDom/context/createModelToDomContext'; import { createParagraph } from '../../../lib/modelApi/creators/createParagraph'; import { handleBlock as originalHandleBlock } from '../../../lib/modelToDom/handlers/handleBlock'; import { handleBlockGroupChildren } from '../../../lib/modelToDom/handlers/handleBlockGroupChildren'; +import { setEntityElementClasses } from '../../domUtils/entityUtilTest'; import { ContentModelBlock, ContentModelBlockGroup, @@ -259,7 +259,10 @@ describe('handleBlockGroupChildren', () => { blockType: 'Entity', format: {}, wrapper: div2, - isReadonly: false, + entityFormat: { + entityType: 'TEST', + isReadonly: false, + }, segmentType: 'Entity', }, { @@ -276,7 +279,7 @@ describe('handleBlockGroupChildren', () => { handleBlockGroupChildren(document, parent, group, context); expect(parent.outerHTML).toBe( - '
test2
' + '
test2
' ); expect(parent.firstChild).toBe(div2); expect(parent.firstChild?.nextSibling).toBe(quote); @@ -336,7 +339,7 @@ describe('handleBlockGroupChildren', () => { div.innerHTML = '
'; const span = document.createElement('span'); - commitEntity(span, 'MyEntity', false); + setEntityElementClasses(span, 'MyEntity', false); parent.appendChild(div); parent.appendChild(span); @@ -359,8 +362,10 @@ describe('handleBlockGroupChildren', () => { segmentType: 'Entity', blockType: 'Entity', wrapper: span, - isReadonly: false, - type: 'MyEntity', + entityFormat: { + isReadonly: false, + entityType: 'MyEntity', + }, format: {}, }, ], diff --git a/packages-content-model/roosterjs-content-model-dom/test/modelToDom/handlers/handleBlockTest.ts b/packages-content-model/roosterjs-content-model-dom/test/modelToDom/handlers/handleBlockTest.ts index 6f04077a554..dc3a20e4785 100644 --- a/packages-content-model/roosterjs-content-model-dom/test/modelToDom/handlers/handleBlockTest.ts +++ b/packages-content-model/roosterjs-content-model-dom/test/modelToDom/handlers/handleBlockTest.ts @@ -143,9 +143,11 @@ describe('handleBlock', () => { segmentType: 'Entity', format: {}, wrapper: element, - type: 'entity', - id: 'entity_1', - isReadonly: true, + entityFormat: { + entityType: 'entity', + id: 'entity_1', + isReadonly: true, + }, }; parent = document.createElement('div'); diff --git a/packages-content-model/roosterjs-content-model-dom/test/modelToDom/handlers/handleEntityTest.ts b/packages-content-model/roosterjs-content-model-dom/test/modelToDom/handlers/handleEntityTest.ts index b6352985ff7..ded0d13fe79 100644 --- a/packages-content-model/roosterjs-content-model-dom/test/modelToDom/handlers/handleEntityTest.ts +++ b/packages-content-model/roosterjs-content-model-dom/test/modelToDom/handlers/handleEntityTest.ts @@ -22,9 +22,11 @@ describe('handleEntity', () => { blockType: 'Entity', segmentType: 'Entity', format: {}, - id: 'entity_1', - type: 'entity', - isReadonly: true, + entityFormat: { + id: 'entity_1', + entityType: 'entity', + isReadonly: true, + }, wrapper: div, }; @@ -49,7 +51,10 @@ describe('handleEntity', () => { segmentType: 'Entity', format: {}, wrapper: div, - isReadonly: true, + entityFormat: { + isFakeEntity: true, + isReadonly: false, + }, }; div.textContent = 'test'; @@ -63,15 +68,41 @@ describe('handleEntity', () => { expect(addDelimiters.default).toHaveBeenCalledTimes(0); }); + it('Readonly fake entity', () => { + const div = document.createElement('div'); + const entityModel: ContentModelEntity = { + blockType: 'Entity', + segmentType: 'Entity', + format: {}, + wrapper: div, + entityFormat: { + isFakeEntity: true, + isReadonly: true, + }, + }; + + div.textContent = 'test'; + + const parent = document.createElement('div'); + + handleEntityBlock(document, parent, entityModel, context, null); + + expect(parent.innerHTML).toBe('
test
'); + expect(div.outerHTML).toBe('
test
'); + expect(addDelimiters.default).toHaveBeenCalledTimes(0); + }); + it('Simple inline readonly entity', () => { const span = document.createElement('span'); const entityModel: ContentModelEntity = { blockType: 'Entity', segmentType: 'Entity', format: {}, - id: 'entity_1', - type: 'entity', - isReadonly: true, + entityFormat: { + id: 'entity_1', + entityType: 'entity', + isReadonly: true, + }, wrapper: span, }; @@ -94,9 +125,11 @@ describe('handleEntity', () => { blockType: 'Entity', segmentType: 'Entity', format: {}, - id: 'entity_1', - type: 'entity', - isReadonly: true, + entityFormat: { + id: 'entity_1', + entityType: 'entity', + isReadonly: true, + }, wrapper: div, }; @@ -131,9 +164,11 @@ describe('handleEntity', () => { blockType: 'Entity', segmentType: 'Entity', format: {}, - id: 'entity_1', - type: 'entity', - isReadonly: true, + entityFormat: { + id: 'entity_1', + entityType: 'entity', + isReadonly: true, + }, wrapper: entityDiv, }; @@ -151,9 +186,11 @@ describe('handleEntity', () => { blockType: 'Entity', segmentType: 'Entity', format: {}, - id: 'entity_1', - type: 'entity', - isReadonly: true, + entityFormat: { + id: 'entity_1', + entityType: 'entity', + isReadonly: true, + }, wrapper: span, }; @@ -182,9 +219,11 @@ describe('handleEntity', () => { blockType: 'Entity', segmentType: 'Entity', format: {}, - id: 'entity_1', - type: 'entity', - isReadonly: true, + entityFormat: { + id: 'entity_1', + entityType: 'entity', + isReadonly: true, + }, wrapper: span, }; @@ -208,9 +247,11 @@ describe('handleEntity', () => { blockType: 'Entity', segmentType: 'Entity', format: {}, - id: 'entity_1', - type: 'entity', - isReadonly: true, + entityFormat: { + id: 'entity_1', + entityType: 'entity', + isReadonly: true, + }, wrapper: entityDiv, }; @@ -234,9 +275,11 @@ describe('handleEntity', () => { blockType: 'Entity', segmentType: 'Entity', format: {}, - id: 'entity_1', - type: 'entity', - isReadonly: true, + entityFormat: { + id: 'entity_1', + entityType: 'entity', + isReadonly: true, + }, wrapper: span, }; @@ -265,9 +308,11 @@ describe('handleEntity', () => { blockType: 'Entity', segmentType: 'Entity', format: {}, - id: 'entity_1', - type: 'entity', - isReadonly: true, + entityFormat: { + id: 'entity_1', + entityType: 'entity', + isReadonly: true, + }, wrapper: span, }; diff --git a/packages-content-model/roosterjs-content-model-dom/test/modelToDom/handlers/handleParagraphTest.ts b/packages-content-model/roosterjs-content-model-dom/test/modelToDom/handlers/handleParagraphTest.ts index 57b45c46787..eb7ed2170d4 100644 --- a/packages-content-model/roosterjs-content-model-dom/test/modelToDom/handlers/handleParagraphTest.ts +++ b/packages-content-model/roosterjs-content-model-dom/test/modelToDom/handlers/handleParagraphTest.ts @@ -1,4 +1,5 @@ import * as stackFormat from '../../../lib/modelToDom/utils/stackFormat'; +import * as unwrap from 'roosterjs-editor-dom/lib/utils/unwrap'; import { createModelToDomContext } from '../../../lib/modelToDom/context/createModelToDomContext'; import { createParagraph } from '../../../lib/modelApi/creators/createParagraph'; import { createText } from '../../../lib/modelApi/creators/createText'; @@ -6,6 +7,7 @@ import { handleParagraph } from '../../../lib/modelToDom/handlers/handleParagrap import { handleSegment as originalHandleSegment } from '../../../lib/modelToDom/handlers/handleSegment'; import { optimize } from '../../../lib/modelToDom/optimizers/optimize'; import { + ContentModelDomIndexer, ContentModelParagraph, ContentModelSegment, ContentModelSegmentHandler, @@ -465,6 +467,30 @@ describe('handleParagraph', () => { expect(onNodeCreated.calls.argsFor(0)[1]).toBe(parent.querySelector('div')); }); + it('With onNodeCreated on implicit paragraph', () => { + const parent = document.createElement('div'); + const segment: ContentModelSegment = { + segmentType: 'Text', + text: 'test', + format: {}, + }; + const paragraph: ContentModelParagraph = { + blockType: 'Paragraph', + segments: [segment], + format: {}, + isImplicit: true, + }; + + const onNodeCreated = jasmine.createSpy('onNodeCreated'); + + context.onNodeCreated = onNodeCreated; + + handleParagraph(document, parent, paragraph, context, null); + + expect(parent.innerHTML).toBe(''); + expect(onNodeCreated).toHaveBeenCalled(); + }); + it('Paragraph with only selection marker and BR', () => { const paragraph: ContentModelParagraph = { blockType: 'Paragraph', @@ -532,4 +558,89 @@ describe('handleParagraph', () => { '
test
' ); }); + + it('Paragraph with domIndexer', () => { + const segment1: ContentModelSegment = { + segmentType: 'Text', + format: {}, + text: 'test', + }; + const segment2: ContentModelSegment = { + segmentType: 'Br', + format: {}, + }; + const paragraph: ContentModelParagraph = { + blockType: 'Paragraph', + segments: [segment1, segment2], + format: {}, + }; + const onSegmentSpy = jasmine.createSpy('onSegment'); + const onParagraphSpy = jasmine.createSpy('onParagraph'); + const domIndexer: ContentModelDomIndexer = { + onParagraph: onParagraphSpy, + onSegment: onSegmentSpy, + onTable: null!, + reconcileSelection: null!, + }; + + context.domIndexer = domIndexer; + + handleSegment.and.callFake(originalHandleSegment); + + handleParagraph(document, parent, paragraph, context, null); + + expect(parent.innerHTML).toBe('
test
'); + expect(onParagraphSpy).toHaveBeenCalledTimes(1); + expect(onParagraphSpy).toHaveBeenCalledWith(parent.firstChild); + expect(onSegmentSpy).toHaveBeenCalledTimes(2); + expect(onSegmentSpy).toHaveBeenCalledWith(parent.firstChild!.firstChild, paragraph, [ + segment1, + ]); + expect(onSegmentSpy).toHaveBeenCalledWith(parent.firstChild!.lastChild, paragraph, [ + segment2, + ]); + }); + + it('Implicit paragraph with domIndexer', () => { + const segment1: ContentModelSegment = { + segmentType: 'Text', + format: {}, + text: 'test', + }; + const segment2: ContentModelSegment = { + segmentType: 'Br', + format: {}, + }; + const paragraph: ContentModelParagraph = { + blockType: 'Paragraph', + segments: [segment1, segment2], + format: {}, + isImplicit: true, + }; + const onSegmentSpy = jasmine.createSpy('onSegment'); + const onParagraphSpy = jasmine.createSpy('onParagraph'); + const domIndexer: ContentModelDomIndexer = { + onParagraph: onParagraphSpy, + onSegment: onSegmentSpy, + onTable: null!, + reconcileSelection: null!, + }; + + context.domIndexer = domIndexer; + + const unwrapSpy = spyOn(unwrap, 'default').and.callThrough(); + handleSegment.and.callFake(originalHandleSegment); + + handleParagraph(document, parent, paragraph, context, null); + + const tempContainer = unwrapSpy.calls.argsFor(0)[0] as HTMLElement; + + expect(parent.innerHTML).toBe('test
'); + expect(tempContainer.outerHTML).toBe('
'); + expect(onParagraphSpy).toHaveBeenCalledTimes(1); + expect(onParagraphSpy).toHaveBeenCalledWith(tempContainer); + expect(onSegmentSpy).toHaveBeenCalledTimes(2); + expect(onSegmentSpy).toHaveBeenCalledWith(parent.firstChild, paragraph, [segment1]); + expect(onSegmentSpy).toHaveBeenCalledWith(parent.lastChild, paragraph, [segment2]); + }); }); diff --git a/packages-content-model/roosterjs-content-model-dom/test/modelToDom/handlers/handleSegmentTest.ts b/packages-content-model/roosterjs-content-model-dom/test/modelToDom/handlers/handleSegmentTest.ts index 0c6adfc2de2..d655674001c 100644 --- a/packages-content-model/roosterjs-content-model-dom/test/modelToDom/handlers/handleSegmentTest.ts +++ b/packages-content-model/roosterjs-content-model-dom/test/modelToDom/handlers/handleSegmentTest.ts @@ -107,10 +107,12 @@ describe('handleSegment', () => { segmentType: 'Entity', blockType: 'Entity', format: {}, - type: 'entity', - id: 'entity_1', + entityFormat: { + entityType: 'entity', + id: 'entity_1', + isReadonly: true, + }, wrapper: div, - isReadonly: true, }; handleSegment(document, parent, segment, context, mockedSegmentNodes); diff --git a/packages-content-model/roosterjs-content-model-dom/test/modelToDom/handlers/handleTableTest.ts b/packages-content-model/roosterjs-content-model-dom/test/modelToDom/handlers/handleTableTest.ts index f0af2442968..a6fd119a446 100644 --- a/packages-content-model/roosterjs-content-model-dom/test/modelToDom/handlers/handleTableTest.ts +++ b/packages-content-model/roosterjs-content-model-dom/test/modelToDom/handlers/handleTableTest.ts @@ -1,10 +1,15 @@ import * as handleBlock from '../../../lib/modelToDom/handlers/handleBlock'; import DarkColorHandlerImpl from 'roosterjs-editor-core/lib/editor/DarkColorHandlerImpl'; -import { ContentModelTable, ModelToDomContext } from 'roosterjs-content-model-types'; import { createModelToDomContext } from '../../../lib/modelToDom/context/createModelToDomContext'; import { createTable } from '../../../lib/modelApi/creators/createTable'; import { createTableCell } from '../../../lib/modelApi/creators/createTableCell'; import { handleTable } from '../../../lib/modelToDom/handlers/handleTable'; +import { + ContentModelDomIndexer, + ContentModelTable, + ContentModelTableRow, + ModelToDomContext, +} from 'roosterjs-content-model-types'; describe('handleTable', () => { let context: ModelToDomContext; @@ -578,4 +583,35 @@ describe('handleTable', () => { '
' ); }); + + it('Regular 1 * 1 table with domIndexer', () => { + const tableRow: ContentModelTableRow = { + format: {}, + height: 0, + cells: [createTableCell(1, 1, false)], + }; + const tableModel: ContentModelTable = { + blockType: 'Table', + rows: [tableRow], + format: {}, + widths: [], + dataset: {}, + }; + const onTableSpy = jasmine.createSpy('onTable'); + const domIndexer: ContentModelDomIndexer = { + onParagraph: null!, + onSegment: null!, + onTable: onTableSpy, + reconcileSelection: null!, + }; + + context.domIndexer = domIndexer; + + const div = document.createElement('div'); + + handleTable(document, div, tableModel, context, null); + + expect(div.innerHTML).toBe('
'); + expect(onTableSpy).toHaveBeenCalledWith(div.firstChild, tableModel); + }); }); diff --git a/packages-content-model/roosterjs-content-model-dom/test/modelToDom/optimizers/optimizeTest.ts b/packages-content-model/roosterjs-content-model-dom/test/modelToDom/optimizers/optimizeTest.ts index 6ae760edabb..3d3a934706f 100644 --- a/packages-content-model/roosterjs-content-model-dom/test/modelToDom/optimizers/optimizeTest.ts +++ b/packages-content-model/roosterjs-content-model-dom/test/modelToDom/optimizers/optimizeTest.ts @@ -1,7 +1,7 @@ import * as mergeNode from '../../../lib/modelToDom/optimizers/mergeNode'; import * as removeUnnecessarySpan from '../../../lib/modelToDom/optimizers/removeUnnecessarySpan'; -import { commitEntity } from 'roosterjs-editor-dom'; import { optimize } from '../../../lib/modelToDom/optimizers/optimize'; +import { setEntityElementClasses } from '../../domUtils/entityUtilTest'; describe('optimize', () => { beforeEach(() => { @@ -43,7 +43,7 @@ describe('real optimization', () => { span1.textContent = 'test1'; childSpan.textContent = 'entity'; - commitEntity(span2, 'test', true); + setEntityElementClasses(span2, 'test', true); span2.appendChild(childSpan); div.appendChild(span1); diff --git a/packages-content-model/roosterjs-content-model-dom/test/modelToDom/utils/reuseCachedElementTest.ts b/packages-content-model/roosterjs-content-model-dom/test/modelToDom/utils/reuseCachedElementTest.ts index 431a62caacb..d09d51e0f0f 100644 --- a/packages-content-model/roosterjs-content-model-dom/test/modelToDom/utils/reuseCachedElementTest.ts +++ b/packages-content-model/roosterjs-content-model-dom/test/modelToDom/utils/reuseCachedElementTest.ts @@ -1,5 +1,5 @@ -import { commitEntity } from 'roosterjs-editor-dom'; import { reuseCachedElement } from '../../../lib/modelToDom/utils/reuseCachedElement'; +import { setEntityElementClasses } from '../../domUtils/entityUtilTest'; describe('reuseCachedElement', () => { it('No refNode', () => { @@ -71,7 +71,7 @@ describe('reuseCachedElement', () => { parent.appendChild(element); parent.appendChild(nextNode); - commitEntity(refNode, 'TestEntity', true); + setEntityElementClasses(refNode, 'TestEntity', true); const result = reuseCachedElement(parent, element, refNode); diff --git a/packages-content-model/roosterjs-content-model-editor/lib/domUtils/borderValues.ts b/packages-content-model/roosterjs-content-model-editor/lib/domUtils/borderValues.ts index 1391b09b900..585742a4e70 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/domUtils/borderValues.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/domUtils/borderValues.ts @@ -1,4 +1,4 @@ -import { Border } from '../publicTypes/interface/Border'; +import type { Border } from '../publicTypes/interface/Border'; const BorderStyles = [ 'none', diff --git a/packages-content-model/roosterjs-content-model-editor/lib/domUtils/metadata/updateImageMetadata.ts b/packages-content-model/roosterjs-content-model-editor/lib/domUtils/metadata/updateImageMetadata.ts index 08139847ff5..2faa6770710 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/domUtils/metadata/updateImageMetadata.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/domUtils/metadata/updateImageMetadata.ts @@ -1,5 +1,5 @@ -import { ContentModelImage, ImageMetadataFormat } from 'roosterjs-content-model-types'; import { updateMetadata } from 'roosterjs-content-model-dom'; +import type { ContentModelImage, ImageMetadataFormat } from 'roosterjs-content-model-types'; import { createNumberDefinition, createObjectDefinition, diff --git a/packages-content-model/roosterjs-content-model-editor/lib/domUtils/metadata/updateTableCellMetadata.ts b/packages-content-model/roosterjs-content-model-editor/lib/domUtils/metadata/updateTableCellMetadata.ts index c8c707769b5..87cd69fe9ac 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/domUtils/metadata/updateTableCellMetadata.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/domUtils/metadata/updateTableCellMetadata.ts @@ -1,7 +1,7 @@ -import { ContentModelTableCell } from 'roosterjs-content-model-types'; import { createBooleanDefinition, createObjectDefinition } from 'roosterjs-editor-dom'; -import { TableCellMetadataFormat } from 'roosterjs-editor-types'; import { updateMetadata } from 'roosterjs-content-model-dom'; +import type { ContentModelTableCell } from 'roosterjs-content-model-types'; +import type { TableCellMetadataFormat } from 'roosterjs-editor-types'; const TableCellMetadataFormatDefinition = createObjectDefinition>( { diff --git a/packages-content-model/roosterjs-content-model-editor/lib/domUtils/metadata/updateTableMetadata.ts b/packages-content-model/roosterjs-content-model-editor/lib/domUtils/metadata/updateTableMetadata.ts index 9692b2de632..7141316890e 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/domUtils/metadata/updateTableMetadata.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/domUtils/metadata/updateTableMetadata.ts @@ -1,6 +1,6 @@ -import { ContentModelTable, TableMetadataFormat } from 'roosterjs-content-model-types'; import { TableBorderFormat } from 'roosterjs-editor-types'; import { updateMetadata } from 'roosterjs-content-model-dom'; +import type { ContentModelTable, TableMetadataFormat } from 'roosterjs-content-model-types'; import { createBooleanDefinition, createNumberDefinition, 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 7c3e9ffad4a..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,11 +1,13 @@ -import { ContentModelEditorCore } from '../publicTypes/ContentModelEditorCore'; -import { ContentModelEditorOptions, IContentModelEditor } from '../publicTypes/IContentModelEditor'; import { createContentModelEditorCore } from './createContentModelEditorCore'; import { EditorBase } from 'roosterjs-editor-core'; -import { SelectionRangeEx } from 'roosterjs-editor-types'; -import { +import type { ContentModelEditorCore } from '../publicTypes/ContentModelEditorCore'; +import type { + ContentModelEditorOptions, + IContentModelEditor, +} from '../publicTypes/IContentModelEditor'; +import type { ContentModelDocument, - ContentModelSegmentFormat, + DOMSelection, DomToModelOption, ModelToDomOption, OnNodeCreated, @@ -25,6 +27,13 @@ export default class ContentModelEditor */ constructor(contentDiv: HTMLDivElement, options: ContentModelEditorOptions = {}) { super(contentDiv, options, createContentModelEditorCore); + + 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 + this.createContentModel(); + } } /** @@ -33,7 +42,7 @@ export default class ContentModelEditor */ createContentModel( option?: DomToModelOption, - selectionOverride?: SelectionRangeEx + selectionOverride?: DOMSelection ): ContentModelDocument { const core = this.getCore(); @@ -50,32 +59,29 @@ export default class ContentModelEditor model: ContentModelDocument, option?: ModelToDomOption, onNodeCreated?: OnNodeCreated - ): SelectionRangeEx | null { + ): DOMSelection | null { const core = this.getCore(); return core.api.setContentModel(core, model, option, onNodeCreated); } /** - * Notify editor the current cache may be invalid + * Get current DOM selection */ - invalidateCache() { + getDOMSelection(): DOMSelection | null { const core = this.getCore(); - if (!core.lifecycle.shadowEditFragment) { - core.cache.cachedModel = undefined; - core.cache.cachedRangeEx = undefined; - } + return core.api.getDOMSelection(core); } /** - * Get default format as ContentModelSegmentFormat. - * This is a replacement of IEditor.getDefaultFormat for Content Model. - * @returns The default format + * Set DOMSelection into editor content. + * This is the replacement of IEditor.select. + * @param selection The selection to set */ - getContentModelDefaultFormat(): ContentModelSegmentFormat { + setDOMSelection(selection: DOMSelection) { const core = this.getCore(); - return core.defaultFormat; + core.api.setDOMSelection(core, selection); } } diff --git a/packages-content-model/roosterjs-content-model-editor/lib/editor/coreApi/createContentModel.ts b/packages-content-model/roosterjs-content-model-editor/lib/editor/coreApi/createContentModel.ts index 4cf8925e2b3..071648a0a20 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/editor/coreApi/createContentModel.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/editor/coreApi/createContentModel.ts @@ -1,12 +1,11 @@ import { cloneModel } from '../../modelApi/common/cloneModel'; -import { DomToModelOption } from 'roosterjs-content-model-types'; -import { SelectionRangeEx } from 'roosterjs-editor-types'; +import type { DOMSelection, DomToModelOption } from 'roosterjs-content-model-types'; import { createDomToModelContext, createDomToModelContextWithConfig, domToContentModel, } from 'roosterjs-content-model-dom'; -import { +import type { ContentModelEditorCore, CreateContentModel, } from '../../publicTypes/ContentModelEditorCore'; @@ -29,10 +28,12 @@ export const createContentModel: CreateContentModel = (core, option, selectionOv if (cachedModel) { return cachedModel; } else { - const model = internalCreateContentModel(core, option, selectionOverride); + const selection = selectionOverride || core.api.getDOMSelection(core) || undefined; + const model = internalCreateContentModel(core, selection, option); if (!option && !selectionOverride) { core.cache.cachedModel = model; + core.cache.cachedSelection = selection; } return model; @@ -41,17 +42,13 @@ export const createContentModel: CreateContentModel = (core, option, selectionOv function internalCreateContentModel( core: ContentModelEditorCore, - option?: DomToModelOption, - selectionOverride?: SelectionRangeEx + selection?: DOMSelection, + option?: DomToModelOption ) { const editorContext = core.api.createEditorContext(core); const domToModelContext = option ? createDomToModelContext(editorContext, ...(core.defaultDomToModelOptions || []), option) : createDomToModelContextWithConfig(core.defaultDomToModelConfig, editorContext); - return domToContentModel( - core.contentDiv, - domToModelContext, - selectionOverride || core.api.getSelectionRangeEx(core) - ); + return domToContentModel(core.contentDiv, domToModelContext, selection); } diff --git a/packages-content-model/roosterjs-content-model-editor/lib/editor/coreApi/createEditorContext.ts b/packages-content-model/roosterjs-content-model-editor/lib/editor/coreApi/createEditorContext.ts index 69006c7e55d..08aafeef4b0 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/editor/coreApi/createEditorContext.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/editor/coreApi/createEditorContext.ts @@ -1,19 +1,20 @@ -import { CreateEditorContext } from '../../publicTypes/ContentModelEditorCore'; -import { EditorContext } from 'roosterjs-content-model-types'; +import type { CreateEditorContext } from '../../publicTypes/ContentModelEditorCore'; +import type { EditorContext } from 'roosterjs-content-model-types'; /** * @internal * Create a EditorContext object used by ContentModel API */ export const createEditorContext: CreateEditorContext = core => { - const { lifecycle, defaultFormat, darkColorHandler, addDelimiterForEntity, contentDiv } = core; + const { lifecycle, format, darkColorHandler, contentDiv, cache } = core; const context: EditorContext = { isDarkMode: lifecycle.isDarkMode, - defaultFormat: defaultFormat, + defaultFormat: format.defaultFormat, darkColorHandler: darkColorHandler, - addDelimiterForEntity: addDelimiterForEntity, + addDelimiterForEntity: true, allowCacheElement: true, + domIndexer: cache.domIndexer, }; checkRootRtl(contentDiv, context); diff --git a/packages-content-model/roosterjs-content-model-editor/lib/editor/coreApi/getDOMSelection.ts b/packages-content-model/roosterjs-content-model-editor/lib/editor/coreApi/getDOMSelection.ts new file mode 100644 index 00000000000..9b73b537fde --- /dev/null +++ b/packages-content-model/roosterjs-content-model-editor/lib/editor/coreApi/getDOMSelection.ts @@ -0,0 +1,41 @@ +import { SelectionRangeTypes } from 'roosterjs-editor-types'; +import type { + ContentModelEditorCore, + GetDOMSelection, +} from '../../publicTypes/ContentModelEditorCore'; +import type { DOMSelection } from 'roosterjs-content-model-types'; + +/** + * @internal + */ +export const getDOMSelection: GetDOMSelection = core => { + return core.cache.cachedSelection ?? getNewSelection(core); +}; + +function getNewSelection(core: ContentModelEditorCore): DOMSelection | null { + // TODO: Get rid of getSelectionRangeEx when we have standalone editor + const rangeEx = core.api.getSelectionRangeEx(core); + + if (rangeEx.type == SelectionRangeTypes.Normal && rangeEx.ranges[0]) { + return { + type: 'range', + range: rangeEx.ranges[0], + }; + } else if (rangeEx.type == SelectionRangeTypes.TableSelection && rangeEx.coordinates) { + return { + type: 'table', + table: rangeEx.table, + firstColumn: rangeEx.coordinates.firstCell.x, + lastColumn: rangeEx.coordinates.lastCell.x, + firstRow: rangeEx.coordinates.firstCell.y, + lastRow: rangeEx.coordinates.lastCell.y, + }; + } else if (rangeEx.type == SelectionRangeTypes.ImageSelection) { + return { + type: 'image', + image: rangeEx.image, + }; + } else { + return null; + } +} diff --git a/packages-content-model/roosterjs-content-model-editor/lib/editor/coreApi/getSelectionRangeEx.ts b/packages-content-model/roosterjs-content-model-editor/lib/editor/coreApi/getSelectionRangeEx.ts deleted file mode 100644 index 45df9c490c0..00000000000 --- a/packages-content-model/roosterjs-content-model-editor/lib/editor/coreApi/getSelectionRangeEx.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { ContentModelEditorCore } from '../../publicTypes/ContentModelEditorCore'; -import { GetSelectionRangeEx } from 'roosterjs-editor-types'; - -/** - * @internal - */ -export const getSelectionRangeEx: GetSelectionRangeEx = core => { - const contentModelCore = core as ContentModelEditorCore; - - return contentModelCore.cache.cachedRangeEx ?? core.originalApi.getSelectionRangeEx(core); -}; diff --git a/packages-content-model/roosterjs-content-model-editor/lib/editor/coreApi/setContentModel.ts b/packages-content-model/roosterjs-content-model-editor/lib/editor/coreApi/setContentModel.ts index 4aad709db70..b274ca09278 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/editor/coreApi/setContentModel.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/editor/coreApi/setContentModel.ts @@ -1,4 +1,4 @@ -import { SetContentModel } from '../../publicTypes/ContentModelEditorCore'; +import type { SetContentModel } from '../../publicTypes/ContentModelEditorCore'; import { contentModelToDom, createModelToDomContext, @@ -11,14 +11,13 @@ import { * @param core The editor core object * @param model The content model to set * @param option Additional options to customize the behavior of Content Model to DOM conversion - * @param onNodeCreated An optional callback that will be called when a DOM node is created */ export const setContentModel: SetContentModel = (core, model, option, onNodeCreated) => { const editorContext = core.api.createEditorContext(core); const modelToDomContext = option ? createModelToDomContext(editorContext, ...(core.defaultModelToDomOptions || []), option) : createModelToDomContextWithConfig(core.defaultModelToDomConfig, editorContext); - const range = contentModelToDom( + const selection = contentModelToDom( core.contentDiv.ownerDocument, core.contentDiv, model, @@ -27,14 +26,14 @@ export const setContentModel: SetContentModel = (core, model, option, onNodeCrea ); if (!core.lifecycle.shadowEditFragment) { - core.api.select(core, range); + core.cache.cachedSelection = selection || undefined; - if (range) { - core.cache.cachedRangeEx = range; + if (selection) { + core.api.setDOMSelection(core, selection); } - } - // TODO: Reconcile selection text node cache + core.cache.cachedModel = model; + } - return range; + return selection; }; diff --git a/packages-content-model/roosterjs-content-model-editor/lib/editor/coreApi/setDOMSelection.ts b/packages-content-model/roosterjs-content-model-editor/lib/editor/coreApi/setDOMSelection.ts new file mode 100644 index 00000000000..44273137c3e --- /dev/null +++ b/packages-content-model/roosterjs-content-model-editor/lib/editor/coreApi/setDOMSelection.ts @@ -0,0 +1,42 @@ +import { SelectionRangeTypes } from 'roosterjs-editor-types'; +import type { SelectionRangeEx } from 'roosterjs-editor-types'; +import type { SetDOMSelection } from '../../publicTypes/ContentModelEditorCore'; + +/** + * @internal + */ +export const setDOMSelection: SetDOMSelection = (core, selection) => { + // TODO: Get rid of SelectionRangeEx in standalone editor + const rangeEx: SelectionRangeEx = + selection.type == 'range' + ? { + type: SelectionRangeTypes.Normal, + ranges: [selection.range], + areAllCollapsed: selection.range.collapsed, + } + : selection.type == 'image' + ? { + type: SelectionRangeTypes.ImageSelection, + ranges: [], + areAllCollapsed: false, + image: selection.image, + } + : { + type: SelectionRangeTypes.TableSelection, + ranges: [], + areAllCollapsed: false, + table: selection.table, + coordinates: { + firstCell: { + x: selection.firstColumn, + y: selection.firstRow, + }, + lastCell: { + x: selection.lastColumn, + y: selection.lastRow, + }, + }, + }; + + core.api.select(core, rangeEx); +}; diff --git a/packages-content-model/roosterjs-content-model-editor/lib/editor/coreApi/switchShadowEdit.ts b/packages-content-model/roosterjs-content-model-editor/lib/editor/coreApi/switchShadowEdit.ts index feb4d828d94..93425f02088 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/editor/coreApi/switchShadowEdit.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/editor/coreApi/switchShadowEdit.ts @@ -1,6 +1,7 @@ -import { ContentModelEditorCore } from '../../publicTypes/ContentModelEditorCore'; import { getSelectionPath } from 'roosterjs-editor-dom'; -import { PluginEventType, SwitchShadowEdit } from 'roosterjs-editor-types'; +import { PluginEventType } from 'roosterjs-editor-types'; +import type { ContentModelEditorCore } from '../../publicTypes/ContentModelEditorCore'; +import type { SwitchShadowEdit } from 'roosterjs-editor-types'; /** * @internal diff --git a/packages-content-model/roosterjs-content-model-editor/lib/editor/corePlugins/ContentModelCachePlugin.ts b/packages-content-model/roosterjs-content-model-editor/lib/editor/corePlugins/ContentModelCachePlugin.ts index 8102176eef5..b839a0e5558 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/editor/corePlugins/ContentModelCachePlugin.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/editor/corePlugins/ContentModelCachePlugin.ts @@ -1,12 +1,14 @@ -import { ContentModelCachePluginState } from '../../publicTypes/pluginState/ContentModelCachePluginState'; -import { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; -import { +import { areSameRangeEx } from '../../modelApi/selection/areSameRangeEx'; +import { isCharacterValue } from 'roosterjs-editor-dom'; +import { Keys, PluginEventType } from 'roosterjs-editor-types'; +import type ContentModelContentChangedEvent from '../../publicTypes/event/ContentModelContentChangedEvent'; +import type { ContentModelCachePluginState } from '../../publicTypes/pluginState/ContentModelCachePluginState'; +import type { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; +import type { IEditor, - Keys, PluginEvent, - PluginEventType, + PluginKeyDownEvent, PluginWithState, - SelectionRangeEx, } from 'roosterjs-editor-types'; /** @@ -77,36 +79,108 @@ export default class ContentModelCachePlugin switch (event.eventType) { case PluginEventType.KeyDown: - switch (event.rawEvent.which) { - case Keys.ENTER: - // ENTER key will create new paragraph, so need to update cache to reflect this change - // TODO: Handle ENTER key to better reuse content model - this.editor.invalidateCache(); - - break; + if (this.shouldClearCache(event)) { + this.invalidateCache(); } break; case PluginEventType.Input: + { + this.updateCachedModel(this.editor, true /*forceUpdate*/); + } + break; + case PluginEventType.SelectionChanged: - this.reconcileSelection(this.editor); + this.updateCachedModel(this.editor); break; case PluginEventType.ContentChanged: - this.editor.invalidateCache(); + { + const { contentModel, selection } = event as ContentModelContentChangedEvent; + + if (contentModel && this.state.domIndexer) { + this.state.cachedModel = contentModel; + this.state.cachedSelection = selection; + } else { + this.invalidateCache(); + } + } + break; } } private onNativeSelectionChange = () => { if (this.editor?.hasFocus()) { - this.reconcileSelection(this.editor); + this.updateCachedModel(this.editor); } }; - private reconcileSelection(editor: IContentModelEditor, newRangeEx?: SelectionRangeEx) { - // TODO: Really do reconcile selection - editor.invalidateCache(); + private invalidateCache() { + if (!this.editor?.isInShadowEdit()) { + this.state.cachedModel = undefined; + this.state.cachedSelection = undefined; + } + } + + private updateCachedModel(editor: IContentModelEditor, forceUpdate?: boolean) { + const cachedSelection = this.state.cachedSelection; + this.state.cachedSelection = undefined; // Clear it to force getDOMSelection() retrieve the latest selection range + + const newRangeEx = editor.getDOMSelection() || undefined; + const model = this.state.cachedModel; + const isSelectionChanged = + forceUpdate || + !cachedSelection || + !newRangeEx || + !areSameRangeEx(newRangeEx, cachedSelection); + + if (isSelectionChanged) { + if ( + !model || + !newRangeEx || + !this.state.domIndexer?.reconcileSelection(model, newRangeEx, cachedSelection) + ) { + this.invalidateCache(); + } else { + this.state.cachedSelection = newRangeEx; + } + } else { + this.state.cachedSelection = cachedSelection; + } + } + + private shouldClearCache(event: PluginKeyDownEvent) { + const { rawEvent, handledByEditFeature } = event; + + // In these cases we can't update the model, so clear cache: + // 1. It is already handled by Content Edit Features + if (handledByEditFeature) { + return true; + } + + // 2. Default behavior is prevented, which means other plugins has handled the event + if (rawEvent.defaultPrevented) { + return true; + } + + // 3. ENTER key is pressed. ENTER key will create new paragraph, so need to update cache to reflect this change + // TODO: Handle ENTER key to better reuse content model + + if (rawEvent.which == Keys.ENTER) { + return true; + } + + // 4. Current selection is image or table or expanded range selection, and is inputting some text + if ( + (this.state.cachedSelection?.type != 'range' || + !this.state.cachedSelection.range.collapsed) && + isCharacterValue(rawEvent) + ) { + return true; + } + + return false; } } diff --git a/packages-content-model/roosterjs-content-model-editor/lib/editor/corePlugins/ContentModelCopyPastePlugin.ts b/packages-content-model/roosterjs-content-model-editor/lib/editor/corePlugins/ContentModelCopyPastePlugin.ts index a3a5eedd2cb..d539cc0b298 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/editor/corePlugins/ContentModelCopyPastePlugin.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/editor/corePlugins/ContentModelCopyPastePlugin.ts @@ -3,40 +3,34 @@ import { cloneModel } from '../../modelApi/common/cloneModel'; import { DeleteResult } from '../../modelApi/edit/utils/DeleteSelectionStep'; import { deleteSelection } from '../../modelApi/edit/deleteSelection'; import { formatWithContentModel } from '../../publicApi/utils/formatWithContentModel'; -import { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; import { iterateSelections } from '../../modelApi/selection/iterateSelections'; import { contentModelToDom, createModelToDomContext, + isElementOfType, + isNodeOfType, + moveChildNodes, normalizeContentModel, + toArray, } from 'roosterjs-content-model-dom'; -import type { - ContentModelBlock, - ContentModelBlockGroup, - ContentModelDecorator, - ContentModelSegment, - ContentModelTableRow, -} from 'roosterjs-content-model-types'; +import type { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; +import type { DOMSelection, OnNodeCreated } from 'roosterjs-content-model-types'; import { addRangeToSelection, createElement, - moveChildNodes, - createRange, extractClipboardItems, - toArray, wrap, - safeInstanceOf, } from 'roosterjs-editor-dom'; -import { - ChangeSource, +import type { CopyPastePluginState, IEditor, - PluginEventType, PluginWithState, - KnownCreateElementDataIndex, ClipboardData, - SelectionRangeTypes, - SelectionRangeEx, +} from 'roosterjs-editor-types'; +import { + ChangeSource, + PluginEventType, + KnownCreateElementDataIndex, ColorTransformDirection, } from 'roosterjs-editor-types'; @@ -95,8 +89,11 @@ export default class ContentModelCopyPastePlugin implements PluginWithState { if (tableContext?.table) { const table = tableContext?.table; @@ -143,7 +144,9 @@ export default class ContentModelCopyPastePlugin implements PluginWithState { cleanUpAndRestoreSelection(tempDiv); editor.focus(); - editor.select(selection); + (editor as IContentModelEditor).setDOMSelection(selection); if (isCut) { formatWithContentModel( @@ -181,6 +184,8 @@ export default class ContentModelCopyPastePlugin implements PluginWithState { - if (safeInstanceOf(node, 'HTMLTableElement')) { +export const onNodeCreated: OnNodeCreated = (_, node): void => { + if (isNodeOfType(node, 'ELEMENT_NODE') && isElementOfType(node, 'table')) { wrap(node, 'div'); } + if (isNodeOfType(node, 'ELEMENT_NODE') && !node.isContentEditable) { + node.removeAttribute('contenteditable'); + } }; diff --git a/packages-content-model/roosterjs-content-model-editor/lib/editor/plugins/ContentModelEditPlugin.ts b/packages-content-model/roosterjs-content-model-editor/lib/editor/corePlugins/ContentModelEditPlugin.ts similarity index 88% rename from packages-content-model/roosterjs-content-model-editor/lib/editor/plugins/ContentModelEditPlugin.ts rename to packages-content-model/roosterjs-content-model-editor/lib/editor/corePlugins/ContentModelEditPlugin.ts index 0258cc14ae9..ebd723b0fa5 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/editor/plugins/ContentModelEditPlugin.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/editor/corePlugins/ContentModelEditPlugin.ts @@ -1,11 +1,10 @@ import keyboardDelete from '../../publicApi/editing/keyboardDelete'; -import { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; -import { +import { Keys, PluginEventType } from 'roosterjs-editor-types'; +import type { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; +import type { EditorPlugin, IEditor, - Keys, PluginEvent, - PluginEventType, PluginKeyDownEvent, } from 'roosterjs-editor-types'; @@ -65,10 +64,7 @@ export default class ContentModelEditPlugin implements EditorPlugin { const rawEvent = event.rawEvent; const which = rawEvent.which; - if (rawEvent.defaultPrevented || event.handledByEditFeature) { - // Other plugins already handled this event, so it is most likely content is already changed, we need to clear cached content model - editor.invalidateCache(); - } else { + if (!rawEvent.defaultPrevented && !event.handledByEditFeature) { // TODO: Consider use ContentEditFeature and need to hide other conflict features that are not based on Content Model switch (which) { case Keys.BACKSPACE: diff --git a/packages-content-model/roosterjs-content-model-editor/lib/editor/plugins/ContentModelFormatPlugin.ts b/packages-content-model/roosterjs-content-model-editor/lib/editor/corePlugins/ContentModelFormatPlugin.ts similarity index 71% rename from packages-content-model/roosterjs-content-model-editor/lib/editor/plugins/ContentModelFormatPlugin.ts rename to packages-content-model/roosterjs-content-model-editor/lib/editor/corePlugins/ContentModelFormatPlugin.ts index 5b615d322ee..a2940ea4734 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/editor/plugins/ContentModelFormatPlugin.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/editor/corePlugins/ContentModelFormatPlugin.ts @@ -1,9 +1,12 @@ import applyDefaultFormat from '../../publicApi/format/applyDefaultFormat'; import applyPendingFormat from '../../publicApi/format/applyPendingFormat'; import { canApplyPendingFormat, clearPendingFormat } from '../../modelApi/format/pendingFormat'; -import { EditorPlugin, IEditor, Keys, PluginEvent, PluginEventType } from 'roosterjs-editor-types'; -import { getObjectKeys, isCharacterValue } from 'roosterjs-editor-dom'; -import { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; +import { getObjectKeys } from 'roosterjs-content-model-dom'; +import { isCharacterValue } from 'roosterjs-editor-dom'; +import { Keys, PluginEventType } from 'roosterjs-editor-types'; +import type { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; +import type { IEditor, PluginEvent, PluginWithState } from 'roosterjs-editor-types'; +import type { ContentModelFormatPluginState } from '../../publicTypes/pluginState/ContentModelFormatPluginState'; // During IME input, KeyDown event will have "Process" as key const ProcessKey = 'Process'; @@ -13,10 +16,19 @@ const ProcessKey = 'Process'; * This includes: * 1. Handle pending format changes when selection is collapsed */ -export default class ContentModelFormatPlugin implements EditorPlugin { +export default class ContentModelFormatPlugin + implements PluginWithState { private editor: IContentModelEditor | null = null; private hasDefaultFormat = false; + /** + * Construct a new instance of ContentModelEditPlugin class + * @param state State of this plugin + */ + constructor(private state: ContentModelFormatPluginState) { + // TODO: Remove tempState parameter once we have standalone Content Model editor + } + /** * Get name of this plugin */ @@ -33,11 +45,10 @@ export default class ContentModelFormatPlugin implements EditorPlugin { initialize(editor: IEditor) { // TODO: Later we may need a different interface for Content Model editor plugin this.editor = editor as IContentModelEditor; - - const defaultFormat = this.editor.getContentModelDefaultFormat(); this.hasDefaultFormat = - getObjectKeys(defaultFormat).filter(x => typeof defaultFormat[x] !== 'undefined') - .length > 0; + getObjectKeys(this.state.defaultFormat).filter( + x => typeof this.state.defaultFormat[x] !== 'undefined' + ).length > 0; } /** @@ -49,6 +60,13 @@ export default class ContentModelFormatPlugin implements EditorPlugin { this.editor = null; } + /** + * Get plugin state object + */ + getState(): ContentModelFormatPluginState { + return this.state; + } + /** * Core method for a plugin. Once an event happens in editor, editor will call this * method of each plugin to handle the event as long as the event is not handled @@ -80,7 +98,7 @@ export default class ContentModelFormatPlugin implements EditorPlugin { this.hasDefaultFormat && (isCharacterValue(event.rawEvent) || event.rawEvent.key == ProcessKey) ) { - applyDefaultFormat(this.editor); + applyDefaultFormat(this.editor, this.state.defaultFormat); } break; @@ -107,6 +125,6 @@ export default class ContentModelFormatPlugin implements EditorPlugin { * Create a new instance of ContentModelFormatPlugin. * This is mostly for unit test */ -export function createContentModelFormatPlugin() { - return new ContentModelFormatPlugin(); +export function createContentModelFormatPlugin(state: ContentModelFormatPluginState) { + return new ContentModelFormatPlugin(state); } diff --git a/packages-content-model/roosterjs-content-model-editor/lib/editor/corePlugins/ContentModelTypeInContainerPlugin.ts b/packages-content-model/roosterjs-content-model-editor/lib/editor/corePlugins/ContentModelTypeInContainerPlugin.ts index 1e83d09ec08..db7747187be 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/editor/corePlugins/ContentModelTypeInContainerPlugin.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/editor/corePlugins/ContentModelTypeInContainerPlugin.ts @@ -1,4 +1,4 @@ -import { EditorPlugin } from 'roosterjs-editor-types'; +import type { EditorPlugin } from 'roosterjs-editor-types'; /** * Dummy plugin, just to skip original TypeInContainerPlugin's behavior 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 1cee2be7ee2..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 @@ -1,21 +1,22 @@ import ContentModelCopyPastePlugin from './corePlugins/ContentModelCopyPastePlugin'; import ContentModelTypeInContainerPlugin from './corePlugins/ContentModelTypeInContainerPlugin'; -import { ContentModelEditorCore } from '../publicTypes/ContentModelEditorCore'; -import { ContentModelEditorOptions } from '../publicTypes/IContentModelEditor'; -import { ContentModelPluginState } from '../publicTypes/pluginState/ContentModelPluginState'; -import { ContentModelSegmentFormat } from 'roosterjs-content-model-types'; -import { CoreCreator, EditorCore, ExperimentalFeatures } from 'roosterjs-editor-types'; +import { contentModelDomIndexer } from './utils/contentModelDomIndexer'; import { createContentModel } from './coreApi/createContentModel'; import { createContentModelCachePlugin } from './corePlugins/ContentModelCachePlugin'; -import { createContentModelEditPlugin } from './plugins/ContentModelEditPlugin'; -import { createContentModelFormatPlugin } from './plugins/ContentModelFormatPlugin'; +import { createContentModelEditPlugin } from './corePlugins/ContentModelEditPlugin'; +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 { getSelectionRangeEx } from './coreApi/getSelectionRangeEx'; +import { createEditorCore } from 'roosterjs-editor-core'; +import { getDOMSelection } from './coreApi/getDOMSelection'; import { setContentModel } from './coreApi/setContentModel'; +import { setDOMSelection } from './coreApi/setDOMSelection'; import { switchShadowEdit } from './coreApi/switchShadowEdit'; import { tablePreProcessor } from './overrides/tablePreProcessor'; +import type { ContentModelEditorCore } from '../publicTypes/ContentModelEditorCore'; +import type { ContentModelEditorOptions } from '../publicTypes/IContentModelEditor'; +import type { ContentModelPluginState } from '../publicTypes/pluginState/ContentModelPluginState'; +import type { CoreCreator, EditorCore } from 'roosterjs-editor-types'; /** * Editor Core creator for Content Model editor @@ -24,29 +25,19 @@ export const createContentModelEditorCore: CoreCreator< ContentModelEditorCore, ContentModelEditorOptions > = (contentDiv, options) => { - const pluginState: ContentModelPluginState = { - cache: {}, - copyPaste: { - allowedCustomPasteType: options.allowedCustomPasteType || [], - }, - }; + const pluginState = getPluginState(options); const modifiedOptions: ContentModelEditorOptions = { ...options, plugins: [ createContentModelCachePlugin(pluginState.cache), ...(options.plugins || []), - createContentModelFormatPlugin(), + createContentModelFormatPlugin(pluginState.format), createContentModelEditPlugin(), ], corePluginOverride: { typeInContainer: new ContentModelTypeInContainerPlugin(), - copyPaste: isFeatureEnabled( - options.experimentalFeatures, - ExperimentalFeatures.ContentModelPaste - ) - ? new ContentModelCopyPastePlugin(pluginState.copyPaste) - : undefined, - ...(options.corePluginOverride || {}), + copyPaste: new ContentModelCopyPastePlugin(pluginState.copyPaste), + ...options.corePluginOverride, }, }; @@ -70,7 +61,6 @@ export function promoteToContentModelEditorCore( const cmCore = core as ContentModelEditorCore; promoteCorePluginState(cmCore, pluginState); - promoteDefaultFormat(cmCore); promoteContentModelInfo(cmCore, options); promoteCoreApi(cmCore); } @@ -82,17 +72,10 @@ function promoteCorePluginState( Object.assign(cmCore, pluginState); } -function promoteDefaultFormat(cmCore: ContentModelEditorCore) { - cmCore.lifecycle.defaultFormat = cmCore.lifecycle.defaultFormat || {}; - cmCore.defaultFormat = getDefaultSegmentFormat(cmCore); -} - function promoteContentModelInfo( cmCore: ContentModelEditorCore, options: ContentModelEditorOptions ) { - const experimentalFeatures = cmCore.lifecycle.experimentalFeatures; - cmCore.defaultDomToModelOptions = [ { processorOverride: { @@ -104,11 +87,6 @@ function promoteContentModelInfo( cmCore.defaultModelToDomOptions = [options.defaultModelToDomOptions]; cmCore.defaultDomToModelConfig = createDomToModelConfig(cmCore.defaultDomToModelOptions); cmCore.defaultModelToDomConfig = createModelToDomConfig(cmCore.defaultModelToDomOptions); - - cmCore.addDelimiterForEntity = isFeatureEnabled( - experimentalFeatures, - ExperimentalFeatures.InlineEntityReadOnlyDelimiters - ); } function promoteCoreApi(cmCore: ContentModelEditorCore) { @@ -116,23 +94,35 @@ function promoteCoreApi(cmCore: ContentModelEditorCore) { cmCore.api.createContentModel = createContentModel; cmCore.api.setContentModel = setContentModel; cmCore.api.switchShadowEdit = switchShadowEdit; - cmCore.api.getSelectionRangeEx = getSelectionRangeEx; + cmCore.api.getDOMSelection = getDOMSelection; + cmCore.api.setDOMSelection = setDOMSelection; cmCore.originalApi.createEditorContext = createEditorContext; cmCore.originalApi.createContentModel = createContentModel; cmCore.originalApi.setContentModel = setContentModel; + cmCore.originalApi.getDOMSelection = getDOMSelection; + cmCore.originalApi.setDOMSelection = setDOMSelection; } -function getDefaultSegmentFormat(core: EditorCore): ContentModelSegmentFormat { - const format = core.lifecycle.defaultFormat ?? {}; - +function getPluginState(options: ContentModelEditorOptions): ContentModelPluginState { + const format = options.defaultFormat || {}; return { - fontWeight: format.bold ? 'bold' : undefined, - italic: format.italic || undefined, - underline: format.underline || undefined, - fontFamily: format.fontFamily || undefined, - fontSize: format.fontSize || undefined, - textColor: format.textColors?.lightModeColor || format.textColor || undefined, - backgroundColor: - format.backgroundColors?.lightModeColor || format.backgroundColor || undefined, + cache: { + domIndexer: options.cacheModel ? contentModelDomIndexer : undefined, + }, + copyPaste: { + allowedCustomPasteType: options.allowedCustomPasteType || [], + }, + format: { + defaultFormat: { + fontWeight: format.bold ? 'bold' : undefined, + italic: format.italic || undefined, + underline: format.underline || undefined, + fontFamily: format.fontFamily || undefined, + fontSize: format.fontSize || undefined, + textColor: format.textColors?.lightModeColor || format.textColor || undefined, + backgroundColor: + format.backgroundColors?.lightModeColor || format.backgroundColor || undefined, + }, + }, }; } diff --git a/packages-content-model/roosterjs-content-model-editor/lib/editor/isContentModelEditor.ts b/packages-content-model/roosterjs-content-model-editor/lib/editor/isContentModelEditor.ts index 51b297a2e84..87c731c6674 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/editor/isContentModelEditor.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/editor/isContentModelEditor.ts @@ -1,5 +1,5 @@ -import { IContentModelEditor } from '../publicTypes/IContentModelEditor'; -import { IEditor } from 'roosterjs-editor-types'; +import type { IContentModelEditor } from '../publicTypes/IContentModelEditor'; +import type { IEditor } from 'roosterjs-editor-types'; /** * Check if the given editor object is Content Model editor diff --git a/packages-content-model/roosterjs-content-model-editor/lib/editor/overrides/tablePreProcessor.ts b/packages-content-model/roosterjs-content-model-editor/lib/editor/overrides/tablePreProcessor.ts index 8b3b539a8a6..5d5c9831c95 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/editor/overrides/tablePreProcessor.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/editor/overrides/tablePreProcessor.ts @@ -1,7 +1,7 @@ import { contains } from 'roosterjs-editor-dom'; -import { DomToModelContext, ElementProcessor } from 'roosterjs-content-model-types'; import { entityProcessor, hasMetadata, tableProcessor } from 'roosterjs-content-model-dom'; import { getSelectionRootNode } from '../../modelApi/selection/getSelectionRootNode'; +import type { DomToModelContext, ElementProcessor } from 'roosterjs-content-model-types'; /** * @internal @@ -21,6 +21,6 @@ function shouldUseTableProcessor(element: HTMLTableElement, context: DomToModelC return ( hasMetadata(element) || context.isInSelection || - contains(element, getSelectionRootNode(context.rangeEx), true /*treatSameNodeAsContain*/) + contains(element, getSelectionRootNode(context.selection), true /*treatSameNodeAsContain*/) ); } diff --git a/packages-content-model/roosterjs-content-model-editor/lib/editor/plugins/PastePlugin/ContentModelPastePlugin.ts b/packages-content-model/roosterjs-content-model-editor/lib/editor/plugins/PastePlugin/ContentModelPastePlugin.ts index 70455d24cf9..cbbf53cfe9b 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/editor/plugins/PastePlugin/ContentModelPastePlugin.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/editor/plugins/PastePlugin/ContentModelPastePlugin.ts @@ -1,22 +1,20 @@ import addParser from './utils/addParser'; -import ContentModelBeforePasteEvent from '../../../publicTypes/event/ContentModelBeforePasteEvent'; import { chainSanitizerCallback, getPasteSource } from 'roosterjs-editor-dom'; -import { ContentModelBlockFormat, FormatParser } from 'roosterjs-content-model-types'; import { deprecatedBorderColorParser } from './utils/deprecatedColorParser'; -import { IContentModelEditor } from '../../../publicTypes/IContentModelEditor'; +import { KnownPasteSourceType, PasteType, PluginEventType } from 'roosterjs-editor-types'; import { parseLink } from './utils/linkParser'; import { processPastedContentFromExcel } from './Excel/processPastedContentFromExcel'; import { processPastedContentFromPowerPoint } from './PowerPoint/processPastedContentFromPowerPoint'; import { processPastedContentFromWordDesktop } from './WordDesktop/processPastedContentFromWordDesktop'; import { processPastedContentWacComponents } from './WacComponents/processPastedContentWacComponents'; -import { +import type ContentModelBeforePasteEvent from '../../../publicTypes/event/ContentModelBeforePasteEvent'; +import type { ContentModelBlockFormat, FormatParser } from 'roosterjs-content-model-types'; +import type { IContentModelEditor } from '../../../publicTypes/IContentModelEditor'; +import type { EditorPlugin, HtmlSanitizerOptions, IEditor, - KnownPasteSourceType, - PasteType, PluginEvent, - PluginEventType, } from 'roosterjs-editor-types'; const GOOGLE_SHEET_NODE_NAME = 'google-sheets-html-origin'; diff --git a/packages-content-model/roosterjs-content-model-editor/lib/editor/plugins/PastePlugin/Excel/processPastedContentFromExcel.ts b/packages-content-model/roosterjs-content-model-editor/lib/editor/plugins/PastePlugin/Excel/processPastedContentFromExcel.ts index 4f2179ec3d0..567900b0f63 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/editor/plugins/PastePlugin/Excel/processPastedContentFromExcel.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/editor/plugins/PastePlugin/Excel/processPastedContentFromExcel.ts @@ -1,7 +1,8 @@ import addParser from '../utils/addParser'; -import ContentModelBeforePasteEvent from '../../../../publicTypes/event/ContentModelBeforePasteEvent'; -import { getTagOfNode, moveChildNodes } from 'roosterjs-editor-dom'; -import { TrustedHTMLHandler } from 'roosterjs-editor-types'; +import { isNodeOfType, moveChildNodes } from 'roosterjs-content-model-dom'; +import { setProcessor } from '../utils/setProcessor'; +import type ContentModelBeforePasteEvent from '../../../../publicTypes/event/ContentModelBeforePasteEvent'; +import type { TrustedHTMLHandler } from 'roosterjs-editor-types'; const LAST_TD_END_REGEX = /<\/\s*td\s*>((?!<\/\s*tr\s*>)[\s\S])*$/i; const LAST_TR_END_REGEX = /<\/\s*tr\s*>((?!<\/\s*table\s*>)[\s\S])*$/i; @@ -29,12 +30,20 @@ export function processPastedContentFromExcel( // For Excel Online const firstChild = fragment.firstChild; - if (firstChild && firstChild.childNodes.length > 0 && getTagOfNode(firstChild) == 'DIV') { + if ( + isNodeOfType(firstChild, 'ELEMENT_NODE') && + firstChild.tagName == 'div' && + firstChild.firstChild + ) { const tableFound = Array.from(firstChild.childNodes).every((child: Node) => { // Tables pasted from Excel Online should be of the format: 0 to N META tags and 1 TABLE tag - return getTagOfNode(child) == 'META' + const tagName = isNodeOfType(child, 'ELEMENT_NODE') && child.tagName; + + return tagName == 'META' ? true - : getTagOfNode(child) == 'TABLE' && child == firstChild.lastChild; + : tagName == 'TABLE' + ? child == firstChild.lastChild + : false; }); // Extract Table from Div @@ -51,6 +60,20 @@ export function processPastedContentFromExcel( format.borderTop = DEFAULT_BORDER_STYLE; } }); + + setProcessor(event.domToModelOption, 'child', (group, element, context) => { + const segmentFormat = { ...context.segmentFormat }; + if (group.blockGroupType === 'TableCell' && group.format.textColor) { + context.segmentFormat.textColor = group.format.textColor; + } + + context.defaultElementProcessors.child(group, element, context); + + if (group.blockGroupType === 'TableCell' && group.format.textColor) { + context.segmentFormat = segmentFormat; + delete group.format.textColor; + } + }); } /** diff --git a/packages-content-model/roosterjs-content-model-editor/lib/editor/plugins/PastePlugin/PowerPoint/processPastedContentFromPowerPoint.ts b/packages-content-model/roosterjs-content-model-editor/lib/editor/plugins/PastePlugin/PowerPoint/processPastedContentFromPowerPoint.ts index aedc779ec2b..668daa5da52 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/editor/plugins/PastePlugin/PowerPoint/processPastedContentFromPowerPoint.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/editor/plugins/PastePlugin/PowerPoint/processPastedContentFromPowerPoint.ts @@ -1,5 +1,5 @@ -import { BeforePasteEvent, TrustedHTMLHandler } from 'roosterjs-editor-types'; -import { moveChildNodes } from 'roosterjs-editor-dom'; +import { moveChildNodes } from 'roosterjs-content-model-dom'; +import type { BeforePasteEvent, TrustedHTMLHandler } from 'roosterjs-editor-types'; /** * @internal diff --git a/packages-content-model/roosterjs-content-model-editor/lib/editor/plugins/PastePlugin/WacComponents/processPastedContentWacComponents.ts b/packages-content-model/roosterjs-content-model-editor/lib/editor/plugins/PastePlugin/WacComponents/processPastedContentWacComponents.ts index 5e1617cf9cc..09452e70548 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/editor/plugins/PastePlugin/WacComponents/processPastedContentWacComponents.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/editor/plugins/PastePlugin/WacComponents/processPastedContentWacComponents.ts @@ -1,8 +1,8 @@ import addParser from '../utils/addParser'; -import ContentModelBeforePasteEvent from '../../../../publicTypes/event/ContentModelBeforePasteEvent'; -import { findClosestElementAncestor, getTagOfNode, matchesSelector } from 'roosterjs-editor-dom'; +import { findClosestElementAncestor, matchesSelector } from 'roosterjs-editor-dom'; import { setProcessor } from '../utils/setProcessor'; -import { +import type ContentModelBeforePasteEvent from '../../../../publicTypes/event/ContentModelBeforePasteEvent'; +import type { ContentModelBlockFormat, ContentModelBlockGroup, ContentModelListItemLevelFormat, @@ -77,7 +77,7 @@ const wacElementProcessor: ElementProcessor = ( element: HTMLElement, context: DomToModelContext ): void => { - const elementTag = getTagOfNode(element); + const elementTag = element.tagName; if (matchesSelector(element, WAC_IDENTIFY_SELECTOR)) { element.style.removeProperty('display'); element.style.removeProperty('margin'); diff --git a/packages-content-model/roosterjs-content-model-editor/lib/editor/plugins/PastePlugin/WordDesktop/processPastedContentFromWordDesktop.ts b/packages-content-model/roosterjs-content-model-editor/lib/editor/plugins/PastePlugin/WordDesktop/processPastedContentFromWordDesktop.ts index f55d40d6407..a7c04b7ec51 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/editor/plugins/PastePlugin/WordDesktop/processPastedContentFromWordDesktop.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/editor/plugins/PastePlugin/WordDesktop/processPastedContentFromWordDesktop.ts @@ -1,10 +1,11 @@ import addParser from '../utils/addParser'; -import ContentModelBeforePasteEvent from '../../../../publicTypes/event/ContentModelBeforePasteEvent'; -import { chainSanitizerCallback, getStyles, moveChildNodes } from 'roosterjs-editor-dom'; +import { chainSanitizerCallback, getStyles } from 'roosterjs-editor-dom'; +import { moveChildNodes } from 'roosterjs-content-model-dom'; import { processWordComments } from './processWordComments'; import { processWordList } from './processWordLists'; import { setProcessor } from '../utils/setProcessor'; -import { +import type ContentModelBeforePasteEvent from '../../../../publicTypes/event/ContentModelBeforePasteEvent'; +import type { ContentModelBlockFormat, ContentModelListItemFormat, ContentModelListItemLevelFormat, diff --git a/packages-content-model/roosterjs-content-model-editor/lib/editor/plugins/PastePlugin/WordDesktop/processWordComments.ts b/packages-content-model/roosterjs-content-model-editor/lib/editor/plugins/PastePlugin/WordDesktop/processWordComments.ts index f49f7513b55..fd264d6a1b9 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/editor/plugins/PastePlugin/WordDesktop/processWordComments.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/editor/plugins/PastePlugin/WordDesktop/processWordComments.ts @@ -1,4 +1,4 @@ -import { safeInstanceOf } from 'roosterjs-editor-dom'; +import { isElementOfType } from 'roosterjs-content-model-dom'; const MSO_COMMENT_ANCHOR_HREF_REGEX = /#_msocom_/; const MSO_SPECIAL_CHARACTER = 'mso-special-character'; @@ -15,8 +15,7 @@ const MSO_ELEMENT_COMMENT_LIST = 'comment-list'; export function processWordComments(styles: Record, element: HTMLElement) { return ( styles[MSO_SPECIAL_CHARACTER] == MSO_SPECIAL_CHARACTER_COMMENT || - (safeInstanceOf(element, 'HTMLAnchorElement') && - MSO_COMMENT_ANCHOR_HREF_REGEX.test(element.href)) || + (isElementOfType(element, 'a') && MSO_COMMENT_ANCHOR_HREF_REGEX.test(element.href)) || styles[MSO_ELEMENT] == MSO_ELEMENT_COMMENT_LIST ); } diff --git a/packages-content-model/roosterjs-content-model-editor/lib/editor/plugins/PastePlugin/WordDesktop/processWordLists.ts b/packages-content-model/roosterjs-content-model-editor/lib/editor/plugins/PastePlugin/WordDesktop/processWordLists.ts index 0cd736fe230..5ad92175939 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/editor/plugins/PastePlugin/WordDesktop/processWordLists.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/editor/plugins/PastePlugin/WordDesktop/processWordLists.ts @@ -1,12 +1,12 @@ import { getStyles } from 'roosterjs-editor-dom'; -import { NodeType } from 'roosterjs-editor-types'; import { addBlock, createListItem, createListLevel, + isNodeOfType, parseFormat, } from 'roosterjs-content-model-dom'; -import { +import type { ContentModelBlockGroup, ContentModelListItemLevelFormat, ContentModelListLevel, @@ -184,7 +184,7 @@ function getFakeBulletText(node: Node, levels?: number): string { if (result.length == 0) { result = 'o'; } - } else if (child.nodeType == NodeType.Element && levels > 1) { + } else if (isNodeOfType(child, 'ELEMENT_NODE') && levels > 1) { // If this is an element and we are not in the last level, try to get the fake bullet // out of the child result = getFakeBulletText(child, levels - 1); @@ -201,7 +201,7 @@ function getFakeBulletText(node: Node, levels?: number): string { * HTML lists */ function isIgnoreNode(node: Node): boolean { - if (node.nodeType == NodeType.Element) { + if (isNodeOfType(node, 'ELEMENT_NODE')) { let listAttribute = getStyles(node as HTMLElement)[MSO_LIST]; if ( listAttribute && diff --git a/packages-content-model/roosterjs-content-model-editor/lib/editor/plugins/PastePlugin/utils/addParser.ts b/packages-content-model/roosterjs-content-model-editor/lib/editor/plugins/PastePlugin/utils/addParser.ts index ebf6c8c5c32..03ba9c9cedf 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/editor/plugins/PastePlugin/utils/addParser.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/editor/plugins/PastePlugin/utils/addParser.ts @@ -1,4 +1,4 @@ -import { +import type { ContentModelFormatMap, DomToModelOption, FormatParser, diff --git a/packages-content-model/roosterjs-content-model-editor/lib/editor/plugins/PastePlugin/utils/deprecatedColorParser.ts b/packages-content-model/roosterjs-content-model-editor/lib/editor/plugins/PastePlugin/utils/deprecatedColorParser.ts index 1165c50f121..a1d271d346e 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/editor/plugins/PastePlugin/utils/deprecatedColorParser.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/editor/plugins/PastePlugin/utils/deprecatedColorParser.ts @@ -1,5 +1,5 @@ -import { BorderFormat, FormatParser } from 'roosterjs-content-model-types'; import { BorderKeys, DeprecatedColors } from 'roosterjs-content-model-dom'; +import type { BorderFormat, FormatParser } from 'roosterjs-content-model-types'; /** * @internal diff --git a/packages-content-model/roosterjs-content-model-editor/lib/editor/plugins/PastePlugin/utils/linkParser.ts b/packages-content-model/roosterjs-content-model-editor/lib/editor/plugins/PastePlugin/utils/linkParser.ts index c4dda78e278..2e508a52c6b 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/editor/plugins/PastePlugin/utils/linkParser.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/editor/plugins/PastePlugin/utils/linkParser.ts @@ -1,5 +1,5 @@ -import { ContentModelHyperLinkFormat, FormatParser } from 'roosterjs-content-model-types'; -import { safeInstanceOf } from 'roosterjs-editor-dom'; +import { isElementOfType } from 'roosterjs-content-model-dom'; +import type { ContentModelHyperLinkFormat, FormatParser } from 'roosterjs-content-model-types'; const SUPPORTED_PROTOCOLS = ['http:', 'https:', 'notes:', 'mailto:', 'onenote:']; const INVALID_LINKS_REGEX = /^file:\/\/\/[a-zA-Z\/]/i; @@ -8,7 +8,7 @@ const INVALID_LINKS_REGEX = /^file:\/\/\/[a-zA-Z\/]/i; * @internal */ export const parseLink: FormatParser = (format, element) => { - if (!safeInstanceOf(element, 'HTMLAnchorElement')) { + if (!isElementOfType(element, 'a')) { return; } diff --git a/packages-content-model/roosterjs-content-model-editor/lib/editor/plugins/PastePlugin/utils/setProcessor.ts b/packages-content-model/roosterjs-content-model-editor/lib/editor/plugins/PastePlugin/utils/setProcessor.ts index 48c2bc32142..2dfd888b73c 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/editor/plugins/PastePlugin/utils/setProcessor.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/editor/plugins/PastePlugin/utils/setProcessor.ts @@ -1,4 +1,4 @@ -import { DomToModelOption, ElementProcessorMap } from 'roosterjs-content-model-types'; +import type { DomToModelOption, ElementProcessorMap } from 'roosterjs-content-model-types'; /** * @internal diff --git a/packages-content-model/roosterjs-content-model-editor/lib/editor/utils/contentModelDomIndexer.ts b/packages-content-model/roosterjs-content-model-editor/lib/editor/utils/contentModelDomIndexer.ts new file mode 100644 index 00000000000..cea02e14a44 --- /dev/null +++ b/packages-content-model/roosterjs-content-model-editor/lib/editor/utils/contentModelDomIndexer.ts @@ -0,0 +1,278 @@ +import { createSelectionMarker, createText, isNodeOfType } from 'roosterjs-content-model-dom'; +import { setSelection } from '../../modelApi/selection/setSelection'; +import type { + ContentModelDocument, + ContentModelDomIndexer, + ContentModelParagraph, + ContentModelSegment, + ContentModelSelectionMarker, + ContentModelTable, + ContentModelTableRow, + ContentModelText, + DOMSelection, + Selectable, +} from 'roosterjs-content-model-types'; + +interface SegmentItem { + paragraph: ContentModelParagraph; + segments: ContentModelSegment[]; +} + +interface TableItem { + tableRows: ContentModelTableRow[]; +} + +interface IndexedSegmentNode extends Node { + __roosterjsContentModel: SegmentItem; +} + +interface IndexedTableElement extends HTMLTableElement { + __roosterjsContentModel: TableItem; +} + +function isIndexedSegment(node: Node): node is IndexedSegmentNode { + const { paragraph, segments } = (node as IndexedSegmentNode).__roosterjsContentModel ?? {}; + + return ( + paragraph && + paragraph.blockType == 'Paragraph' && + Array.isArray(paragraph.segments) && + Array.isArray(segments) + ); +} + +function onSegment( + segmentNode: Node, + paragraph: ContentModelParagraph, + segment: ContentModelSegment[] +) { + const indexedText = segmentNode as IndexedSegmentNode; + indexedText.__roosterjsContentModel = { + paragraph, + segments: segment, + }; +} + +function onParagraph(paragraphElement: HTMLElement) { + let previousText: Text | null = null; + + for (let child = paragraphElement.firstChild; child; child = child.nextSibling) { + if (isNodeOfType(child, 'TEXT_NODE')) { + if (!previousText) { + previousText = child; + } else { + const item = isIndexedSegment(previousText) + ? previousText.__roosterjsContentModel + : undefined; + + if (item && isIndexedSegment(child)) { + item.segments = item.segments.concat(child.__roosterjsContentModel.segments); + child.__roosterjsContentModel.segments = []; + } + } + } else if (isNodeOfType(child, 'ELEMENT_NODE')) { + previousText = null; + + onParagraph(child); + } else { + previousText = null; + } + } +} + +function onTable(tableElement: HTMLTableElement, table: ContentModelTable) { + const indexedTable = tableElement as IndexedTableElement; + indexedTable.__roosterjsContentModel = { tableRows: table.rows }; +} + +function reconcileSelection( + model: ContentModelDocument, + newSelection: DOMSelection, + oldSelection?: DOMSelection +): boolean { + if (oldSelection) { + if ( + oldSelection.type == 'range' && + oldSelection.range.collapsed && + isNodeOfType(oldSelection.range.startContainer, 'TEXT_NODE') + ) { + if (isIndexedSegment(oldSelection.range.startContainer)) { + reconcileTextSelection(oldSelection.range.startContainer); + } + } else { + setSelection(model); + } + } + + switch (newSelection.type) { + case 'image': + case 'table': + // For image and table selection, we just clear the cached model since during selecting the element id might be changed + return false; + + case 'range': + const newRange = newSelection.range; + if (newRange) { + const { + startContainer, + startOffset, + endContainer, + endOffset, + collapsed, + } = newRange; + + if (collapsed) { + return !!reconcileNodeSelection(startContainer, startOffset); + } else if ( + startContainer == endContainer && + isNodeOfType(startContainer, 'TEXT_NODE') + ) { + return ( + isIndexedSegment(startContainer) && + !!reconcileTextSelection(startContainer, startOffset, endOffset) + ); + } else { + const marker1 = reconcileNodeSelection(startContainer, startOffset); + const marker2 = reconcileNodeSelection(endContainer, endOffset); + + if (marker1 && marker2) { + setSelection(model, marker1, marker2); + return true; + } else { + return false; + } + } + } + + break; + } + + return false; +} + +function reconcileNodeSelection(node: Node, offset: number): Selectable | undefined { + if (isNodeOfType(node, 'TEXT_NODE')) { + return isIndexedSegment(node) ? reconcileTextSelection(node, offset) : undefined; + } else if (offset >= node.childNodes.length) { + return insertMarker(node.lastChild, true /*isAfter*/); + } else { + return insertMarker(node.childNodes[offset], false /*isAfter*/); + } +} + +function insertMarker(node: Node | null, isAfter: boolean): Selectable | undefined { + let marker: ContentModelSelectionMarker | undefined; + + if (node && isIndexedSegment(node)) { + const { paragraph, segments } = node.__roosterjsContentModel; + const index = paragraph.segments.indexOf(segments[0]); + + if (index >= 0) { + const formatSegment = + (!isAfter && paragraph.segments[index - 1]) || paragraph.segments[index]; + marker = createSelectionMarker(formatSegment.format); + + paragraph.segments.splice(isAfter ? index + 1 : index, 0, marker); + } + } + + return marker; +} + +function reconcileTextSelection( + textNode: IndexedSegmentNode, + startOffset?: number, + endOffset?: number +) { + const { paragraph, segments } = textNode.__roosterjsContentModel; + const first = segments[0]; + const last = segments[segments.length - 1]; + let selectable: Selectable | undefined; + + if (first?.segmentType == 'Text' && last?.segmentType == 'Text') { + const newSegments: ContentModelSegment[] = []; + const txt = textNode.nodeValue || ''; + const textSegments: ContentModelText[] = []; + + if (startOffset === undefined) { + first.text = txt; + newSegments.push(first); + textSegments.push(first); + } else { + if (startOffset > 0) { + first.text = txt.substring(0, startOffset); + newSegments.push(first); + textSegments.push(first); + } + + if (endOffset === undefined) { + const marker = createSelectionMarker(first.format); + newSegments.push(marker); + + selectable = marker; + endOffset = startOffset; + } else if (endOffset > startOffset) { + const middle = createText( + txt.substring(startOffset, endOffset), + first.format, + first.link, + first.code + ); + + middle.isSelected = true; + newSegments.push(middle); + textSegments.push(middle); + selectable = middle; + } + + if (endOffset < txt.length) { + const newLast = createText( + txt.substring(endOffset), + first.format, + first.link, + first.code + ); + newSegments.push(newLast); + textSegments.push(newLast); + } + } + + let firstIndex = paragraph.segments.indexOf(first); + let lastIndex = paragraph.segments.indexOf(last); + + if (firstIndex >= 0 && lastIndex >= 0) { + while ( + firstIndex > 0 && + paragraph.segments[firstIndex - 1].segmentType == 'SelectionMarker' + ) { + firstIndex--; + } + + while ( + lastIndex < paragraph.segments.length - 1 && + paragraph.segments[lastIndex + 1].segmentType == 'SelectionMarker' + ) { + lastIndex++; + } + + paragraph.segments.splice(firstIndex, lastIndex - firstIndex + 1, ...newSegments); + } + + onSegment(textNode, paragraph, textSegments); + + delete paragraph.cachedElement; + } + + return selectable; +} + +/** + * @internal + * Implementation of ContentModelDomIndexer + */ +export const contentModelDomIndexer: ContentModelDomIndexer = { + onSegment, + onParagraph, + onTable, + reconcileSelection, +}; diff --git a/packages-content-model/roosterjs-content-model-editor/lib/editor/utils/handleKeyboardEventCommon.ts b/packages-content-model/roosterjs-content-model-editor/lib/editor/utils/handleKeyboardEventCommon.ts index c13919d1aa8..af82943e0af 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/editor/utils/handleKeyboardEventCommon.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/editor/utils/handleKeyboardEventCommon.ts @@ -1,9 +1,9 @@ -import { ContentModelDocument } from 'roosterjs-content-model-types'; import { DeleteResult } from '../../modelApi/edit/utils/DeleteSelectionStep'; -import { FormatWithContentModelContext } from '../../publicTypes/parameter/FormatWithContentModelContext'; -import { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; import { normalizeContentModel } from 'roosterjs-content-model-dom'; import { PluginEventType } from 'roosterjs-editor-types'; +import type { ContentModelDocument } from 'roosterjs-content-model-types'; +import type { FormatWithContentModelContext } from '../../publicTypes/parameter/FormatWithContentModelContext'; +import type { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; /** * @internal diff --git a/packages-content-model/roosterjs-content-model-editor/lib/index.ts b/packages-content-model/roosterjs-content-model-editor/lib/index.ts index 66ccdd8cdea..aa6e4c0963c 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/index.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/index.ts @@ -7,6 +7,8 @@ export { ContentModelEditorCore, CreateContentModel, SetContentModel, + GetDOMSelection, + SetDOMSelection, } from './publicTypes/ContentModelEditorCore'; export { default as ContentModelBeforePasteEvent, @@ -85,9 +87,10 @@ export { default as keyboardDelete } from './publicApi/editing/keyboardDelete'; export { default as ContentModelEditor } from './editor/ContentModelEditor'; export { default as isContentModelEditor } from './editor/isContentModelEditor'; -export { default as ContentModelFormatPlugin } from './editor/plugins/ContentModelFormatPlugin'; -export { default as ContentModelEditPlugin } from './editor/plugins/ContentModelEditPlugin'; export { default as ContentModelPastePlugin } from './editor/plugins/PastePlugin/ContentModelPastePlugin'; + +export { default as ContentModelFormatPlugin } from './editor/corePlugins/ContentModelFormatPlugin'; +export { default as ContentModelEditPlugin } from './editor/corePlugins/ContentModelEditPlugin'; export { default as ContentModelTypeInContainerPlugin } from './editor/corePlugins/ContentModelTypeInContainerPlugin'; export { default as ContentModelCopyPastePlugin } from './editor/corePlugins/ContentModelCopyPastePlugin'; export { default as ContentModelCachePlugin } from './editor/corePlugins/ContentModelCachePlugin'; @@ -103,3 +106,4 @@ export { updateTableMetadata } from './domUtils/metadata/updateTableMetadata'; export { ContentModelCachePluginState } from './publicTypes/pluginState/ContentModelCachePluginState'; export { ContentModelPluginState } from './publicTypes/pluginState/ContentModelPluginState'; +export { ContentModelFormatPluginState } from './publicTypes/pluginState/ContentModelFormatPluginState'; diff --git a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/block/getLeafSiblingBlock.ts b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/block/getLeafSiblingBlock.ts index 72e3d6e6794..0ef87faa73c 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/block/getLeafSiblingBlock.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/block/getLeafSiblingBlock.ts @@ -1,5 +1,5 @@ import { isGeneralSegment } from 'roosterjs-content-model-dom'; -import { +import type { ContentModelBlock, ContentModelBlockGroup, ContentModelParagraph, diff --git a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/block/setModelAlignment.ts b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/block/setModelAlignment.ts index 6dc1be2a72c..c90535cc35b 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/block/setModelAlignment.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/block/setModelAlignment.ts @@ -1,7 +1,7 @@ import { alignTable } from '../table/alignTable'; -import { ContentModelDocument, ContentModelListItem } from 'roosterjs-content-model-types'; import { getOperationalBlocks } from '../selection/collectSelections'; import { TableOperation } from 'roosterjs-editor-types'; +import type { ContentModelDocument, ContentModelListItem } from 'roosterjs-content-model-types'; const ResultMap: Record< 'left' | 'center' | 'right', diff --git a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/block/setModelDirection.ts b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/block/setModelDirection.ts index b99a52de76b..f8d6f9497a2 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/block/setModelDirection.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/block/setModelDirection.ts @@ -1,7 +1,7 @@ import { findListItemsInSameThread } from '../list/findListItemsInSameThread'; import { getOperationalBlocks } from '../selection/collectSelections'; import { isBlockGroupOfType } from '../common/isBlockGroupOfType'; -import { +import type { ContentModelBlockFormat, ContentModelDocument, ContentModelListItem, diff --git a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/block/setModelIndentation.ts b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/block/setModelIndentation.ts index feda9dd7917..4f5b1776a2b 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/block/setModelIndentation.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/block/setModelIndentation.ts @@ -1,7 +1,7 @@ import { createListLevel, parseValueWithUnit } from 'roosterjs-content-model-dom'; import { getOperationalBlocks } from '../selection/collectSelections'; import { isBlockGroupOfType } from '../common/isBlockGroupOfType'; -import { +import type { ContentModelDocument, ContentModelListItem, ContentModelListLevel, diff --git a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/block/toggleModelBlockQuote.ts b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/block/toggleModelBlockQuote.ts index 5fc117fde25..5bef7411040 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/block/toggleModelBlockQuote.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/block/toggleModelBlockQuote.ts @@ -1,8 +1,10 @@ import { areSameFormats, createFormatContainer, unwrapBlock } from 'roosterjs-content-model-dom'; -import { getOperationalBlocks, OperationalBlocks } from '../selection/collectSelections'; +import { getOperationalBlocks } from '../selection/collectSelections'; import { isBlockGroupOfType } from '../common/isBlockGroupOfType'; -import { wrapBlockStep1, WrapBlockStep1Result, wrapBlockStep2 } from '../common/wrapBlock'; -import { +import { wrapBlockStep1, wrapBlockStep2 } from '../common/wrapBlock'; +import type { OperationalBlocks } from '../selection/collectSelections'; +import type { WrapBlockStep1Result } from '../common/wrapBlock'; +import type { ContentModelBlock, ContentModelBlockGroup, ContentModelDocument, diff --git a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/common/clearModelFormat.ts b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/common/clearModelFormat.ts index e3031cb0b25..7228bf4ccf4 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/common/clearModelFormat.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/common/clearModelFormat.ts @@ -1,13 +1,12 @@ import { adjustWordSelection } from '../selection/adjustWordSelection'; import { applyTableFormat } from '../table/applyTableFormat'; -import { arrayPush } from 'roosterjs-editor-dom'; import { createFormatContainer } from 'roosterjs-content-model-dom'; import { getClosestAncestorBlockGroupIndex } from './getClosestAncestorBlockGroupIndex'; import { iterateSelections } from '../selection/iterateSelections'; -import { TableSelectionContext } from '../../publicTypes/selection/TableSelectionContext'; import { updateTableCellMetadata } from '../../domUtils/metadata/updateTableCellMetadata'; import { updateTableMetadata } from '../../domUtils/metadata/updateTableMetadata'; -import { +import type { TableSelectionContext } from '../../publicTypes/selection/TableSelectionContext'; +import type { ContentModelBlock, ContentModelBlockGroup, ContentModelDocument, @@ -32,7 +31,7 @@ export function clearModelFormat( [model], (path, tableContext, block, segments) => { if (segments) { - arrayPush(segmentsToClear, segments); + segmentsToClear.push(...segments); } if (block) { diff --git a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/common/cloneModel.ts b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/common/cloneModel.ts index 01eee7d6922..13238e7b996 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/common/cloneModel.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/common/cloneModel.ts @@ -173,14 +173,12 @@ function cloneSegmentBase( } function cloneEntity(entity: ContentModelEntity, options: CloneModelOptions): ContentModelEntity { - const { wrapper, isReadonly, type, id } = entity; + const { wrapper, entityFormat } = entity; return Object.assign( { wrapper: handleCachedElement(wrapper, 'entity', options), - isReadonly, - type, - id, + entityFormat: { ...entityFormat }, }, cloneBlockBase(entity), cloneSegmentBase(entity) diff --git a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/common/getClosestAncestorBlockGroupIndex.ts b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/common/getClosestAncestorBlockGroupIndex.ts index 42c1fecdf7e..2180768f52d 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/common/getClosestAncestorBlockGroupIndex.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/common/getClosestAncestorBlockGroupIndex.ts @@ -1,4 +1,4 @@ -import { +import type { ContentModelBlockGroup, ContentModelBlockGroupBase, ContentModelBlockGroupType, diff --git a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/common/isBlockGroupOfType.ts b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/common/isBlockGroupOfType.ts index cd2a87a3665..da24115d6dc 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/common/isBlockGroupOfType.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/common/isBlockGroupOfType.ts @@ -1,5 +1,5 @@ -import { ContentModelBlock, ContentModelBlockGroup } from 'roosterjs-content-model-types'; -import { TypeOfBlockGroup } from './getClosestAncestorBlockGroupIndex'; +import type { ContentModelBlock, ContentModelBlockGroup } from 'roosterjs-content-model-types'; +import type { TypeOfBlockGroup } from './getClosestAncestorBlockGroupIndex'; /** * @internal diff --git a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/common/mergeModel.ts b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/common/mergeModel.ts index d68454351e8..1abe26a9994 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/common/mergeModel.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/common/mergeModel.ts @@ -1,19 +1,19 @@ -import { addSegment } from 'roosterjs-content-model-dom'; import { applyTableFormat } from '../table/applyTableFormat'; import { deleteSelection } from '../edit/deleteSelection'; -import { FormatWithContentModelContext } from '../../publicTypes/parameter/FormatWithContentModelContext'; import { getClosestAncestorBlockGroupIndex } from './getClosestAncestorBlockGroupIndex'; -import { getObjectKeys } from 'roosterjs-editor-dom'; -import { InsertPoint } from '../../publicTypes/selection/InsertPoint'; import { normalizeTable } from '../table/normalizeTable'; import { + addSegment, createListItem, createParagraph, createSelectionMarker, createTableCell, + getObjectKeys, normalizeContentModel, } from 'roosterjs-content-model-dom'; -import { +import type { FormatWithContentModelContext } from '../../publicTypes/parameter/FormatWithContentModelContext'; +import type { InsertPoint } from '../../publicTypes/selection/InsertPoint'; +import type { ContentModelBlock, ContentModelBlockFormat, ContentModelBlockGroup, @@ -146,8 +146,14 @@ function mergeParagraph( newParagraph.segments.splice(segmentIndex + i, 0, segment); - if (context && segment.segmentType == 'Entity') { - context.newEntities.push(segment); + if (context) { + if (segment.segmentType == 'Entity') { + context.newEntities.push(segment); + } + + if (segment.segmentType == 'Image') { + context.newImages.push(segment); + } } } } diff --git a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/common/retrieveModelFormatState.ts b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/common/retrieveModelFormatState.ts index 859fb89f1ba..54eeeb83e1c 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/common/retrieveModelFormatState.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/common/retrieveModelFormatState.ts @@ -1,11 +1,11 @@ -import { ContentModelFormatState } from '../../publicTypes/format/formatState/ContentModelFormatState'; import { extractBorderValues } from '../../domUtils/borderValues'; import { getClosestAncestorBlockGroupIndex } from './getClosestAncestorBlockGroupIndex'; import { isBold } from '../../publicApi/segment/toggleBold'; import { iterateSelections } from '../selection/iterateSelections'; -import { TableSelectionContext } from '../../publicTypes/selection/TableSelectionContext'; import { updateTableMetadata } from '../../domUtils/metadata/updateTableMetadata'; -import { +import type { ContentModelFormatState } from '../../publicTypes/format/formatState/ContentModelFormatState'; +import type { TableSelectionContext } from '../../publicTypes/selection/TableSelectionContext'; +import type { ContentModelBlock, ContentModelBlockGroup, ContentModelDocument, diff --git a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/common/wrapBlock.ts b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/common/wrapBlock.ts index efc91fc92f8..6acaa412e3f 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/common/wrapBlock.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/common/wrapBlock.ts @@ -1,6 +1,5 @@ import { addBlock, setParagraphNotImplicit } from 'roosterjs-content-model-dom'; -import { arrayPush } from 'roosterjs-editor-dom'; -import { ContentModelBlock, ContentModelBlockGroup } from 'roosterjs-content-model-types'; +import type { ContentModelBlock, ContentModelBlockGroup } from 'roosterjs-content-model-types'; /** * @internal @@ -49,7 +48,7 @@ export function wrapBlockStep2= 0 && canMerge(nextBlock, wrapper)) { wrapper.blocks.forEach(setParagraphNotImplicit); - arrayPush(wrapper.blocks, nextBlock.blocks); + wrapper.blocks.push(...nextBlock.blocks); parent.blocks.splice(index + 1, 1); } }); diff --git a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/edit/deleteSelection.ts b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/edit/deleteSelection.ts index 26d4b9a9236..7fa31e40c52 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/edit/deleteSelection.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/edit/deleteSelection.ts @@ -1,8 +1,8 @@ -import { ContentModelDocument } from 'roosterjs-content-model-types'; import { deleteExpandedSelection } from './utils/deleteExpandedSelection'; -import { FormatWithContentModelContext } from '../../publicTypes/parameter/FormatWithContentModelContext'; -import { - DeleteResult, +import { DeleteResult } from './utils/DeleteSelectionStep'; +import type { ContentModelDocument } from 'roosterjs-content-model-types'; +import type { FormatWithContentModelContext } from '../../publicTypes/parameter/FormatWithContentModelContext'; +import type { DeleteSelectionContext, DeleteSelectionResult, DeleteSelectionStep, diff --git a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/edit/deleteSteps/deleteAllSegmentBefore.ts b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/edit/deleteSteps/deleteAllSegmentBefore.ts index ecc9a18fac8..3a2d1624a07 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/edit/deleteSteps/deleteAllSegmentBefore.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/edit/deleteSteps/deleteAllSegmentBefore.ts @@ -1,5 +1,6 @@ -import { DeleteResult, DeleteSelectionStep } from '../utils/DeleteSelectionStep'; +import { DeleteResult } from '../utils/DeleteSelectionStep'; import { deleteSegment } from '../utils/deleteSegment'; +import type { DeleteSelectionStep } from '../utils/DeleteSelectionStep'; /** * @internal diff --git a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/edit/deleteSteps/deleteCollapsedSelection.ts b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/edit/deleteSteps/deleteCollapsedSelection.ts index 3512044aeab..48fa94cf146 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/edit/deleteSteps/deleteCollapsedSelection.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/edit/deleteSteps/deleteCollapsedSelection.ts @@ -1,10 +1,12 @@ -import { BlockAndPath, getLeafSiblingBlock } from '../../block/getLeafSiblingBlock'; -import { ContentModelSegment } from 'roosterjs-content-model-types'; import { createInsertPoint } from '../utils/createInsertPoint'; import { deleteBlock } from '../utils/deleteBlock'; -import { DeleteResult, DeleteSelectionStep } from '../utils/DeleteSelectionStep'; +import { DeleteResult } from '../utils/DeleteSelectionStep'; import { deleteSegment } from '../utils/deleteSegment'; +import { getLeafSiblingBlock } from '../../block/getLeafSiblingBlock'; import { setParagraphNotImplicit } from 'roosterjs-content-model-dom'; +import type { BlockAndPath } from '../../block/getLeafSiblingBlock'; +import type { ContentModelSegment } from 'roosterjs-content-model-types'; +import type { DeleteSelectionStep } from '../utils/DeleteSelectionStep'; function getDeleteCollapsedSelection(direction: 'forward' | 'backward'): DeleteSelectionStep { return context => { diff --git a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/edit/deleteSteps/deleteWordSelection.ts b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/edit/deleteSteps/deleteWordSelection.ts index df61ea0c88d..50b5b37d665 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/edit/deleteSteps/deleteWordSelection.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/edit/deleteSteps/deleteWordSelection.ts @@ -1,11 +1,8 @@ -import { ContentModelParagraph } from 'roosterjs-content-model-types'; +import { DeleteResult } from '../utils/DeleteSelectionStep'; import { isPunctuation, isSpace, normalizeText } from '../../../domUtils/stringUtil'; import { isWhiteSpacePreserved } from 'roosterjs-content-model-dom'; -import { - DeleteResult, - DeleteSelectionContext, - DeleteSelectionStep, -} from '../utils/DeleteSelectionStep'; +import type { ContentModelParagraph } from 'roosterjs-content-model-types'; +import type { DeleteSelectionContext, DeleteSelectionStep } from '../utils/DeleteSelectionStep'; const enum DeleteWordState { Start, diff --git a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/edit/utils/DeleteSelectionStep.ts b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/edit/utils/DeleteSelectionStep.ts index 64bd1300ab3..6422aca74c0 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/edit/utils/DeleteSelectionStep.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/edit/utils/DeleteSelectionStep.ts @@ -1,7 +1,7 @@ -import { ContentModelParagraph } from 'roosterjs-content-model-types'; -import { FormatWithContentModelContext } from '../../../publicTypes/parameter/FormatWithContentModelContext'; -import { InsertPoint } from '../../../publicTypes/selection/InsertPoint'; -import { TableSelectionContext } from '../../../publicTypes/selection/TableSelectionContext'; +import type { ContentModelParagraph } from 'roosterjs-content-model-types'; +import type { FormatWithContentModelContext } from '../../../publicTypes/parameter/FormatWithContentModelContext'; +import type { InsertPoint } from '../../../publicTypes/selection/InsertPoint'; +import type { TableSelectionContext } from '../../../publicTypes/selection/TableSelectionContext'; /** * @internal diff --git a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/edit/utils/createInsertPoint.ts b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/edit/utils/createInsertPoint.ts index 670a6ec8346..c89e236d120 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/edit/utils/createInsertPoint.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/edit/utils/createInsertPoint.ts @@ -1,6 +1,6 @@ -import { InsertPoint } from '../../../publicTypes/selection/InsertPoint'; -import { TableSelectionContext } from '../../../publicTypes/selection/TableSelectionContext'; -import { +import type { InsertPoint } from '../../../publicTypes/selection/InsertPoint'; +import type { TableSelectionContext } from '../../../publicTypes/selection/TableSelectionContext'; +import type { ContentModelBlockGroup, ContentModelParagraph, ContentModelSelectionMarker, diff --git a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/edit/utils/deleteBlock.ts b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/edit/utils/deleteBlock.ts index fdc5f6ce501..fb5665efb6d 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/edit/utils/deleteBlock.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/edit/utils/deleteBlock.ts @@ -1,6 +1,6 @@ -import { ContentModelBlock } from 'roosterjs-content-model-types'; import { EntityOperation } from 'roosterjs-editor-types'; -import { FormatWithContentModelContext } from '../../../publicTypes/parameter/FormatWithContentModelContext'; +import type { ContentModelBlock } from 'roosterjs-content-model-types'; +import type { FormatWithContentModelContext } from '../../../publicTypes/parameter/FormatWithContentModelContext'; /** * @internal diff --git a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/edit/utils/deleteExpandedSelection.ts b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/edit/utils/deleteExpandedSelection.ts index 9b4998b6aba..bf7bcf69abd 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/edit/utils/deleteExpandedSelection.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/edit/utils/deleteExpandedSelection.ts @@ -1,10 +1,12 @@ -import { ContentModelDocument } from 'roosterjs-content-model-types'; import { createInsertPoint } from '../utils/createInsertPoint'; import { deleteBlock } from '../utils/deleteBlock'; -import { DeleteResult, DeleteSelectionContext } from '../utils/DeleteSelectionStep'; +import { DeleteResult } from '../utils/DeleteSelectionStep'; import { deleteSegment } from '../utils/deleteSegment'; -import { FormatWithContentModelContext } from '../../../publicTypes/parameter/FormatWithContentModelContext'; -import { iterateSelections, IterateSelectionsOption } from '../../selection/iterateSelections'; +import { iterateSelections } from '../../selection/iterateSelections'; +import type { ContentModelDocument } from 'roosterjs-content-model-types'; +import type { DeleteSelectionContext } from '../utils/DeleteSelectionStep'; +import type { FormatWithContentModelContext } from '../../../publicTypes/parameter/FormatWithContentModelContext'; +import type { IterateSelectionsOption } from '../../selection/iterateSelections'; import { createBr, createParagraph, diff --git a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/edit/utils/deleteSegment.ts b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/edit/utils/deleteSegment.ts index 35f33db56ba..05d390e33e5 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/edit/utils/deleteSegment.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/edit/utils/deleteSegment.ts @@ -1,9 +1,9 @@ -import { ContentModelParagraph, ContentModelSegment } from 'roosterjs-content-model-types'; import { deleteSingleChar } from './deleteSingleChar'; import { EntityOperation } from 'roosterjs-editor-types'; -import { FormatWithContentModelContext } from '../../../publicTypes/parameter/FormatWithContentModelContext'; import { isWhiteSpacePreserved, normalizeSingleSegment } from 'roosterjs-content-model-dom'; import { normalizeText } from '../../../domUtils/stringUtil'; +import type { ContentModelParagraph, ContentModelSegment } from 'roosterjs-content-model-types'; +import type { FormatWithContentModelContext } from '../../../publicTypes/parameter/FormatWithContentModelContext'; /** * @internal diff --git a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/entity/insertEntityModel.ts b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/entity/insertEntityModel.ts index fe5b5a6f0ec..d6874fe26b0 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/entity/insertEntityModel.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/entity/insertEntityModel.ts @@ -1,16 +1,17 @@ +import { DeleteResult } from '../edit/utils/DeleteSelectionStep'; +import { deleteSelection } from '../edit/deleteSelection'; +import { getClosestAncestorBlockGroupIndex } from '../common/getClosestAncestorBlockGroupIndex'; +import { setSelection } from '../selection/setSelection'; import { createBr, createParagraph, createSelectionMarker, normalizeContentModel, } from 'roosterjs-content-model-dom'; -import { DeleteResult, DeleteSelectionResult } from '../edit/utils/DeleteSelectionStep'; -import { deleteSelection } from '../edit/deleteSelection'; -import { FormatWithContentModelContext } from '../../publicTypes/parameter/FormatWithContentModelContext'; -import { getClosestAncestorBlockGroupIndex } from '../common/getClosestAncestorBlockGroupIndex'; -import { InsertEntityPosition } from '../../publicTypes/parameter/InsertEntityOptions'; -import { setSelection } from '../selection/setSelection'; -import { +import type { DeleteSelectionResult } from '../edit/utils/DeleteSelectionStep'; +import type { FormatWithContentModelContext } from '../../publicTypes/parameter/FormatWithContentModelContext'; +import type { InsertEntityPosition } from '../../publicTypes/parameter/InsertEntityOptions'; +import type { ContentModelBlock, ContentModelBlockGroup, ContentModelDocument, diff --git a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/format/pendingFormat.ts b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/format/pendingFormat.ts index 1dfbdeb69d2..38f9a75dc09 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/format/pendingFormat.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/format/pendingFormat.ts @@ -1,6 +1,5 @@ -import { ContentModelSegmentFormat } from 'roosterjs-content-model-types'; -import { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; -import { NodePosition } from 'roosterjs-editor-types'; +import type { ContentModelSegmentFormat } from 'roosterjs-content-model-types'; +import type { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; /** * @internal @@ -16,17 +15,20 @@ export function getPendingFormat(editor: IContentModelEditor): ContentModelSegme * Set pending segment format to editor * @param editor The editor to set pending format to * @param format The format to set. - * @param position Cursor position when set this format + * @param posContainer Container node of current focus position + * @param posOffset Offset number of current focus position */ export function setPendingFormat( editor: IContentModelEditor, format: ContentModelSegmentFormat, - position: NodePosition + posContainer: Node, + posOffset: number ) { const holder = getPendingFormatHolder(editor); holder.format = format; - holder.position = position; + holder.posContainer = posContainer; + holder.posOffset = posOffset; } /** @@ -37,7 +39,8 @@ export function clearPendingFormat(editor: IContentModelEditor) { const holder = getPendingFormatHolder(editor); holder.format = null; - holder.position = null; + holder.posContainer = null; + holder.posOffset = null; } /** @@ -49,10 +52,10 @@ export function canApplyPendingFormat(editor: IContentModelEditor): boolean { const holder = getPendingFormatHolder(editor); let result = false; - if (holder.format && holder.position) { + if (holder.format && holder.posContainer && holder.posOffset !== null) { const position = editor.getFocusedPosition(); - if (position?.equalTo(holder.position)) { + if (position?.node == holder.posContainer && position?.offset == holder.posOffset) { result = true; } } @@ -61,7 +64,8 @@ export function canApplyPendingFormat(editor: IContentModelEditor): boolean { } interface PendingFormatHolder { format: ContentModelSegmentFormat | null; - position: NodePosition | null; + posContainer: Node | null; + posOffset: number | null; } const PendingFormatHolderKey = '__ContentModelPendingFormat'; @@ -69,6 +73,7 @@ const PendingFormatHolderKey = '__ContentModelPendingFormat'; function getPendingFormatHolder(editor: IContentModelEditor): PendingFormatHolder { return editor.getCustomData(PendingFormatHolderKey, () => ({ format: null, - position: null, + posContainer: null, + posOffset: null, })); } diff --git a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/image/applyImageBorderFormat.ts b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/image/applyImageBorderFormat.ts index abc9e266158..d2d8aa8abb5 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/image/applyImageBorderFormat.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/image/applyImageBorderFormat.ts @@ -1,7 +1,7 @@ -import { Border } from '../../publicTypes/interface/Border'; -import { ContentModelImage } from 'roosterjs-content-model-types'; import { extractBorderValues } from '../../domUtils/borderValues'; import { parseValueWithUnit } from 'roosterjs-content-model-dom'; +import type { Border } from '../../publicTypes/interface/Border'; +import type { ContentModelImage } from 'roosterjs-content-model-types'; /** * @internal diff --git a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/list/findListItemsInSameThread.ts b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/list/findListItemsInSameThread.ts index c32ad0704f5..4a59423cd94 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/list/findListItemsInSameThread.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/list/findListItemsInSameThread.ts @@ -1,4 +1,4 @@ -import { +import type { ContentModelBlockGroup, ContentModelDocument, ContentModelListItem, diff --git a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/list/setListType.ts b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/list/setListType.ts index 354580e834e..f29470f2ec3 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/list/setListType.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/list/setListType.ts @@ -6,7 +6,7 @@ import { normalizeContentModel, setParagraphNotImplicit, } from 'roosterjs-content-model-dom'; -import { +import type { ContentModelBlock, ContentModelDocument, ContentModelListItem, @@ -96,8 +96,14 @@ export function setListType(model: ContentModelDocument, listType: 'OL' | 'UL') } function shouldIgnoreBlock(block: ContentModelBlock) { - return ( - block.blockType != 'Paragraph' || - block.segments.every(x => x.segmentType == 'Br' || x.segmentType == 'SelectionMarker') - ); + switch (block.blockType) { + case 'Table': + return false; + case 'Paragraph': + return block.segments.every( + x => x.segmentType == 'Br' || x.segmentType == 'SelectionMarker' + ); + default: + return true; + } } diff --git a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/selection/adjustSegmentSelection.ts b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/selection/adjustSegmentSelection.ts index c1786b3767f..1af7eb1ccbc 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/selection/adjustSegmentSelection.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/selection/adjustSegmentSelection.ts @@ -1,6 +1,6 @@ -import { ContentModelDocument, ContentModelSegment } from 'roosterjs-content-model-types'; import { getSelectedParagraphs } from './collectSelections'; import { setSelection } from './setSelection'; +import type { ContentModelDocument, ContentModelSegment } from 'roosterjs-content-model-types'; /** * @internal diff --git a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/selection/adjustWordSelection.ts b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/selection/adjustWordSelection.ts index 4db23ddadf2..0a2035f99b8 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/selection/adjustWordSelection.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/selection/adjustWordSelection.ts @@ -1,7 +1,7 @@ import { createText } from 'roosterjs-content-model-dom'; import { isPunctuation, isSpace } from '../../domUtils/stringUtil'; import { iterateSelections } from '../../modelApi/selection/iterateSelections'; -import { +import type { ContentModelDocument, ContentModelParagraph, ContentModelSegment, diff --git a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/selection/areSameRangeEx.ts b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/selection/areSameRangeEx.ts new file mode 100644 index 00000000000..7cdd8dca1ef --- /dev/null +++ b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/selection/areSameRangeEx.ts @@ -0,0 +1,41 @@ +import type { DOMSelection } from 'roosterjs-content-model-types'; + +/** + * @internal + * Check if the given selections are the same + */ +export function areSameRangeEx(sel1: DOMSelection, sel2: DOMSelection): boolean { + if (sel1 == sel2) { + return true; + } + + switch (sel1.type) { + case 'image': + return sel2.type == 'image' && sel2.image == sel1.image; + + case 'table': + return ( + sel2.type == 'table' && + sel2.table == sel1.table && + sel2.firstColumn == sel1.firstColumn && + sel2.lastColumn == sel1.lastColumn && + sel2.firstRow == sel1.firstRow && + sel2.lastRow == sel1.lastRow + ); + + case 'range': + default: + return sel2.type == 'range' && areSameRanges(sel2.range, sel1.range); + } +} + +function areSameRanges(r1?: Range, r2?: Range): boolean { + return !!( + r1 && + r2 && + r1.startContainer == r2.startContainer && + r1.startOffset == r2.startOffset && + r1.endContainer == r2.endContainer && + r1.endOffset == r2.endOffset + ); +} diff --git a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/selection/collapseTableSelection.ts b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/selection/collapseTableSelection.ts index 22f05473455..5fde8bd014d 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/selection/collapseTableSelection.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/selection/collapseTableSelection.ts @@ -1,6 +1,6 @@ import { addSegment, createSelectionMarker } from 'roosterjs-content-model-dom'; -import { ContentModelTableRow } from 'roosterjs-content-model-types'; -import { TableSelectionCoordinates } from '../table/getSelectedCells'; +import type { ContentModelTableRow } from 'roosterjs-content-model-types'; +import type { TableSelectionCoordinates } from '../table/getSelectedCells'; /** * @internal diff --git a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/selection/collectSelections.ts b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/selection/collectSelections.ts index ef737bdab62..1089c46eeb4 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/selection/collectSelections.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/selection/collectSelections.ts @@ -1,7 +1,9 @@ +import { getClosestAncestorBlockGroupIndex } from '../common/getClosestAncestorBlockGroupIndex'; import { isBlockGroupOfType } from '../common/isBlockGroupOfType'; -import { iterateSelections, IterateSelectionsOption } from './iterateSelections'; -import { TableSelectionContext } from '../../publicTypes/selection/TableSelectionContext'; -import { +import { iterateSelections } from './iterateSelections'; +import type { IterateSelectionsOption } from './iterateSelections'; +import type { TableSelectionContext } from '../../publicTypes/selection/TableSelectionContext'; +import type { ContentModelBlock, ContentModelBlockGroup, ContentModelBlockGroupType, @@ -11,10 +13,7 @@ import { ContentModelSegment, ContentModelTable, } from 'roosterjs-content-model-types'; -import { - getClosestAncestorBlockGroupIndex, - TypeOfBlockGroup, -} from '../common/getClosestAncestorBlockGroupIndex'; +import type { TypeOfBlockGroup } from '../common/getClosestAncestorBlockGroupIndex'; /** * @internal @@ -39,7 +38,7 @@ export function getSelectedSegmentsAndParagraphs( selections.forEach(({ segments, block }) => { if (segments && ((includingFormatHolder && !block) || block?.blockType == 'Paragraph')) { segments.forEach(segment => { - if (segment.segmentType != 'Entity' || !segment.isReadonly) { + if (segment.segmentType != 'Entity' || !segment.entityFormat.isReadonly) { result.push([segment, block?.blockType == 'Paragraph' ? block : null]); } }); diff --git a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/selection/getSelectionRootNode.ts b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/selection/getSelectionRootNode.ts index b99b34a0108..bc7115d0006 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/selection/getSelectionRootNode.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/selection/getSelectionRootNode.ts @@ -1,16 +1,16 @@ -import { SelectionRangeEx, SelectionRangeTypes } from 'roosterjs-editor-types'; +import type { DOMSelection } from 'roosterjs-content-model-types'; /** * @internal */ -export function getSelectionRootNode(rangeEx: SelectionRangeEx | undefined): Node | undefined { - return !rangeEx +export function getSelectionRootNode(selection: DOMSelection | undefined): Node | undefined { + return !selection ? undefined - : rangeEx.type == SelectionRangeTypes.Normal - ? rangeEx.ranges[0]?.commonAncestorContainer - : rangeEx.type == SelectionRangeTypes.TableSelection - ? rangeEx.table - : rangeEx.type == SelectionRangeTypes.ImageSelection - ? rangeEx.image + : selection.type == 'range' + ? selection.range.commonAncestorContainer + : selection.type == 'table' + ? selection.table + : selection.type == 'image' + ? selection.image : undefined; } diff --git a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/selection/iterateSelections.ts b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/selection/iterateSelections.ts index b6b63b6b237..454832349e4 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/selection/iterateSelections.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/selection/iterateSelections.ts @@ -1,5 +1,5 @@ -import { TableSelectionContext } from '../../publicTypes/selection/TableSelectionContext'; -import { +import type { TableSelectionContext } from '../../publicTypes/selection/TableSelectionContext'; +import type { ContentModelBlock, ContentModelBlockGroup, ContentModelBlockWithCache, 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 08a61d52246..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,6 +1,5 @@ -import { Coordinates } from 'roosterjs-editor-types'; import { isGeneralSegment } from 'roosterjs-content-model-dom'; -import { +import type { ContentModelBlock, ContentModelBlockGroup, ContentModelSegment, @@ -102,16 +101,27 @@ function setSelectionToTable( start: Selectable | null, end: Selectable | null ): boolean { - const startCo = findCell(table, start); - const endCo = end ? findCell(table, end) : startCo; + const first = findCell(table, start); + const last = end ? findCell(table, end) : first; - if (!isInSelection && startCo && endCo) { + if (!isInSelection) { for (let row = 0; row < table.rows.length; row++) { - for (let col = 0; col < table.rows[row].cells.length; col++) { + const currentRow = table.rows[row]; + for (let col = 0; col < currentRow.cells.length; col++) { + const currentCell = table.rows[row].cells[col]; const isSelected = - row >= startCo.y && row <= endCo.y && col >= startCo.x && col <= endCo.x; + row >= first.row && row <= last.row && col >= first.col && col <= last.col; - setIsSelected(table.rows[row].cells[col], isSelected); + setIsSelected(currentCell, isSelected); + + if (!isSelected) { + setSelectionToBlockGroup( + currentCell, + false /*isInSelection*/, + null /*start*/, + null /*end*/ + ); + } } } } else { @@ -125,22 +135,13 @@ function setSelectionToTable( return isInSelection; } -function findCell(table: ContentModelTable, cell: Selectable | null): Coordinates | undefined { - let x = -1; - let y = -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; - } - } - } - } +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; - return x >= 0 && y >= 0 ? { x, y } : undefined; + return { row, col }; } function setSelectionToSegment( diff --git a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/table/alignTable.ts b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/table/alignTable.ts index f5008867674..22341edecca 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/table/alignTable.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/table/alignTable.ts @@ -1,5 +1,5 @@ -import { ContentModelTable } from 'roosterjs-content-model-types'; import { TableOperation } from 'roosterjs-editor-types'; +import type { ContentModelTable } from 'roosterjs-content-model-types'; import type { CompatibleTableOperation } from 'roosterjs-editor-types/lib/compatibleTypes'; /** diff --git a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/table/alignTableCell.ts b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/table/alignTableCell.ts index 476bad16d32..214ebc15efd 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/table/alignTableCell.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/table/alignTableCell.ts @@ -1,7 +1,7 @@ -import { ContentModelTable } from 'roosterjs-content-model-types'; import { getSelectedCells } from './getSelectedCells'; import { TableOperation } from 'roosterjs-editor-types'; import { updateTableCellMetadata } from '../../domUtils/metadata/updateTableCellMetadata'; +import type { ContentModelTable } from 'roosterjs-content-model-types'; import type { CompatibleTableOperation } from 'roosterjs-editor-types/lib/compatibleTypes'; const TextAlignValueMap: Partial> = { diff --git a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/table/applyTableFormat.ts b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/table/applyTableFormat.ts index d180db17ea1..7bdb950f591 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/table/applyTableFormat.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/table/applyTableFormat.ts @@ -4,7 +4,7 @@ import { setTableCellBackgroundColor } from './setTableCellBackgroundColor'; import { TableBorderFormat } from 'roosterjs-editor-types'; import { updateTableCellMetadata } from '../../domUtils/metadata/updateTableCellMetadata'; import { updateTableMetadata } from '../../domUtils/metadata/updateTableMetadata'; -import { +import type { BorderFormat, ContentModelTable, ContentModelTableRow, diff --git a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/table/canMergeCells.ts b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/table/canMergeCells.ts index 631c89922a6..622ad9c8324 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/table/canMergeCells.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/table/canMergeCells.ts @@ -1,4 +1,4 @@ -import { ContentModelTableRow } from 'roosterjs-content-model-types'; +import type { ContentModelTableRow } from 'roosterjs-content-model-types'; /** * @internal diff --git a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/table/createTableStructure.ts b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/table/createTableStructure.ts index 0fbebed4754..2901c158a3c 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/table/createTableStructure.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/table/createTableStructure.ts @@ -1,5 +1,5 @@ import { addBlock, createTable, createTableCell } from 'roosterjs-content-model-dom'; -import { ContentModelBlockGroup, ContentModelTable } from 'roosterjs-content-model-types'; +import type { ContentModelBlockGroup, ContentModelTable } from 'roosterjs-content-model-types'; /** * @internal diff --git a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/table/deleteTable.ts b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/table/deleteTable.ts index bcf384ba607..bf930321835 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/table/deleteTable.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/table/deleteTable.ts @@ -1,4 +1,4 @@ -import { ContentModelTable } from 'roosterjs-content-model-types'; +import type { ContentModelTable } from 'roosterjs-content-model-types'; /** * @internal diff --git a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/table/deleteTableColumn.ts b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/table/deleteTableColumn.ts index af2fe04ff09..6072314ef28 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/table/deleteTableColumn.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/table/deleteTableColumn.ts @@ -1,6 +1,6 @@ import { collapseTableSelection } from '../selection/collapseTableSelection'; -import { ContentModelTable } from 'roosterjs-content-model-types'; import { getSelectedCells } from './getSelectedCells'; +import type { ContentModelTable } from 'roosterjs-content-model-types'; /** * @internal diff --git a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/table/deleteTableRow.ts b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/table/deleteTableRow.ts index 890f6dde8f2..aba304f535a 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/table/deleteTableRow.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/table/deleteTableRow.ts @@ -1,6 +1,6 @@ import { collapseTableSelection } from '../selection/collapseTableSelection'; -import { ContentModelTable } from 'roosterjs-content-model-types'; import { getSelectedCells } from './getSelectedCells'; +import type { ContentModelTable } from 'roosterjs-content-model-types'; /** * @internal diff --git a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/table/ensureFocusableParagraphForTable.ts b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/table/ensureFocusableParagraphForTable.ts index 81e72372f23..b51358e86d2 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/table/ensureFocusableParagraphForTable.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/table/ensureFocusableParagraphForTable.ts @@ -1,5 +1,5 @@ import { createBr, createParagraph } from 'roosterjs-content-model-dom'; -import { +import type { ContentModelBlock, ContentModelBlockGroup, ContentModelDocument, diff --git a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/table/getSelectedCells.ts b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/table/getSelectedCells.ts index 8e793723e12..f06354a3567 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/table/getSelectedCells.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/table/getSelectedCells.ts @@ -1,5 +1,5 @@ import hasSelectionInBlockGroup from '../../publicApi/selection/hasSelectionInBlockGroup'; -import { ContentModelTable } from 'roosterjs-content-model-types'; +import type { ContentModelTable } from 'roosterjs-content-model-types'; /** * @internal diff --git a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/table/insertTableColumn.ts b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/table/insertTableColumn.ts index 3644f22b057..03256dd5636 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/table/insertTableColumn.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/table/insertTableColumn.ts @@ -1,7 +1,7 @@ -import { ContentModelTable } from 'roosterjs-content-model-types'; import { createTableCell } from 'roosterjs-content-model-dom'; import { getSelectedCells } from './getSelectedCells'; import { TableOperation } from 'roosterjs-editor-types'; +import type { ContentModelTable } from 'roosterjs-content-model-types'; import type { CompatibleTableOperation } from 'roosterjs-editor-types/lib/compatibleTypes'; /** diff --git a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/table/insertTableRow.ts b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/table/insertTableRow.ts index 647053cb355..1f7f6cfd7e0 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/table/insertTableRow.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/table/insertTableRow.ts @@ -1,7 +1,7 @@ -import { ContentModelTable } from 'roosterjs-content-model-types'; import { createTableCell } from 'roosterjs-content-model-dom'; import { getSelectedCells } from './getSelectedCells'; import { TableOperation } from 'roosterjs-editor-types'; +import type { ContentModelTable } from 'roosterjs-content-model-types'; import type { CompatibleTableOperation } from 'roosterjs-editor-types/lib/compatibleTypes'; /** diff --git a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/table/mergeTableCells.ts b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/table/mergeTableCells.ts index e871ff05cfb..3ebb89af2b8 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/table/mergeTableCells.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/table/mergeTableCells.ts @@ -1,6 +1,6 @@ import { canMergeCells } from './canMergeCells'; import { getSelectedCells } from './getSelectedCells'; -import { ContentModelTable } from 'roosterjs-content-model-types'; +import type { ContentModelTable } from 'roosterjs-content-model-types'; /** * @internal diff --git a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/table/mergeTableColumn.ts b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/table/mergeTableColumn.ts index bfdf90b770d..9362b73f316 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/table/mergeTableColumn.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/table/mergeTableColumn.ts @@ -1,7 +1,7 @@ import { canMergeCells } from './canMergeCells'; -import { ContentModelTable } from 'roosterjs-content-model-types'; import { getSelectedCells } from './getSelectedCells'; import { TableOperation } from 'roosterjs-editor-types'; +import type { ContentModelTable } from 'roosterjs-content-model-types'; import type { CompatibleTableOperation } from 'roosterjs-editor-types/lib/compatibleTypes'; /** diff --git a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/table/mergeTableRow.ts b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/table/mergeTableRow.ts index 6066e7ffc76..b2aad23fa8b 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/table/mergeTableRow.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/table/mergeTableRow.ts @@ -1,7 +1,7 @@ import { canMergeCells } from './canMergeCells'; -import { ContentModelTable } from 'roosterjs-content-model-types'; import { getSelectedCells } from './getSelectedCells'; import { TableOperation } from 'roosterjs-editor-types'; +import type { ContentModelTable } from 'roosterjs-content-model-types'; import type { CompatibleTableOperation } from 'roosterjs-editor-types/lib/compatibleTypes'; /** diff --git a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/table/normalizeTable.ts b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/table/normalizeTable.ts index 41c28233876..7a278a3589f 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/table/normalizeTable.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/table/normalizeTable.ts @@ -1,6 +1,5 @@ import { addBlock, addSegment, createBr, createParagraph } from 'roosterjs-content-model-dom'; -import { arrayPush } from 'roosterjs-editor-dom'; -import { +import type { ContentModelSegment, ContentModelSegmentFormat, ContentModelTable, @@ -127,7 +126,7 @@ function tryMoveBlocks(targetCell: ContentModelTableCell, sourceCell: ContentMod ); if (!onlyHasEmptyOrBr) { - arrayPush(targetCell.blocks, sourceCell.blocks); + targetCell.blocks.push(...sourceCell.blocks); sourceCell.blocks = []; } } diff --git a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/table/setTableCellBackgroundColor.ts b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/table/setTableCellBackgroundColor.ts index 4523426c7a0..d2a7eac6a08 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/table/setTableCellBackgroundColor.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/table/setTableCellBackgroundColor.ts @@ -1,6 +1,6 @@ -import { ContentModelTableCell } from 'roosterjs-content-model-types'; import { parseColor } from 'roosterjs-editor-dom'; import { updateTableCellMetadata } from '../../domUtils/metadata/updateTableCellMetadata'; +import type { ContentModelTableCell } from 'roosterjs-content-model-types'; // Using the HSL (hue, saturation and lightness) representation for RGB color values. // If the value of the lightness is less than 20, the color is dark. diff --git a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/table/splitTableCellHorizontally.ts b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/table/splitTableCellHorizontally.ts index 5c9e151d422..ea59cbabd3b 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/table/splitTableCellHorizontally.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/table/splitTableCellHorizontally.ts @@ -1,6 +1,6 @@ -import { ContentModelTable } from 'roosterjs-content-model-types'; import { createTableCell } from 'roosterjs-content-model-dom'; import { getSelectedCells } from './getSelectedCells'; +import type { ContentModelTable } from 'roosterjs-content-model-types'; const MIN_WIDTH = 30; diff --git a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/table/splitTableCellVertically.ts b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/table/splitTableCellVertically.ts index 533787c7930..40943f808da 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/table/splitTableCellVertically.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/table/splitTableCellVertically.ts @@ -1,6 +1,6 @@ -import { ContentModelTable, ContentModelTableRow } from 'roosterjs-content-model-types'; import { createTableCell } from 'roosterjs-content-model-dom'; import { getSelectedCells } from './getSelectedCells'; +import type { ContentModelTable, ContentModelTableRow } from 'roosterjs-content-model-types'; const MIN_HEIGHT = 22; diff --git a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/block/setAlignment.ts b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/block/setAlignment.ts index 85ac1349333..fadbcc769a4 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/block/setAlignment.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/block/setAlignment.ts @@ -1,6 +1,6 @@ import { formatWithContentModel } from '../utils/formatWithContentModel'; -import { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; import { setModelAlignment } from '../../modelApi/block/setModelAlignment'; +import type { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; /** * Set text alignment of selected paragraphs @@ -11,5 +11,7 @@ export default function setAlignment( editor: IContentModelEditor, alignment: 'left' | 'center' | 'right' ) { + editor.focus(); + formatWithContentModel(editor, 'setAlignment', model => setModelAlignment(model, alignment)); } diff --git a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/block/setDirection.ts b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/block/setDirection.ts index 45e51120b90..83804c4dabe 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/block/setDirection.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/block/setDirection.ts @@ -1,6 +1,6 @@ import { formatWithContentModel } from '../utils/formatWithContentModel'; -import { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; import { setModelDirection } from '../../modelApi/block/setModelDirection'; +import type { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; /** * Set text direction of selected paragraphs (Left to right or Right to left) @@ -8,5 +8,7 @@ import { setModelDirection } from '../../modelApi/block/setModelDirection'; * @param direction Direction value: ltr (Left to right) or rtl (Right to left) */ export default function setDirection(editor: IContentModelEditor, direction: 'ltr' | 'rtl') { + editor.focus(); + formatWithContentModel(editor, 'setDirection', model => setModelDirection(model, direction)); } diff --git a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/block/setHeadingLevel.ts b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/block/setHeadingLevel.ts index 83fd603ba54..0a172bb41d1 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/block/setHeadingLevel.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/block/setHeadingLevel.ts @@ -1,6 +1,6 @@ -import { ContentModelParagraphDecorator } from 'roosterjs-content-model-types'; import { formatParagraphWithContentModel } from '../utils/formatParagraphWithContentModel'; -import { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; +import type { ContentModelParagraphDecorator } from 'roosterjs-content-model-types'; +import type { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; type HeadingLevelTags = 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6'; @@ -22,6 +22,8 @@ export default function setHeadingLevel( editor: IContentModelEditor, headingLevel: 0 | 1 | 2 | 3 | 4 | 5 | 6 ) { + editor.focus(); + formatParagraphWithContentModel(editor, 'setHeadingLevel', para => { const tagName = headingLevel > 0 diff --git a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/block/setIndentation.ts b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/block/setIndentation.ts index c9f811fb16a..6c01898cec3 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/block/setIndentation.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/block/setIndentation.ts @@ -1,7 +1,7 @@ import { formatWithContentModel } from '../utils/formatWithContentModel'; -import { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; import { normalizeContentModel } from 'roosterjs-content-model-dom'; import { setModelIndentation } from '../../modelApi/block/setModelIndentation'; +import type { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; /** * Indent or outdent to selected paragraphs @@ -14,6 +14,8 @@ export default function setIndentation( indentation: 'indent' | 'outdent', length?: number ) { + editor.focus(); + formatWithContentModel( editor, 'setIndentation', diff --git a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/block/setParagraphMargin.ts b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/block/setParagraphMargin.ts index 466b1f88917..903f8cae4e9 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/block/setParagraphMargin.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/block/setParagraphMargin.ts @@ -1,6 +1,6 @@ import { createParagraphDecorator } from 'roosterjs-content-model-dom'; import { formatParagraphWithContentModel } from '../utils/formatParagraphWithContentModel'; -import { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; +import type { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; /** * Toggles the current block(s) margin properties. @@ -14,6 +14,8 @@ export default function setParagraphMargin( marginTop?: string | null, marginBottom?: string | null ) { + editor.focus(); + formatParagraphWithContentModel(editor, 'setParagraphMargin', para => { if (!para.decorator) { para.decorator = createParagraphDecorator('p'); diff --git a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/block/setSpacing.ts b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/block/setSpacing.ts index 5e7002e716a..f5c36d46448 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/block/setSpacing.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/block/setSpacing.ts @@ -1,5 +1,5 @@ import { formatParagraphWithContentModel } from '../utils/formatParagraphWithContentModel'; -import { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; +import type { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; /** * Sets current selected block(s) line-height property and wipes such property from child segments @@ -7,6 +7,8 @@ import { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; * @param spacing Unitless/px value to set line height */ export default function setSpacing(editor: IContentModelEditor, spacing: number | string) { + editor.focus(); + formatParagraphWithContentModel(editor, 'setSpacing', paragraph => { paragraph.format.lineHeight = spacing.toString(); paragraph.segments.forEach(segment => { diff --git a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/block/toggleBlockQuote.ts b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/block/toggleBlockQuote.ts index eebaa3b194d..d3f4a69c6ef 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/block/toggleBlockQuote.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/block/toggleBlockQuote.ts @@ -1,7 +1,7 @@ -import { ContentModelFormatContainerFormat } from 'roosterjs-content-model-types'; import { formatWithContentModel } from '../utils/formatWithContentModel'; -import { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; import { toggleModelBlockQuote } from '../../modelApi/block/toggleModelBlockQuote'; +import type { ContentModelFormatContainerFormat } from 'roosterjs-content-model-types'; +import type { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; const DefaultQuoteFormat: ContentModelFormatContainerFormat = { borderLeft: '3px solid rgb(200, 200, 200)', // TODO: Support RTL @@ -31,6 +31,8 @@ export default function toggleBlockQuote( ...quoteFormat, }; + editor.focus(); + formatWithContentModel( editor, 'toggleBlockQuote', diff --git a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/editing/keyboardDelete.ts b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/editing/keyboardDelete.ts index 3259fcdbc0f..305db2020e8 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/editing/keyboardDelete.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/editing/keyboardDelete.ts @@ -1,10 +1,12 @@ import { Browser, isModifierKey } from 'roosterjs-editor-dom'; -import { ChangeSource, Keys, NodeType, SelectionRangeTypes } from 'roosterjs-editor-types'; +import { ChangeSource, Keys } from 'roosterjs-editor-types'; import { deleteAllSegmentBefore } from '../../modelApi/edit/deleteSteps/deleteAllSegmentBefore'; -import { DeleteResult, DeleteSelectionStep } from '../../modelApi/edit/utils/DeleteSelectionStep'; +import { DeleteResult } from '../../modelApi/edit/utils/DeleteSelectionStep'; import { deleteSelection } from '../../modelApi/edit/deleteSelection'; import { formatWithContentModel } from '../utils/formatWithContentModel'; -import { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; +import { isNodeOfType } from 'roosterjs-content-model-dom'; +import type { DeleteSelectionStep } from '../../modelApi/edit/utils/DeleteSelectionStep'; +import type { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; import { handleKeyboardEventResult, shouldDeleteAllSegmentsBefore, @@ -30,8 +32,8 @@ export default function keyboardDelete( rawEvent: KeyboardEvent ): boolean { const which = rawEvent.which; - const rangeEx = editor.getSelectionRangeEx(); - const range = rangeEx.type == SelectionRangeTypes.Normal ? rangeEx.ranges[0] : null; + const selection = editor.getDOMSelection(); + const range = selection?.type == 'range' ? selection.range : null; let isDeleted = false; if (shouldDeleteWithContentModel(range, rawEvent)) { @@ -77,7 +79,7 @@ function getDeleteSteps(rawEvent: KeyboardEvent): (DeleteSelectionStep | null)[] function shouldDeleteWithContentModel(range: Range | null, rawEvent: KeyboardEvent) { return !( range?.collapsed && - range.startContainer.nodeType == NodeType.Text && + isNodeOfType(range.startContainer, 'TEXT_NODE') && !isModifierKey(rawEvent) && (canDeleteBefore(rawEvent, range) || canDeleteAfter(rawEvent, range)) ); diff --git a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/entity/insertEntity.ts b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/entity/insertEntity.ts index 2d7774da420..ca907e6809e 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/entity/insertEntity.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/entity/insertEntity.ts @@ -1,10 +1,11 @@ -import { ChangeSource, Entity, SelectionRangeEx } from 'roosterjs-editor-types'; -import { commitEntity, getEntityFromElement } from 'roosterjs-editor-dom'; +import { ChangeSource } from 'roosterjs-editor-types'; import { createEntity, normalizeContentModel } from 'roosterjs-content-model-dom'; import { formatWithContentModel } from '../utils/formatWithContentModel'; -import { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; import { insertEntityModel } from '../../modelApi/entity/insertEntityModel'; -import { +import type { ContentModelEntity, DOMSelection } from 'roosterjs-content-model-types'; +import type { Entity } from 'roosterjs-editor-types'; +import type { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; +import type { InsertEntityOptions, InsertEntityPosition, } from '../../publicTypes/parameter/InsertEntityOptions'; @@ -27,9 +28,9 @@ export default function insertEntity( editor: IContentModelEditor, type: string, isBlock: boolean, - position: 'focus' | 'begin' | 'end' | SelectionRangeEx, + position: 'focus' | 'begin' | 'end' | DOMSelection, options?: InsertEntityOptions -): Entity | null; +): ContentModelEntity | null; /** * Insert a block entity into editor @@ -46,17 +47,17 @@ export default function insertEntity( editor: IContentModelEditor, type: string, isBlock: true, - position: InsertEntityPosition | SelectionRangeEx, + position: InsertEntityPosition | DOMSelection, options?: InsertEntityOptions -): Entity | null; +): ContentModelEntity | null; export default function insertEntity( editor: IContentModelEditor, type: string, isBlock: boolean, - position?: InsertEntityPosition | SelectionRangeEx, + position?: InsertEntityPosition | DOMSelection, options?: InsertEntityOptions -): Entity | null { +): ContentModelEntity | null { const { contentNode, focusAfterEntity, wrapperDisplay, skipUndoSnapshot } = options || {}; const wrapper = editor.getDocument().createElement(isBlock ? BlockEntityTag : InlineEntityTag); const display = wrapperDisplay ?? (isBlock ? undefined : 'inline-block'); @@ -67,10 +68,7 @@ export default function insertEntity( wrapper.appendChild(contentNode); } - commitEntity(wrapper, type, true /*isReadonly*/); - - const entityModel = createEntity(wrapper, true /*isReadonly*/, type); - let newEntity: Entity | null = null; + const entityModel = createEntity(wrapper, true /*isReadonly*/, undefined /*format*/, type); formatWithContentModel( editor, @@ -96,11 +94,18 @@ export default function insertEntity( selectionOverride: typeof position === 'object' ? position : undefined, changeSource: ChangeSource.InsertEntity, getChangeData: () => { - newEntity = getEntityFromElement(wrapper); - return newEntity; + // TODO: Remove this entity when we have standalone editor + const entity: Entity = { + wrapper, + type, + id: '', + isReadonly: true, + }; + + return entity; }, } ); - return newEntity; + return entityModel; } diff --git a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/format/applyDefaultFormat.ts b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/format/applyDefaultFormat.ts index 84f88c319b7..8cddb479939 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/format/applyDefaultFormat.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/format/applyDefaultFormat.ts @@ -1,26 +1,30 @@ -import { ContentModelSegmentFormat } from 'roosterjs-content-model-types'; import { DeleteResult } from '../../modelApi/edit/utils/DeleteSelectionStep'; import { deleteSelection } from '../../modelApi/edit/deleteSelection'; import { formatWithContentModel } from '../utils/formatWithContentModel'; import { getPendingFormat, setPendingFormat } from '../../modelApi/format/pendingFormat'; -import { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; -import { isBlockElement, Position } from 'roosterjs-editor-dom'; import { isNodeOfType, normalizeContentModel } from 'roosterjs-content-model-dom'; -import { NodePosition, NodeType, SelectionRangeTypes } from 'roosterjs-editor-types'; +import { isBlockElement } from 'roosterjs-editor-dom'; +import type { ContentModelSegmentFormat } from 'roosterjs-content-model-types'; +import type { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; /** * @internal * When necessary, set default format as current pending format so it will be applied when Input event is fired * @param editor The Content Model Editor + * @param defaultFormat The default segment format to apply */ -export default function applyDefaultFormat(editor: IContentModelEditor) { - const rangeEx = editor.getSelectionRangeEx(); - const range = rangeEx?.type == SelectionRangeTypes.Normal ? rangeEx.ranges[0] : null; - const startPos = range ? Position.getStart(range) : null; - let node: Node | null = startPos?.node ?? null; +export default function applyDefaultFormat( + editor: IContentModelEditor, + defaultFormat: ContentModelSegmentFormat +) { + const selection = editor.getDOMSelection(); + const range = selection?.type == 'range' ? selection.range : null; + const posContainer = range?.startContainer ?? null; + const posOffset = range?.startOffset ?? null; + let node = posContainer; while (node && editor.contains(node)) { - if (isNodeOfType(node, NodeType.Element) && node.getAttribute?.('style')) { + if (isNodeOfType(node, 'ELEMENT_NODE') && node.getAttribute?.('style')) { return; } else if (isBlockElement(node)) { break; @@ -40,7 +44,8 @@ export default function applyDefaultFormat(editor: IContentModelEditor) { } else if ( result.deleteResult == DeleteResult.NotDeleted && result.insertPoint && - startPos + posContainer && + posOffset !== null ) { const { paragraph, path, marker } = result.insertPoint; const blocks = path[0].blocks; @@ -63,10 +68,22 @@ export default function applyDefaultFormat(editor: IContentModelEditor) { const previousBlock = blocks[blockIndex - 1]; if (previousBlock?.blockType != 'Paragraph') { - internalApplyDefaultFormat(editor, marker.format, startPos); + internalApplyDefaultFormat( + editor, + defaultFormat, + marker.format, + posContainer, + posOffset + ); } } else if (paragraph.segments.every(x => x.segmentType != 'Text')) { - internalApplyDefaultFormat(editor, marker.format, startPos); + internalApplyDefaultFormat( + editor, + defaultFormat, + marker.format, + posContainer, + posOffset + ); } // We didn't do any change but just apply default format to pending format, so no need to write back @@ -79,16 +96,17 @@ export default function applyDefaultFormat(editor: IContentModelEditor) { function internalApplyDefaultFormat( editor: IContentModelEditor, + defaultFormat: ContentModelSegmentFormat, currentFormat: ContentModelSegmentFormat, - startPos: NodePosition + posContainer: Node, + posOffset: number ) { const pendingFormat = getPendingFormat(editor) || {}; - const defaultFormat = editor.getContentModelDefaultFormat(); const newFormat: ContentModelSegmentFormat = { ...defaultFormat, ...pendingFormat, ...currentFormat, }; - setPendingFormat(editor, newFormat, startPos); + setPendingFormat(editor, newFormat, posContainer, posOffset); } diff --git a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/format/applyPendingFormat.ts b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/format/applyPendingFormat.ts index 50293fb72a6..7a892435298 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/format/applyPendingFormat.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/format/applyPendingFormat.ts @@ -1,7 +1,7 @@ import { formatWithContentModel } from '../utils/formatWithContentModel'; import { getPendingFormat } from '../../modelApi/format/pendingFormat'; -import { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; import { iterateSelections } from '../../modelApi/selection/iterateSelections'; +import type { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; import { createText, normalizeContentModel, diff --git a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/format/clearFormat.ts b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/format/clearFormat.ts index 26f276ebe53..285ba03b6b7 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/format/clearFormat.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/format/clearFormat.ts @@ -1,8 +1,8 @@ import { clearModelFormat } from '../../modelApi/common/clearModelFormat'; import { formatWithContentModel } from '../utils/formatWithContentModel'; -import { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; import { normalizeContentModel } from 'roosterjs-content-model-dom'; -import { +import type { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; +import type { ContentModelBlock, ContentModelBlockGroup, ContentModelSegment, @@ -14,6 +14,8 @@ import { * @param editor The editor to clear format from */ export default function clearFormat(editor: IContentModelEditor) { + editor.focus(); + formatWithContentModel(editor, 'clearFormat', model => { const blocksToClear: [ContentModelBlockGroup[], ContentModelBlock][] = []; const segmentsToClear: ContentModelSegment[] = []; diff --git a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/format/getFormatState.ts b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/format/getFormatState.ts index 42dbb47f7e3..4ec030cbbcf 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/format/getFormatState.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/format/getFormatState.ts @@ -1,13 +1,14 @@ -import { contains, getTagOfNode } from 'roosterjs-editor-dom'; -import { ContentModelBlockGroup, DomToModelContext } from 'roosterjs-content-model-types'; -import { ContentModelFormatState } from '../../publicTypes/format/formatState/ContentModelFormatState'; +import { contains } from 'roosterjs-editor-dom'; import { getPendingFormat } from '../../modelApi/format/pendingFormat'; import { getSelectionRootNode } from '../../modelApi/selection/getSelectionRootNode'; -import { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; import { retrieveModelFormatState } from '../../modelApi/common/retrieveModelFormatState'; +import type { ContentModelBlockGroup, DomToModelContext } from 'roosterjs-content-model-types'; +import type { ContentModelFormatState } from '../../publicTypes/format/formatState/ContentModelFormatState'; +import type { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; import { getRegularSelectionOffsets, handleRegularSelection, + isNodeOfType, processChildNode, } from 'roosterjs-content-model-dom'; @@ -57,7 +58,7 @@ export function reducedModelChildProcessor( parent: ParentNode, context: FormatStateContext ) { - const selectionRootNode = getSelectionRootNode(context.rangeEx); + const selectionRootNode = getSelectionRootNode(context.selection); if (selectionRootNode) { if (!context.nodeStack) { @@ -96,7 +97,7 @@ function createNodeStack(root: Node, startNode: Node): Node[] { let node: Node | null = startNode; while (node && contains(root, node)) { - if (getTagOfNode(node) == 'TABLE') { + if (isNodeOfType(node, 'ELEMENT_NODE') && node.tagName == 'TABLE') { // For table, we can't do a reduced model creation since we need to handle their cells and indexes, // so clean up whatever we already have, and just put table into the stack result.splice(0, result.length, node); diff --git a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/image/adjustImageSelection.ts b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/image/adjustImageSelection.ts index 3010f6ea882..52bf444be05 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/image/adjustImageSelection.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/image/adjustImageSelection.ts @@ -1,7 +1,7 @@ import { adjustSegmentSelection } from '../../modelApi/selection/adjustSegmentSelection'; -import { ContentModelImage } from 'roosterjs-content-model-types'; import { formatWithContentModel } from '../utils/formatWithContentModel'; -import { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; +import type { ContentModelImage } from 'roosterjs-content-model-types'; +import type { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; /** * Adjust selection to make sure select an image if any diff --git a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/image/changeImage.ts b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/image/changeImage.ts index a3f6de4f25d..b87c7db6cc8 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/image/changeImage.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/image/changeImage.ts @@ -1,8 +1,7 @@ import formatImageWithContentModel from '../utils/formatImageWithContentModel'; -import { ContentModelImage } from 'roosterjs-content-model-types'; import { getMetadata, readFile } from 'roosterjs-editor-dom'; -import { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; -import { SelectionRangeTypes } from 'roosterjs-editor-types'; +import type { ContentModelImage } from 'roosterjs-content-model-types'; +import type { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; /** * Change the selected image src @@ -10,13 +9,11 @@ import { SelectionRangeTypes } from 'roosterjs-editor-types'; * @param file The image file */ export default function changeImage(editor: IContentModelEditor, file: File) { - const selection = editor.getSelectionRangeEx(); + editor.focus(); + + const selection = editor.getDOMSelection(); readFile(file, dataUrl => { - if ( - dataUrl && - !editor.isDisposed() && - selection.type === SelectionRangeTypes.ImageSelection - ) { + if (dataUrl && !editor.isDisposed() && selection?.type === 'image') { formatImageWithContentModel( editor, 'changeImage', diff --git a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/image/insertImage.ts b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/image/insertImage.ts index b3a47c880df..32d60e9b203 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/image/insertImage.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/image/insertImage.ts @@ -1,8 +1,8 @@ import { addSegment, createContentModelDocument, createImage } from 'roosterjs-content-model-dom'; import { formatWithContentModel } from '../utils/formatWithContentModel'; -import { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; import { mergeModel } from '../../modelApi/common/mergeModel'; import { readFile } from 'roosterjs-editor-dom'; +import type { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; /** * Insert an image into current selected position @@ -10,6 +10,8 @@ import { readFile } from 'roosterjs-editor-dom'; * @param file Image Blob file or source string */ export default function insertImage(editor: IContentModelEditor, imageFileOrSrc: File | string) { + editor.focus(); + if (typeof imageFileOrSrc == 'string') { insertImageWithSrc(editor, imageFileOrSrc); } else { diff --git a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/image/setImageAltText.ts b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/image/setImageAltText.ts index 9b1a769d416..fc40ca1c4ad 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/image/setImageAltText.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/image/setImageAltText.ts @@ -1,6 +1,6 @@ import formatImageWithContentModel from '../utils/formatImageWithContentModel'; -import { ContentModelImage } from 'roosterjs-content-model-types'; -import { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; +import type { ContentModelImage } from 'roosterjs-content-model-types'; +import type { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; /** * Set image alt text for all selected images at selection. If no images is contained @@ -9,6 +9,8 @@ import { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; * @param altText The image alt text */ export default function setImageAltText(editor: IContentModelEditor, altText: string) { + editor.focus(); + formatImageWithContentModel(editor, 'setImageAltText', (image: ContentModelImage) => { image.alt = altText; }); diff --git a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/image/setImageBorder.ts b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/image/setImageBorder.ts index 0f4a57eab3e..9774b47a1e6 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/image/setImageBorder.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/image/setImageBorder.ts @@ -1,8 +1,8 @@ import applyImageBorderFormat from '../../modelApi/image/applyImageBorderFormat'; import formatImageWithContentModel from '../utils/formatImageWithContentModel'; -import { Border } from '../../publicTypes/interface/Border'; -import { ContentModelImage } from 'roosterjs-content-model-types'; -import { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; +import type { Border } from '../../publicTypes/interface/Border'; +import type { ContentModelImage } from 'roosterjs-content-model-types'; +import type { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; /** * Set image border style for all selected images at selection. @@ -16,6 +16,8 @@ export default function setImageBorder( border: Border | null, borderRadius?: string ) { + editor.focus(); + formatImageWithContentModel(editor, 'setImageBorder', (image: ContentModelImage) => { applyImageBorderFormat(image, border, borderRadius); }); diff --git a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/image/setImageBoxShadow.ts b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/image/setImageBoxShadow.ts index 3b9177ea81e..014221dce4c 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/image/setImageBoxShadow.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/image/setImageBoxShadow.ts @@ -1,6 +1,6 @@ import formatImageWithContentModel from '../utils/formatImageWithContentModel'; -import { ContentModelImage } from 'roosterjs-content-model-types'; -import { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; +import type { ContentModelImage } from 'roosterjs-content-model-types'; +import type { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; /** * Set image box shadow for all selected images at selection. @@ -13,6 +13,8 @@ export default function setImageBoxShadow( boxShadow: string, margin?: string | null ) { + editor.focus(); + formatImageWithContentModel(editor, 'setImageBoxShadow', (image: ContentModelImage) => { image.format.boxShadow = boxShadow; if (margin) { diff --git a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/link/adjustLinkSelection.ts b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/link/adjustLinkSelection.ts index 7cbcfc0bd06..8c7101d18bf 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/link/adjustLinkSelection.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/link/adjustLinkSelection.ts @@ -2,8 +2,8 @@ import getSelectedSegments from '../selection/getSelectedSegments'; import { adjustSegmentSelection } from '../../modelApi/selection/adjustSegmentSelection'; import { adjustWordSelection } from '../../modelApi/selection/adjustWordSelection'; import { formatWithContentModel } from '../utils/formatWithContentModel'; -import { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; import { setSelection } from '../../modelApi/selection/setSelection'; +import type { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; /** * Adjust selection to make sure select a hyperlink if any, or a word if original selection is collapsed diff --git a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/link/insertLink.ts b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/link/insertLink.ts index fd61d92252f..c88f826ac07 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/link/insertLink.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/link/insertLink.ts @@ -1,11 +1,11 @@ import getSelectedSegments from '../selection/getSelectedSegments'; import { ChangeSource } from 'roosterjs-editor-types'; -import { ContentModelLink } from 'roosterjs-content-model-types'; import { formatWithContentModel } from '../utils/formatWithContentModel'; import { getPendingFormat } from '../../modelApi/format/pendingFormat'; import { HtmlSanitizer, matchLink } from 'roosterjs-editor-dom'; -import { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; import { mergeModel } from '../../modelApi/common/mergeModel'; +import type { ContentModelLink } from 'roosterjs-content-model-types'; +import type { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; import { addLink, addSegment, @@ -40,19 +40,12 @@ export default function insertLink( displayText?: string, target?: string ) { + editor.focus(); + let url = (checkXss(link) || '').trim(); if (url) { const linkData = matchLink(url); - const link: ContentModelLink = { - dataset: {}, - format: { - href: linkData ? linkData.normalizedUrl : applyLinkPrefix(url), - anchorTitle, - target, - underline: true, - }, - }; - + const linkUrl = linkData ? linkData.normalizedUrl : applyLinkPrefix(url); const links: ContentModelLink[] = []; let anchorNode: Node | undefined; @@ -71,8 +64,13 @@ export default function insertLink( originalText == text ) { segments.forEach(x => { + const link = createLink( + linkUrl, + anchorTitle, + target, + x.segmentType == 'Text' + ); addLink(x, link); - if (x.link) { links.push(x.link); } @@ -86,6 +84,7 @@ export default function insertLink( ...(getPendingFormat(editor) || {}), }); const doc = createContentModelDocument(); + const link = createLink(linkUrl, anchorTitle, target); addLink(segment, link); addSegment(doc, segment); @@ -114,6 +113,23 @@ export default function insertLink( } } +const createLink = ( + url: string, + anchorTitle?: string, + target?: string, + underline: boolean = true +): ContentModelLink => { + return { + dataset: {}, + format: { + href: url, + anchorTitle, + target, + underline: underline, + }, + }; +}; + // TODO: This is copied from original code. We may need to integrate this logic into matchLink() later. function applyLinkPrefix(url: string): string { if (!url) { diff --git a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/link/removeLink.ts b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/link/removeLink.ts index a874b404731..45684396f2f 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/link/removeLink.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/link/removeLink.ts @@ -1,7 +1,7 @@ import getSelectedSegments from '../selection/getSelectedSegments'; import { adjustSegmentSelection } from '../../modelApi/selection/adjustSegmentSelection'; import { formatWithContentModel } from '../utils/formatWithContentModel'; -import { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; +import type { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; /** * Remove link at selection. If no links at selection, do nothing. @@ -10,6 +10,8 @@ import { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; * @param editor The editor instance */ export default function removeLink(editor: IContentModelEditor) { + editor.focus(); + formatWithContentModel(editor, 'removeLink', model => { adjustSegmentSelection( model, diff --git a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/list/setListStartNumber.ts b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/list/setListStartNumber.ts index a8f264be14e..a1ebe108070 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/list/setListStartNumber.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/list/setListStartNumber.ts @@ -1,6 +1,6 @@ import { formatWithContentModel } from '../utils/formatWithContentModel'; import { getFirstSelectedListItem } from '../../modelApi/selection/collectSelections'; -import { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; +import type { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; /** * Set start number of a list item @@ -8,6 +8,8 @@ import { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; * @param value The number to set to, must be equal or greater than 1 */ export default function setListStartNumber(editor: IContentModelEditor, value: number) { + editor.focus(); + formatWithContentModel(editor, 'setListStartNumber', model => { const listItem = getFirstSelectedListItem(model); const level = listItem?.levels[listItem?.levels.length - 1]; diff --git a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/list/setListStyle.ts b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/list/setListStyle.ts index 4776c693acf..5d08bc38cc2 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/list/setListStyle.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/list/setListStyle.ts @@ -1,9 +1,9 @@ import { findListItemsInSameThread } from '../../modelApi/list/findListItemsInSameThread'; import { formatWithContentModel } from '../utils/formatWithContentModel'; import { getFirstSelectedListItem } from '../../modelApi/selection/collectSelections'; -import { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; -import { ListMetadataFormat } from 'roosterjs-content-model-types'; import { updateListMetadata } from 'roosterjs-content-model-dom'; +import type { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; +import type { ListMetadataFormat } from 'roosterjs-content-model-types'; /** * Set style of list items with in same thread of current item @@ -11,6 +11,8 @@ import { updateListMetadata } from 'roosterjs-content-model-dom'; * @param style The target list item style to set */ export default function setListStyle(editor: IContentModelEditor, style: ListMetadataFormat) { + editor.focus(); + formatWithContentModel(editor, 'setListStyle', model => { const listItem = getFirstSelectedListItem(model); diff --git a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/list/toggleBullet.ts b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/list/toggleBullet.ts index f0012bd0b30..379ede04394 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/list/toggleBullet.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/list/toggleBullet.ts @@ -1,6 +1,6 @@ import { formatWithContentModel } from '../utils/formatWithContentModel'; -import { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; import { setListType } from '../../modelApi/list/setListType'; +import type { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; /** * Toggle bullet list type @@ -9,6 +9,8 @@ import { setListType } from '../../modelApi/list/setListType'; * @param editor The editor to operate on */ export default function toggleBullet(editor: IContentModelEditor) { + editor.focus(); + formatWithContentModel(editor, 'toggleBullet', model => setListType(model, 'UL'), { preservePendingFormat: true, }); diff --git a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/list/toggleNumbering.ts b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/list/toggleNumbering.ts index a04459ecbad..7400d3d0c0b 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/list/toggleNumbering.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/list/toggleNumbering.ts @@ -1,6 +1,6 @@ import { formatWithContentModel } from '../utils/formatWithContentModel'; -import { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; import { setListType } from '../../modelApi/list/setListType'; +import type { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; /** * Toggle numbering list type @@ -9,6 +9,8 @@ import { setListType } from '../../modelApi/list/setListType'; * @param editor The editor to operate on */ export default function toggleNumbering(editor: IContentModelEditor) { + editor.focus(); + formatWithContentModel(editor, 'toggleNumbering', model => setListType(model, 'OL'), { preservePendingFormat: true, }); diff --git a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/segment/applySegmentFormat.ts b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/segment/applySegmentFormat.ts index caeed913c1b..d4efff86879 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/segment/applySegmentFormat.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/segment/applySegmentFormat.ts @@ -1,6 +1,6 @@ -import { ContentModelSegmentFormat } from 'roosterjs-content-model-types'; import { formatSegmentWithContentModel } from '../utils/formatSegmentWithContentModel'; -import { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; +import type { ContentModelSegmentFormat } from 'roosterjs-content-model-types'; +import type { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; /** * Bulk apply segment format to all selected content. This is usually used for format painter. diff --git a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/segment/changeCapitalization.ts b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/segment/changeCapitalization.ts index a3900553351..e0f11220813 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/segment/changeCapitalization.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/segment/changeCapitalization.ts @@ -1,5 +1,5 @@ import { formatSegmentWithContentModel } from '../utils/formatSegmentWithContentModel'; -import { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; +import type { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; /** * Change the capitalization of text in the selection @@ -14,6 +14,8 @@ export default function changeCapitalization( capitalization: 'sentence' | 'lowerCase' | 'upperCase' | 'capitalize', language?: string ) { + editor.focus(); + formatSegmentWithContentModel(editor, 'changeCapitalization', (_, __, segment) => { if (segment?.segmentType == 'Text') { switch (capitalization) { diff --git a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/segment/changeFontSize.ts b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/segment/changeFontSize.ts index 10868f8b4b8..f37953262ce 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/segment/changeFontSize.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/segment/changeFontSize.ts @@ -1,8 +1,11 @@ -import { ContentModelParagraph, ContentModelSegmentFormat } from 'roosterjs-content-model-types'; import { formatSegmentWithContentModel } from '../utils/formatSegmentWithContentModel'; -import { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; import { parseValueWithUnit } from 'roosterjs-content-model-dom'; import { setFontSizeInternal } from './setFontSize'; +import type { + ContentModelParagraph, + ContentModelSegmentFormat, +} from 'roosterjs-content-model-types'; +import type { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; /** * Default font size sequence, in pt. Suggest editor UI use this sequence as your font size list, @@ -22,6 +25,8 @@ export default function changeFontSize( editor: IContentModelEditor, change: 'increase' | 'decrease' ) { + editor.focus(); + formatSegmentWithContentModel( editor, 'changeFontSize', diff --git a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/segment/setBackgroundColor.ts b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/segment/setBackgroundColor.ts index b029b0e87db..3f8e769b69d 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/segment/setBackgroundColor.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/segment/setBackgroundColor.ts @@ -1,8 +1,8 @@ -import { ContentModelParagraph } from 'roosterjs-content-model-types'; import { createSelectionMarker } from 'roosterjs-content-model-dom'; import { formatSegmentWithContentModel } from '../utils/formatSegmentWithContentModel'; -import { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; import { setSelection } from '../../modelApi/selection/setSelection'; +import type { ContentModelParagraph } from 'roosterjs-content-model-types'; +import type { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; /** * Set background color @@ -13,6 +13,8 @@ export default function setBackgroundColor( editor: IContentModelEditor, backgroundColor: string | null ) { + editor.focus(); + let lastParagraph: ContentModelParagraph | null = null; let lastSegmentIndex: number = -1; diff --git a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/segment/setFontName.ts b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/segment/setFontName.ts index 65964bee048..64b8014f39f 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/segment/setFontName.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/segment/setFontName.ts @@ -1,5 +1,5 @@ import { formatSegmentWithContentModel } from '../utils/formatSegmentWithContentModel'; -import { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; +import type { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; /** * Set font name @@ -7,6 +7,8 @@ import { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; * @param fontName The font name to set */ export default function setFontName(editor: IContentModelEditor, fontName: string) { + editor.focus(); + formatSegmentWithContentModel( editor, 'setFontName', diff --git a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/segment/setFontSize.ts b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/segment/setFontSize.ts index c718710e088..e958825158f 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/segment/setFontSize.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/segment/setFontSize.ts @@ -1,6 +1,9 @@ -import { ContentModelParagraph, ContentModelSegmentFormat } from 'roosterjs-content-model-types'; import { formatSegmentWithContentModel } from '../utils/formatSegmentWithContentModel'; -import { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; +import type { + ContentModelParagraph, + ContentModelSegmentFormat, +} from 'roosterjs-content-model-types'; +import type { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; /** * Set font size @@ -8,6 +11,8 @@ import { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; * @param fontSize The font size to set */ export default function setFontSize(editor: IContentModelEditor, fontSize: string) { + editor.focus(); + formatSegmentWithContentModel( editor, 'setFontSize', diff --git a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/segment/setTextColor.ts b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/segment/setTextColor.ts index fe2df53f0e8..60d51fcaae3 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/segment/setTextColor.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/segment/setTextColor.ts @@ -1,5 +1,5 @@ import { formatSegmentWithContentModel } from '../utils/formatSegmentWithContentModel'; -import { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; +import type { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; /** * Set text color @@ -7,6 +7,8 @@ import { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; * @param textColor The text color to set. Pass null to remove existing color. */ export default function setTextColor(editor: IContentModelEditor, textColor: string | null) { + editor.focus(); + formatSegmentWithContentModel( editor, 'setTextColor', diff --git a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/segment/toggleBold.ts b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/segment/toggleBold.ts index 58d77c03e5e..f31e6a73981 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/segment/toggleBold.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/segment/toggleBold.ts @@ -1,11 +1,13 @@ import { formatSegmentWithContentModel } from '../utils/formatSegmentWithContentModel'; -import { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; +import type { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; /** * Toggle bold style * @param editor The editor to operate on */ export default function toggleBold(editor: IContentModelEditor) { + editor.focus(); + formatSegmentWithContentModel( editor, 'toggleBold', diff --git a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/segment/toggleCode.ts b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/segment/toggleCode.ts index 31797f33a64..05a9d49f62f 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/segment/toggleCode.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/segment/toggleCode.ts @@ -1,7 +1,7 @@ import { addCode } from 'roosterjs-content-model-dom'; -import { ContentModelCode } from 'roosterjs-content-model-types'; import { formatSegmentWithContentModel } from '../utils/formatSegmentWithContentModel'; -import { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; +import type { ContentModelCode } from 'roosterjs-content-model-types'; +import type { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; const DefaultCode: ContentModelCode = { format: { @@ -14,6 +14,8 @@ const DefaultCode: ContentModelCode = { * @param editor The editor to operate on */ export default function toggleCode(editor: IContentModelEditor) { + editor.focus(); + formatSegmentWithContentModel( editor, 'toggleCode', diff --git a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/segment/toggleItalic.ts b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/segment/toggleItalic.ts index 8c62d361122..70a8d718153 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/segment/toggleItalic.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/segment/toggleItalic.ts @@ -1,11 +1,13 @@ import { formatSegmentWithContentModel } from '../utils/formatSegmentWithContentModel'; -import { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; +import type { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; /** * Toggle italic style * @param editor The editor to operate on */ export default function toggleItalic(editor: IContentModelEditor) { + editor.focus(); + formatSegmentWithContentModel( editor, 'toggleItalic', diff --git a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/segment/toggleStrikethrough.ts b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/segment/toggleStrikethrough.ts index aa227e891d3..075eb2ebefa 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/segment/toggleStrikethrough.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/segment/toggleStrikethrough.ts @@ -1,11 +1,13 @@ import { formatSegmentWithContentModel } from '../utils/formatSegmentWithContentModel'; -import { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; +import type { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; /** * Toggle strikethrough style * @param editor The editor to operate on */ export default function toggleStrikethrough(editor: IContentModelEditor) { + editor.focus(); + formatSegmentWithContentModel( editor, 'toggleStrikethrough', diff --git a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/segment/toggleSubscript.ts b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/segment/toggleSubscript.ts index 9e9c569fa15..8e1d177a21d 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/segment/toggleSubscript.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/segment/toggleSubscript.ts @@ -1,11 +1,13 @@ import { formatSegmentWithContentModel } from '../utils/formatSegmentWithContentModel'; -import { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; +import type { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; /** * Toggle subscript style * @param editor The editor to operate on */ export default function toggleSubscript(editor: IContentModelEditor) { + editor.focus(); + formatSegmentWithContentModel( editor, 'toggleSubscript', diff --git a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/segment/toggleSuperscript.ts b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/segment/toggleSuperscript.ts index 4ebbbe2127d..716838ae7da 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/segment/toggleSuperscript.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/segment/toggleSuperscript.ts @@ -1,11 +1,13 @@ import { formatSegmentWithContentModel } from '../utils/formatSegmentWithContentModel'; -import { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; +import type { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; /** * Toggle superscript style * @param editor The editor to operate on */ export default function toggleSuperscript(editor: IContentModelEditor) { + editor.focus(); + formatSegmentWithContentModel( editor, 'toggleSuperscript', diff --git a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/segment/toggleUnderline.ts b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/segment/toggleUnderline.ts index 39ffc055444..6fdd7cd2aec 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/segment/toggleUnderline.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/segment/toggleUnderline.ts @@ -1,11 +1,13 @@ import { formatSegmentWithContentModel } from '../utils/formatSegmentWithContentModel'; -import { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; +import type { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; /** * Toggle underline style * @param editor The editor to operate on */ export default function toggleUnderline(editor: IContentModelEditor) { + editor.focus(); + formatSegmentWithContentModel( editor, 'toggleUnderline', diff --git a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/selection/getSelectedSegments.ts b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/selection/getSelectedSegments.ts index 988c843092e..fceae7b5a91 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/selection/getSelectedSegments.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/selection/getSelectedSegments.ts @@ -1,5 +1,5 @@ -import { ContentModelDocument, ContentModelSegment } from 'roosterjs-content-model-types'; import { getSelectedSegmentsAndParagraphs } from '../../modelApi/selection/collectSelections'; +import type { ContentModelDocument, ContentModelSegment } from 'roosterjs-content-model-types'; /** * Get selected segments from a content model diff --git a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/selection/hasSelectionInBlock.ts b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/selection/hasSelectionInBlock.ts index 23b312bd524..a0144bcb1e9 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/selection/hasSelectionInBlock.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/selection/hasSelectionInBlock.ts @@ -1,6 +1,6 @@ import hasSelectionInBlockGroup from './hasSelectionInBlockGroup'; import hasSelectionInSegment from './hasSelectionInSegment'; -import { ContentModelBlock } from 'roosterjs-content-model-types'; +import type { ContentModelBlock } from 'roosterjs-content-model-types'; /** * Check if there is selection within the given block diff --git a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/selection/hasSelectionInBlockGroup.ts b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/selection/hasSelectionInBlockGroup.ts index 99118984bb8..a6bc83ede70 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/selection/hasSelectionInBlockGroup.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/selection/hasSelectionInBlockGroup.ts @@ -1,5 +1,5 @@ import hasSelectionInBlock from './hasSelectionInBlock'; -import { ContentModelBlockGroup } from 'roosterjs-content-model-types'; +import type { ContentModelBlockGroup } from 'roosterjs-content-model-types'; /** * Check if there is selection within the given block diff --git a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/selection/hasSelectionInSegment.ts b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/selection/hasSelectionInSegment.ts index b7592d62808..8d059ab6196 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/selection/hasSelectionInSegment.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/selection/hasSelectionInSegment.ts @@ -1,5 +1,5 @@ import hasSelectionInBlock from './hasSelectionInBlock'; -import { ContentModelSegment } from 'roosterjs-content-model-types'; +import type { ContentModelSegment } from 'roosterjs-content-model-types'; /** * Check if there is selection within the given segment diff --git a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/table/editTable.ts b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/table/editTable.ts index f6c2e5fa93f..71a71d72fe8 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/table/editTable.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/table/editTable.ts @@ -8,7 +8,6 @@ import { deleteTableRow } from '../../modelApi/table/deleteTableRow'; import { ensureFocusableParagraphForTable } from '../../modelApi/table/ensureFocusableParagraphForTable'; import { formatWithContentModel } from '../utils/formatWithContentModel'; import { getFirstSelectedTable } from '../../modelApi/selection/collectSelections'; -import { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; import { insertTableColumn } from '../../modelApi/table/insertTableColumn'; import { insertTableRow } from '../../modelApi/table/insertTableRow'; import { mergeTableCells } from '../../modelApi/table/mergeTableCells'; @@ -19,6 +18,7 @@ import { setSelection } from '../../modelApi/selection/setSelection'; import { splitTableCellHorizontally } from '../../modelApi/table/splitTableCellHorizontally'; import { splitTableCellVertically } from '../../modelApi/table/splitTableCellVertically'; import { TableOperation } from 'roosterjs-editor-types'; +import type { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; import { createSelectionMarker, hasMetadata, @@ -31,6 +31,8 @@ import { * @param operation The table operation to apply */ export default function editTable(editor: IContentModelEditor, operation: TableOperation) { + editor.focus(); + formatWithContentModel(editor, 'editTable', model => { const [tableModel, path] = getFirstSelectedTable(model); diff --git a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/table/formatTable.ts b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/table/formatTable.ts index e45a1796609..eccafb745f3 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/table/formatTable.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/table/formatTable.ts @@ -1,8 +1,8 @@ import { applyTableFormat } from '../../modelApi/table/applyTableFormat'; import { formatWithContentModel } from '../utils/formatWithContentModel'; import { getFirstSelectedTable } from '../../modelApi/selection/collectSelections'; -import { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; -import { TableMetadataFormat } from 'roosterjs-content-model-types'; +import type { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; +import type { TableMetadataFormat } from 'roosterjs-content-model-types'; /** * Format current focused table with the given format @@ -15,6 +15,8 @@ export default function formatTable( format: TableMetadataFormat, keepCellShade?: boolean ) { + editor.focus(); + formatWithContentModel(editor, 'formatTable', model => { const [tableModel] = getFirstSelectedTable(model); diff --git a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/table/insertTable.ts b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/table/insertTable.ts index 530d2a46785..c46dc0b5e85 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/table/insertTable.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/table/insertTable.ts @@ -4,11 +4,11 @@ import { createTableStructure } from '../../modelApi/table/createTableStructure' import { deleteSelection } from '../../modelApi/edit/deleteSelection'; import { formatWithContentModel } from '../utils/formatWithContentModel'; import { getPendingFormat } from '../../modelApi/format/pendingFormat'; -import { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; import { mergeModel } from '../../modelApi/common/mergeModel'; import { normalizeTable } from '../../modelApi/table/normalizeTable'; import { setSelection } from '../../modelApi/selection/setSelection'; -import { TableMetadataFormat } from 'roosterjs-content-model-types'; +import type { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; +import type { TableMetadataFormat } from 'roosterjs-content-model-types'; /** * Insert table into editor at current selection @@ -25,6 +25,8 @@ export default function insertTable( rows: number, format?: Partial ) { + editor.focus(); + formatWithContentModel(editor, 'insertTable', (model, context) => { const insertPosition = deleteSelection(model, [], context).insertPoint; diff --git a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/table/setTableCellShade.ts b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/table/setTableCellShade.ts index 93cf22486c2..f9483e86267 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/table/setTableCellShade.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/table/setTableCellShade.ts @@ -1,9 +1,9 @@ import hasSelectionInBlockGroup from '../selection/hasSelectionInBlockGroup'; import { formatWithContentModel } from '../utils/formatWithContentModel'; import { getFirstSelectedTable } from '../../modelApi/selection/collectSelections'; -import { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; import { normalizeTable } from '../../modelApi/table/normalizeTable'; import { setTableCellBackgroundColor } from '../../modelApi/table/setTableCellBackgroundColor'; +import type { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; /** * Set table cell shade color @@ -11,6 +11,8 @@ import { setTableCellBackgroundColor } from '../../modelApi/table/setTableCellBa * @param color The color to set. Pass null to remove existing shade color */ export default function setTableCellShade(editor: IContentModelEditor, color: string | null) { + editor.focus(); + formatWithContentModel(editor, 'setTableCellShade', model => { const [table] = getFirstSelectedTable(model); diff --git a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/utils/formatImageWithContentModel.ts b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/utils/formatImageWithContentModel.ts index 6dd6e308225..8fbbced3103 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/utils/formatImageWithContentModel.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/utils/formatImageWithContentModel.ts @@ -1,7 +1,8 @@ -import { ContentModelImage } from 'roosterjs-content-model-types'; -import { EditImageEventData, PluginEventType } from 'roosterjs-editor-types'; import { formatSegmentWithContentModel } from './formatSegmentWithContentModel'; -import { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; +import { PluginEventType } from 'roosterjs-editor-types'; +import type { ContentModelImage } from 'roosterjs-content-model-types'; +import type { EditImageEventData } from 'roosterjs-editor-types'; +import type { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; /** * @internal diff --git a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/utils/formatParagraphWithContentModel.ts b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/utils/formatParagraphWithContentModel.ts index d87b3a736d1..21321732213 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/utils/formatParagraphWithContentModel.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/utils/formatParagraphWithContentModel.ts @@ -1,7 +1,7 @@ -import { ContentModelParagraph } from 'roosterjs-content-model-types'; import { formatWithContentModel } from './formatWithContentModel'; import { getSelectedParagraphs } from '../../modelApi/selection/collectSelections'; -import { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; +import type { ContentModelParagraph } from 'roosterjs-content-model-types'; +import type { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; /** * @internal diff --git a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/utils/formatSegmentWithContentModel.ts b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/utils/formatSegmentWithContentModel.ts index 899f3f0b882..db26c1dd112 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/utils/formatSegmentWithContentModel.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/utils/formatSegmentWithContentModel.ts @@ -2,8 +2,8 @@ import { adjustWordSelection } from '../../modelApi/selection/adjustWordSelectio import { formatWithContentModel } from './formatWithContentModel'; import { getPendingFormat, setPendingFormat } from '../../modelApi/format/pendingFormat'; import { getSelectedSegmentsAndParagraphs } from '../../modelApi/selection/collectSelections'; -import { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; -import { +import type { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; +import type { ContentModelDocument, ContentModelParagraph, ContentModelSegment, @@ -73,7 +73,7 @@ export function formatSegmentWithContentModel( const pos = editor.getFocusedPosition(); if (pos) { - setPendingFormat(editor, segmentAndParagraphs[0][0].format, pos); + setPendingFormat(editor, segmentAndParagraphs[0][0].format, pos.node, pos.offset); } } diff --git a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/utils/formatWithContentModel.ts b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/utils/formatWithContentModel.ts index d47922f5134..1642d867764 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/utils/formatWithContentModel.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/utils/formatWithContentModel.ts @@ -1,12 +1,14 @@ -import { ChangeSource, PluginEventType, SelectionRangeEx } from 'roosterjs-editor-types'; -import { ContentModelContentChangedEventData } from '../../publicTypes/event/ContentModelContentChangedEvent'; +import { ChangeSource, PluginEventType } from 'roosterjs-editor-types'; import { getPendingFormat, setPendingFormat } from '../../modelApi/format/pendingFormat'; -import { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; -import { +import type { Entity } from 'roosterjs-editor-types'; +import type { ContentModelContentChangedEventData } from '../../publicTypes/event/ContentModelContentChangedEvent'; +import type { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; +import type { ContentModelFormatter, FormatWithContentModelContext, FormatWithContentModelOptions, } from '../../publicTypes/parameter/FormatWithContentModelContext'; +import type { DOMSelection } from 'roosterjs-content-model-types'; /** * The general API to do format change with Content Model @@ -33,22 +35,22 @@ export function formatWithContentModel( selectionOverride, } = options || {}; - editor.focus(); - const model = editor.createContentModel(undefined /*option*/, selectionOverride); const context: FormatWithContentModelContext = { newEntities: [], deletedEntities: [], rawEvent, + newImages: [], }; - let rangeEx: SelectionRangeEx | undefined; + let selection: DOMSelection | undefined; if (formatter(model, context)) { const writeBack = () => { handleNewEntities(editor, context); handleDeletedEntities(editor, context); + handleImages(editor, context); - rangeEx = + selection = editor.setContentModel(model, undefined /*options*/, onNodeCreated) || undefined; if (preservePendingFormat) { @@ -56,7 +58,7 @@ export function formatWithContentModel( const pos = editor.getFocusedPosition(); if (pendingFormat && pos) { - setPendingFormat(editor, pendingFormat, pos); + setPendingFormat(editor, pendingFormat, pos.node, pos.offset); } } }; @@ -76,7 +78,7 @@ export function formatWithContentModel( const eventData: ContentModelContentChangedEventData = { contentModel: model, - rangeEx: rangeEx, + selection: selection, source: changeSource || ChangeSource.Format, data: getChangeData?.(), additionalData: { @@ -103,18 +105,44 @@ function handleDeletedEntities( editor: IContentModelEditor, context: FormatWithContentModelContext ) { - context.deletedEntities.forEach(({ entity, operation }) => { - if (entity.id && entity.type) { - editor.triggerPluginEvent(PluginEventType.EntityOperation, { - entity: { - id: entity.id, - isReadonly: entity.isReadonly, - type: entity.type, - wrapper: entity.wrapper, - }, - operation, - rawEvent: context.rawEvent, + context.deletedEntities.forEach( + ({ + entity: { + wrapper, + entityFormat: { id, entityType, isReadonly }, + }, + operation, + }) => { + if (id && entityType) { + // TODO: Revisit this entity parameter for standalone editor, we may just directly pass ContentModelEntity object instead + const entity: Entity = { + id, + type: entityType, + isReadonly: !!isReadonly, + wrapper, + }; + editor.triggerPluginEvent(PluginEventType.EntityOperation, { + entity, + operation, + rawEvent: context.rawEvent, + }); + } + } + ); +} + +function handleImages(editor: IContentModelEditor, context: FormatWithContentModelContext) { + if (context.newImages.length > 0) { + const viewport = editor.getVisibleViewport(); + if (viewport) { + const { top, bottom, left, right } = viewport; + const minMaxImageSize = 10; + const maxWidth = Math.max(right - left, minMaxImageSize); + const maxHeight = Math.max(bottom - top, minMaxImageSize); + context.newImages.forEach(image => { + image.format.maxHeight = `${maxHeight}px`; + image.format.maxWidth = `${maxWidth}px`; }); } - }); + } } diff --git a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/utils/paste.ts b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/utils/paste.ts index 3df09920465..bb85ab324c5 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/publicApi/utils/paste.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/publicApi/utils/paste.ts @@ -1,34 +1,30 @@ import getSelectedSegments from '../selection/getSelectedSegments'; -import { ContentModelDocument, ContentModelSegmentFormat } from 'roosterjs-content-model-types'; +import { ChangeSource, GetContentMode, PasteType, PluginEventType } from 'roosterjs-editor-types'; import { formatWithContentModel } from './formatWithContentModel'; -import { FormatWithContentModelContext } from '../../publicTypes/parameter/FormatWithContentModelContext'; -import { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; import { mergeModel } from '../../modelApi/common/mergeModel'; -import { NodePosition } from 'roosterjs-editor-types'; +import type { + ContentModelDocument, + ContentModelSegmentFormat, +} from 'roosterjs-content-model-types'; +import type { FormatWithContentModelContext } from '../../publicTypes/parameter/FormatWithContentModelContext'; +import type { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; +import type { ClipboardData } from 'roosterjs-editor-types'; import { applySegmentFormatToElement, createDomToModelContext, domToContentModel, + moveChildNodes, } from 'roosterjs-content-model-dom'; -import ContentModelBeforePasteEvent, { - ContentModelBeforePasteEventData, -} from '../../publicTypes/event/ContentModelBeforePasteEvent'; +import type { ContentModelBeforePasteEventData } from '../../publicTypes/event/ContentModelBeforePasteEvent'; +import type ContentModelBeforePasteEvent from '../../publicTypes/event/ContentModelBeforePasteEvent'; import { createDefaultHtmlSanitizerOptions, getPasteType, handleImagePaste, handleTextPaste, - moveChildNodes, retrieveMetadataFromClipboard, sanitizePasteContent, } from 'roosterjs-editor-dom'; -import { - ChangeSource, - ClipboardData, - GetContentMode, - PasteType, - PluginEventType, -} from 'roosterjs-editor-types'; /** * Paste into editor using a clipboardData object @@ -52,6 +48,8 @@ export default function paste( clipboardData.snapshotBeforePaste = editor.getContent(GetContentMode.RawHTMLWithSelection); } + editor.focus(); + formatWithContentModel( editor, 'Paste', @@ -71,7 +69,6 @@ export default function paste( } = triggerPluginEventAndCreatePasteFragment( editor, clipboardData, - null /* position */, pasteAsText, pasteAsImage, eventData, @@ -162,7 +159,6 @@ function createBeforePasteEventData( function triggerPluginEventAndCreatePasteFragment( editor: IContentModelEditor, clipboardData: ClipboardData, - position: NodePosition | null, pasteAsText: boolean, pasteAsImage: boolean, eventData: ContentModelBeforePasteEventData, @@ -192,7 +188,7 @@ function triggerPluginEventAndCreatePasteFragment( moveChildNodes(fragment, doc?.body); } else if (text) { // Paste text - handleTextPaste(text, position, fragment); + handleTextPaste(text, null /*position*/, fragment); } const formatContainer = fragment.ownerDocument.createElement('span'); @@ -213,7 +209,7 @@ function triggerPluginEventAndCreatePasteFragment( } // Step 5. Sanitize the fragment before paste to make sure the content is safe - sanitizePasteContent(event, position); + sanitizePasteContent(event, null /*position*/); return pluginEvent; } diff --git a/packages-content-model/roosterjs-content-model-editor/lib/publicTypes/ContentModelEditorCore.ts b/packages-content-model/roosterjs-content-model-editor/lib/publicTypes/ContentModelEditorCore.ts index cab3015c2bb..9f0954cbeb2 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/publicTypes/ContentModelEditorCore.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/publicTypes/ContentModelEditorCore.ts @@ -1,8 +1,8 @@ -import { ContentModelPluginState } from './pluginState/ContentModelPluginState'; -import { CoreApiMap, EditorCore, SelectionRangeEx } from 'roosterjs-editor-types'; -import { +import type { ContentModelPluginState } from './pluginState/ContentModelPluginState'; +import type { CoreApiMap, EditorCore } from 'roosterjs-editor-types'; +import type { ContentModelDocument, - ContentModelSegmentFormat, + DOMSelection, DomToModelOption, DomToModelSettings, EditorContext, @@ -26,11 +26,17 @@ export type CreateEditorContext = (core: ContentModelEditorCore) => EditorContex export type CreateContentModel = ( core: ContentModelEditorCore, option?: DomToModelOption, - selectionOverride?: SelectionRangeEx + selectionOverride?: DOMSelection ) => ContentModelDocument; /** - * Set content with content model + * Get current DOM selection from editor + * @param core The ContentModelEditorCore object + */ +export type GetDOMSelection = (core: ContentModelEditorCore) => DOMSelection | null; + +/** + * Set content with content model. This is the replacement of core API getSelectionRangeEx * @param core The ContentModelEditorCore object * @param model The content model to set * @param option Additional options to customize the behavior of Content Model to DOM conversion @@ -41,7 +47,14 @@ export type SetContentModel = ( model: ContentModelDocument, option?: ModelToDomOption, onNodeCreated?: OnNodeCreated -) => SelectionRangeEx | null; +) => DOMSelection | null; + +/** + * Set current DOM selection from editor. This is the replacement of core API select + * @param core The ContentModelEditorCore object + * @param selection The selection to set + */ +export type SetDOMSelection = (core: ContentModelEditorCore, selection: DOMSelection) => void; /** * The interface for the map of core API for Content Model editor. @@ -61,6 +74,12 @@ export interface ContentModelCoreApiMap extends CoreApiMap { */ createContentModel: CreateContentModel; + /** + * Get current DOM selection from editor + * @param core The ContentModelEditorCore object + */ + getDOMSelection: GetDOMSelection; + /** * Set content with content model * @param core The ContentModelEditorCore object @@ -68,6 +87,13 @@ export interface ContentModelCoreApiMap extends CoreApiMap { * @param option Additional options to customize the behavior of Content Model to DOM conversion */ setContentModel: SetContentModel; + + /** + * Set current DOM selection from editor. This is the replacement of core API select + * @param core The ContentModelEditorCore object + * @param selection The selection to set + */ + setDOMSelection: SetDOMSelection; } /** @@ -84,11 +110,6 @@ export interface ContentModelEditorCore extends EditorCore, ContentModelPluginSt */ readonly originalApi: ContentModelCoreApiMap; - /** - * Default format used by Content Model. This is calculated from lifecycle.defaultFormat - */ - defaultFormat: ContentModelSegmentFormat; - /** * Default DOM to Content Model options */ @@ -110,9 +131,4 @@ export interface ContentModelEditorCore extends EditorCore, ContentModelPluginSt * will be used for setting content model if there is no other customized options */ defaultModelToDomConfig: ModelToDomSettings; - - /** - * Whether adding delimiter for entity is allowed - */ - addDelimiterForEntity: boolean; } 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 a66976dbbc8..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 @@ -1,7 +1,7 @@ -import { EditorOptions, IEditor, SelectionRangeEx } from 'roosterjs-editor-types'; -import { +import type { EditorOptions, IEditor } from 'roosterjs-editor-types'; +import type { ContentModelDocument, - ContentModelSegmentFormat, + DOMSelection, DomToModelOption, ModelToDomOption, OnNodeCreated, @@ -21,7 +21,7 @@ export interface IContentModelEditor extends IEditor { */ createContentModel( option?: DomToModelOption, - selectionOverride?: SelectionRangeEx + selectionOverride?: DOMSelection ): ContentModelDocument; /** @@ -34,19 +34,20 @@ export interface IContentModelEditor extends IEditor { model: ContentModelDocument, option?: ModelToDomOption, onNodeCreated?: OnNodeCreated - ): SelectionRangeEx | null; + ): DOMSelection | null; /** - * Notify editor the current cache may be invalid + * Get current DOM selection. + * This is the replacement of IEditor.getSelectionRangeEx. */ - invalidateCache(): void; + getDOMSelection(): DOMSelection | null; /** - * Get default format as ContentModelSegmentFormat. - * This is a replacement of IEditor.getDefaultFormat for Content Model. - * @returns The default format + * Set DOMSelection into editor content. + * This is the replacement of IEditor.select. + * @param selection The selection to set */ - getContentModelDefaultFormat(): ContentModelSegmentFormat; + setDOMSelection(selection: DOMSelection): void; } /** @@ -62,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/lib/publicTypes/event/ContentModelBeforePasteEvent.ts b/packages-content-model/roosterjs-content-model-editor/lib/publicTypes/event/ContentModelBeforePasteEvent.ts index fdb3aee34c6..3c6a526740d 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/publicTypes/event/ContentModelBeforePasteEvent.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/publicTypes/event/ContentModelBeforePasteEvent.ts @@ -1,5 +1,5 @@ -import { ContentModelDocument, DomToModelOption } from 'roosterjs-content-model-types'; -import { +import type { ContentModelDocument, DomToModelOption } from 'roosterjs-content-model-types'; +import type { BeforePasteEvent, BeforePasteEventData, CompatibleBeforePasteEvent, diff --git a/packages-content-model/roosterjs-content-model-editor/lib/publicTypes/event/ContentModelContentChangedEvent.ts b/packages-content-model/roosterjs-content-model-editor/lib/publicTypes/event/ContentModelContentChangedEvent.ts index debe522cd9a..9b0e082335d 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/publicTypes/event/ContentModelContentChangedEvent.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/publicTypes/event/ContentModelContentChangedEvent.ts @@ -1,9 +1,8 @@ -import { ContentModelDocument } from 'roosterjs-content-model-types'; -import { +import type { ContentModelDocument, DOMSelection } from 'roosterjs-content-model-types'; +import type { CompatibleContentChangedEvent, ContentChangedEvent, ContentChangedEventData, - SelectionRangeEx, } from 'roosterjs-editor-types'; /** @@ -18,7 +17,7 @@ export interface ContentModelContentChangedEventData extends ContentChangedEvent /** * Selection range applied to the document */ - rangeEx?: SelectionRangeEx; + selection?: DOMSelection; } /** diff --git a/packages-content-model/roosterjs-content-model-editor/lib/publicTypes/format/formatState/ContentModelFormatState.ts b/packages-content-model/roosterjs-content-model-editor/lib/publicTypes/format/formatState/ContentModelFormatState.ts index 82eb6de52f7..bb7c11f71d7 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/publicTypes/format/formatState/ContentModelFormatState.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/publicTypes/format/formatState/ContentModelFormatState.ts @@ -1,5 +1,5 @@ -import { FormatState } from 'roosterjs-editor-types'; -import { ImageFormatState } from './ImageFormatState'; +import type { FormatState } from 'roosterjs-editor-types'; +import type { ImageFormatState } from './ImageFormatState'; /** * The format object state in Content Model diff --git a/packages-content-model/roosterjs-content-model-editor/lib/publicTypes/parameter/FormatWithContentModelContext.ts b/packages-content-model/roosterjs-content-model-editor/lib/publicTypes/parameter/FormatWithContentModelContext.ts index 37e7b13284e..ed42831d41a 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/publicTypes/parameter/FormatWithContentModelContext.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/publicTypes/parameter/FormatWithContentModelContext.ts @@ -1,7 +1,9 @@ -import { EntityOperation, SelectionRangeEx } from 'roosterjs-editor-types'; -import { +import type { EntityOperation } from 'roosterjs-editor-types'; +import type { ContentModelDocument, ContentModelEntity, + ContentModelImage, + DOMSelection, OnNodeCreated, } from 'roosterjs-content-model-types'; import type { CompatibleEntityOperation } from 'roosterjs-editor-types/lib/compatibleTypes'; @@ -34,6 +36,11 @@ export interface FormatWithContentModelContext { */ readonly deletedEntities: DeletedEntity[]; + /** + * Images inserted in the editor that needs to have their size adjusted + */ + readonly newImages: ContentModelImage[]; + /** * Raw Event that triggers this format call */ @@ -81,7 +88,7 @@ export interface FormatWithContentModelOptions { /** * When specified, use this selection range to override current selection inside editor */ - selectionOverride?: SelectionRangeEx; + selectionOverride?: DOMSelection; } /** diff --git a/packages-content-model/roosterjs-content-model-editor/lib/publicTypes/pluginState/ContentModelCachePluginState.ts b/packages-content-model/roosterjs-content-model-editor/lib/publicTypes/pluginState/ContentModelCachePluginState.ts index ee20ac33156..807d2bb7de2 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/publicTypes/pluginState/ContentModelCachePluginState.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/publicTypes/pluginState/ContentModelCachePluginState.ts @@ -1,17 +1,25 @@ -import { ContentModelDocument } from 'roosterjs-content-model-types'; -import { SelectionRangeEx } from 'roosterjs-editor-types'; +import type { + ContentModelDocument, + ContentModelDomIndexer, + DOMSelection, +} from 'roosterjs-content-model-types'; /** * Plugin state for ContentModelEditPlugin */ export interface ContentModelCachePluginState { /** - * Cached selection range + * Cached selection */ - cachedRangeEx?: SelectionRangeEx | undefined; + cachedSelection?: DOMSelection | undefined; /** * When reuse Content Model is allowed, we cache the Content Model object here after created */ cachedModel?: ContentModelDocument; + + /** + * @optional Indexer for content model, to help build backward relationship from DOM node to Content Model + */ + domIndexer?: ContentModelDomIndexer; } diff --git a/packages-content-model/roosterjs-content-model-editor/lib/publicTypes/pluginState/ContentModelFormatPluginState.ts b/packages-content-model/roosterjs-content-model-editor/lib/publicTypes/pluginState/ContentModelFormatPluginState.ts new file mode 100644 index 00000000000..81444445060 --- /dev/null +++ b/packages-content-model/roosterjs-content-model-editor/lib/publicTypes/pluginState/ContentModelFormatPluginState.ts @@ -0,0 +1,11 @@ +import type { ContentModelSegmentFormat } from 'roosterjs-content-model-types'; + +/** + * Plugin state for ContentModelFormatPlugin + */ +export interface ContentModelFormatPluginState { + /** + * Default format of this editor + */ + defaultFormat: ContentModelSegmentFormat; +} diff --git a/packages-content-model/roosterjs-content-model-editor/lib/publicTypes/pluginState/ContentModelPluginState.ts b/packages-content-model/roosterjs-content-model-editor/lib/publicTypes/pluginState/ContentModelPluginState.ts index 63e8ccd721d..b31f0382035 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/publicTypes/pluginState/ContentModelPluginState.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/publicTypes/pluginState/ContentModelPluginState.ts @@ -1,5 +1,6 @@ -import { ContentModelCachePluginState } from './ContentModelCachePluginState'; -import { CopyPastePluginState } from 'roosterjs-editor-types'; +import type { CopyPastePluginState } from 'roosterjs-editor-types'; +import type { ContentModelCachePluginState } from './ContentModelCachePluginState'; +import type { ContentModelFormatPluginState } from './ContentModelFormatPluginState'; /** * Temporary core plugin state for Content Model editor @@ -15,4 +16,9 @@ export interface ContentModelPluginState { * Plugin state for ContentModelCopyPastePlugin */ copyPaste: CopyPastePluginState; + + /** + * Plugin state for ContentModelFormatPlugin + */ + format: ContentModelFormatPluginState; } diff --git a/packages-content-model/roosterjs-content-model-editor/lib/publicTypes/selection/InsertPoint.ts b/packages-content-model/roosterjs-content-model-editor/lib/publicTypes/selection/InsertPoint.ts index 272dbf96f09..6b740d2278d 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/publicTypes/selection/InsertPoint.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/publicTypes/selection/InsertPoint.ts @@ -1,5 +1,5 @@ -import { TableSelectionContext } from './TableSelectionContext'; -import { +import type { TableSelectionContext } from './TableSelectionContext'; +import type { ContentModelBlockGroup, ContentModelParagraph, ContentModelSelectionMarker, diff --git a/packages-content-model/roosterjs-content-model-editor/lib/publicTypes/selection/TableSelectionContext.ts b/packages-content-model/roosterjs-content-model-editor/lib/publicTypes/selection/TableSelectionContext.ts index 3cfd44be2c5..0cfc059f5ba 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/publicTypes/selection/TableSelectionContext.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/publicTypes/selection/TableSelectionContext.ts @@ -1,4 +1,4 @@ -import { ContentModelTable } from 'roosterjs-content-model-types'; +import type { ContentModelTable } from 'roosterjs-content-model-types'; /** * Context object for table in a selection diff --git a/packages-content-model/roosterjs-content-model-editor/test/editor/ContentModelEditorTest.ts b/packages-content-model/roosterjs-content-model-editor/test/editor/ContentModelEditorTest.ts index dd834130598..5b082f10a81 100644 --- a/packages-content-model/roosterjs-content-model-editor/test/editor/ContentModelEditorTest.ts +++ b/packages-content-model/roosterjs-content-model-editor/test/editor/ContentModelEditorTest.ts @@ -4,7 +4,7 @@ import * as createModelToDomContext from 'roosterjs-content-model-dom/lib/modelT import * as domToContentModel from 'roosterjs-content-model-dom/lib/domToModel/domToContentModel'; import ContentModelEditor from '../../lib/editor/ContentModelEditor'; import { ContentModelDocument, EditorContext } from 'roosterjs-content-model-types'; -import { EditorPlugin, PluginEventType, SelectionRangeTypes } from 'roosterjs-editor-types'; +import { EditorPlugin, PluginEventType } from 'roosterjs-editor-types'; const editorContext: EditorContext = { isDarkMode: false, @@ -32,11 +32,11 @@ describe('ContentModelEditor', () => { expect(model).toBe(mockedResult); expect(domToContentModel.domToContentModel).toHaveBeenCalledTimes(1); - expect(domToContentModel.domToContentModel).toHaveBeenCalledWith(div, mockedContext, { - type: SelectionRangeTypes.Normal, - ranges: [], - areAllCollapsed: true, - }); + expect(domToContentModel.domToContentModel).toHaveBeenCalledWith( + div, + mockedContext, + undefined + ); expect(createDomToModelContext.createDomToModelContextWithConfig).toHaveBeenCalledWith( mockedConfig, editorContext @@ -63,11 +63,11 @@ describe('ContentModelEditor', () => { expect(model).toBe(mockedResult); expect(domToContentModel.domToContentModel).toHaveBeenCalledTimes(1); - expect(domToContentModel.domToContentModel).toHaveBeenCalledWith(div, mockedContext, { - type: SelectionRangeTypes.Normal, - ranges: [], - areAllCollapsed: true, - }); + expect(domToContentModel.domToContentModel).toHaveBeenCalledWith( + div, + mockedContext, + undefined + ); expect(createDomToModelContext.createDomToModelContextWithConfig).toHaveBeenCalledWith( mockedConfig, editorContext @@ -76,8 +76,8 @@ describe('ContentModelEditor', () => { it('setContentModel with normal selection', () => { const mockedRange = { - type: SelectionRangeTypes.Normal, - ranges: [document.createRange()], + type: 'range', + range: document.createRange(), } as any; const mockedModel = 'MockedModel' as any; const mockedContext = 'MockedContext' as any; @@ -94,7 +94,7 @@ describe('ContentModelEditor', () => { spyOn((editor as any).core.api, 'createEditorContext').and.returnValue(editorContext); - const rangeEx = editor.setContentModel(mockedModel); + const selection = editor.setContentModel(mockedModel); expect(contentModelToDom.contentModelToDom).toHaveBeenCalledTimes(1); expect(contentModelToDom.contentModelToDom).toHaveBeenCalledWith( @@ -108,13 +108,13 @@ describe('ContentModelEditor', () => { mockedConfig, editorContext ); - expect(rangeEx).toBe(mockedRange); + expect(selection).toBe(mockedRange); }); it('setContentModel', () => { const mockedRange = { - type: SelectionRangeTypes.Normal, - ranges: [document.createRange()], + type: 'range', + range: document.createRange(), } as any; const mockedModel = 'MockedModel' as any; const mockedContext = 'MockedContext' as any; @@ -131,7 +131,7 @@ describe('ContentModelEditor', () => { spyOn((editor as any).core.api, 'createEditorContext').and.returnValue(editorContext); - const rangeEx = editor.setContentModel(mockedModel); + const selection = editor.setContentModel(mockedModel); expect(contentModelToDom.contentModelToDom).toHaveBeenCalledTimes(1); expect(contentModelToDom.contentModelToDom).toHaveBeenCalledWith( @@ -145,7 +145,7 @@ describe('ContentModelEditor', () => { mockedConfig, editorContext ); - expect(rangeEx).toBe(mockedRange); + expect(selection).toBe(mockedRange); }); it('createContentModel in EditorReady event', () => { @@ -247,27 +247,4 @@ describe('ContentModelEditor', () => { expect(div.style.fontFamily).toBe('Arial'); }); - - it('getContentModelDefaultFormat', () => { - const div = document.createElement('div'); - const editor = new ContentModelEditor(div, { - defaultFormat: { - fontFamily: 'Tahoma', - fontSize: '20pt', - }, - }); - const format = editor.getContentModelDefaultFormat(); - - editor.dispose(); - - expect(format).toEqual({ - fontWeight: undefined, - italic: undefined, - underline: undefined, - fontFamily: 'Tahoma', - fontSize: '20pt', - textColor: undefined, - backgroundColor: undefined, - }); - }); }); diff --git a/packages-content-model/roosterjs-content-model-editor/test/editor/coreApi/createContentModelTest.ts b/packages-content-model/roosterjs-content-model-editor/test/editor/coreApi/createContentModelTest.ts index 61cf54bfa8c..ca87e4d8305 100644 --- a/packages-content-model/roosterjs-content-model-editor/test/editor/coreApi/createContentModelTest.ts +++ b/packages-content-model/roosterjs-content-model-editor/test/editor/coreApi/createContentModelTest.ts @@ -3,7 +3,6 @@ import * as createDomToModelContext from 'roosterjs-content-model-dom/lib/domToM import * as domToContentModel from 'roosterjs-content-model-dom/lib/domToModel/domToContentModel'; import { ContentModelEditorCore } from '../../../lib/publicTypes/ContentModelEditorCore'; import { createContentModel } from '../../../lib/editor/coreApi/createContentModel'; -import { SelectionRangeTypes } from 'roosterjs-editor-types'; const mockedEditorContext = 'EDITORCONTEXT' as any; const mockedContext = 'CONTEXT' as any; @@ -15,7 +14,7 @@ const mockedClonedModel = 'CLONEDMODEL' as any; describe('createContentModel', () => { let core: ContentModelEditorCore; let createEditorContext: jasmine.Spy; - let getSelectionRangeEx: jasmine.Spy; + let getDOMSelection: jasmine.Spy; let domToContentModelSpy: jasmine.Spy; let cloneModelSpy: jasmine.Spy; @@ -23,7 +22,7 @@ describe('createContentModel', () => { createEditorContext = jasmine .createSpy('createEditorContext') .and.returnValue(mockedEditorContext); - getSelectionRangeEx = jasmine.createSpy('getSelectionRangeEx').and.returnValue(null); + getDOMSelection = jasmine.createSpy('getDOMSelection').and.returnValue(null); domToContentModelSpy = spyOn(domToContentModel, 'domToContentModel').and.returnValue( mockedModel @@ -38,7 +37,7 @@ describe('createContentModel', () => { contentDiv: mockedDiv, api: { createEditorContext, - getSelectionRangeEx, + getDOMSelection, }, cache: { cachedModel: mockedCachedMode, @@ -53,8 +52,8 @@ describe('createContentModel', () => { const model = createContentModel(core); expect(createEditorContext).toHaveBeenCalledWith(core); - expect(getSelectionRangeEx).toHaveBeenCalledWith(core); - expect(domToContentModelSpy).toHaveBeenCalledWith(mockedDiv, mockedContext, null); + expect(getDOMSelection).toHaveBeenCalledWith(core); + expect(domToContentModelSpy).toHaveBeenCalledWith(mockedDiv, mockedContext, undefined); expect(model).toBe(mockedModel); }); @@ -62,7 +61,7 @@ describe('createContentModel', () => { const model = createContentModel(core); expect(createEditorContext).not.toHaveBeenCalled(); - expect(getSelectionRangeEx).not.toHaveBeenCalled(); + expect(getDOMSelection).not.toHaveBeenCalled(); expect(domToContentModelSpy).not.toHaveBeenCalled(); expect(model).toBe(mockedCachedMode); }); @@ -76,21 +75,21 @@ describe('createContentModel', () => { includeCachedElement: true, }); expect(createEditorContext).not.toHaveBeenCalled(); - expect(getSelectionRangeEx).not.toHaveBeenCalled(); + expect(getDOMSelection).not.toHaveBeenCalled(); expect(domToContentModelSpy).not.toHaveBeenCalled(); expect(model).toBe(mockedClonedModel); }); }); describe('createContentModel with selection', () => { - let getSelectionRangeExSpy: jasmine.Spy; + let getDOMSelectionSpy: jasmine.Spy; let domToContentModelSpy: jasmine.Spy; let createEditorContextSpy: jasmine.Spy; let core: any; const MockedDiv = 'CONTENT_DIV' as any; beforeEach(() => { - getSelectionRangeExSpy = jasmine.createSpy('getSelectionRangeEx'); + getDOMSelectionSpy = jasmine.createSpy('getDOMSelection'); domToContentModelSpy = spyOn(domToContentModel, 'domToContentModel'); createEditorContextSpy = jasmine.createSpy('createEditorContext'); @@ -101,7 +100,7 @@ describe('createContentModel with selection', () => { core = { contentDiv: MockedDiv, api: { - getSelectionRangeEx: getSelectionRangeExSpy, + getDOMSelection: getDOMSelectionSpy, createEditorContext: createEditorContextSpy, }, cache: {}, @@ -115,17 +114,17 @@ describe('createContentModel with selection', () => { commonAncestorContainer: MockedContainer, } as any; - getSelectionRangeExSpy.and.returnValue({ - type: SelectionRangeTypes.Normal, - ranges: [MockedRange], + getDOMSelectionSpy.and.returnValue({ + type: 'range', + range: MockedRange, }); createContentModel(core); expect(domToContentModelSpy).toHaveBeenCalledTimes(1); expect(domToContentModelSpy).toHaveBeenCalledWith(MockedDiv, mockedContext, { - type: SelectionRangeTypes.Normal, - ranges: [MockedRange], + type: 'range', + range: MockedRange, } as any); }); @@ -134,8 +133,8 @@ describe('createContentModel with selection', () => { const MockedFirstCell = { name: 'FirstCell' }; const MockedLastCell = { name: 'LastCell' }; - getSelectionRangeExSpy.and.returnValue({ - type: SelectionRangeTypes.TableSelection, + getDOMSelectionSpy.and.returnValue({ + type: 'table', table: MockedContainer, coordinates: { firstCell: MockedFirstCell, @@ -147,7 +146,7 @@ describe('createContentModel with selection', () => { expect(domToContentModelSpy).toHaveBeenCalledTimes(1); expect(domToContentModelSpy).toHaveBeenCalledWith(MockedDiv, mockedContext, { - type: SelectionRangeTypes.TableSelection, + type: 'table', table: MockedContainer, coordinates: { firstCell: MockedFirstCell, @@ -159,8 +158,8 @@ describe('createContentModel with selection', () => { it('Image selection', () => { const MockedContainer = 'MockedContainer'; - getSelectionRangeExSpy.and.returnValue({ - type: SelectionRangeTypes.ImageSelection, + getDOMSelectionSpy.and.returnValue({ + type: 'image', image: MockedContainer, }); @@ -168,36 +167,36 @@ describe('createContentModel with selection', () => { expect(domToContentModelSpy).toHaveBeenCalledTimes(1); expect(domToContentModelSpy).toHaveBeenCalledWith(MockedDiv, mockedContext, { - type: SelectionRangeTypes.ImageSelection, + type: 'image', image: MockedContainer, } as any); }); it('Incorrect regular selection', () => { - getSelectionRangeExSpy.and.returnValue({ - type: SelectionRangeTypes.Normal, - ranges: [], + getDOMSelectionSpy.and.returnValue({ + type: 'range', + range: null!, }); createContentModel(core); expect(domToContentModelSpy).toHaveBeenCalledTimes(1); expect(domToContentModelSpy).toHaveBeenCalledWith(MockedDiv, mockedContext, { - type: SelectionRangeTypes.Normal, - ranges: [], + type: 'range', + range: null!, } as any); }); it('Incorrect table selection', () => { - getSelectionRangeExSpy.and.returnValue({ - type: SelectionRangeTypes.TableSelection, + getDOMSelectionSpy.and.returnValue({ + type: 'table', }); createContentModel(core); expect(domToContentModelSpy).toHaveBeenCalledTimes(1); expect(domToContentModelSpy).toHaveBeenCalledWith(MockedDiv, mockedContext, { - type: SelectionRangeTypes.TableSelection, + type: 'table', } as any); }); }); diff --git a/packages-content-model/roosterjs-content-model-editor/test/editor/coreApi/createEditorContextTest.ts b/packages-content-model/roosterjs-content-model-editor/test/editor/coreApi/createEditorContextTest.ts index f0b6708b0a3..6a4fbc1bfa7 100644 --- a/packages-content-model/roosterjs-content-model-editor/test/editor/coreApi/createEditorContextTest.ts +++ b/packages-content-model/roosterjs-content-model-editor/test/editor/coreApi/createEditorContextTest.ts @@ -6,7 +6,6 @@ describe('createEditorContext', () => { const isDarkMode = 'DARKMODE' as any; const defaultFormat = 'DEFAULTFORMAT' as any; const darkColorHandler = 'DARKHANDLER' as any; - const addDelimiterForEntity = 'ADDDELIMITER' as any; const getComputedStyleSpy = jasmine.createSpy('getComputedStyleSpy'); const getBoundingClientRectSpy = jasmine.createSpy('getBoundingClientRect'); @@ -24,9 +23,54 @@ describe('createEditorContext', () => { lifecycle: { isDarkMode, }, + format: { + defaultFormat, + }, + darkColorHandler, + cache: {}, + } as any) as ContentModelEditorCore; + + const context = createEditorContext(core); + + expect(context).toEqual({ + isDarkMode, + darkColorHandler, defaultFormat, + addDelimiterForEntity: true, + allowCacheElement: true, + domIndexer: undefined, + }); + }); + + it('create a normal context with domIndexer', () => { + const isDarkMode = 'DARKMODE' as any; + const defaultFormat = 'DEFAULTFORMAT' as any; + const darkColorHandler = 'DARKHANDLER' as any; + const getComputedStyleSpy = jasmine.createSpy('getComputedStyleSpy'); + const getBoundingClientRectSpy = jasmine.createSpy('getBoundingClientRect'); + const domIndexer = 'DOMINDEXER' as any; + + const div = { + ownerDocument: { + defaultView: { + getComputedStyle: getComputedStyleSpy, + }, + }, + getBoundingClientRect: getBoundingClientRectSpy, + }; + + const core = ({ + contentDiv: div, + lifecycle: { + isDarkMode, + }, + format: { + defaultFormat, + }, darkColorHandler, - addDelimiterForEntity, + cache: { + domIndexer, + }, } as any) as ContentModelEditorCore; const context = createEditorContext(core); @@ -35,8 +79,9 @@ describe('createEditorContext', () => { isDarkMode, darkColorHandler, defaultFormat, - addDelimiterForEntity, + addDelimiterForEntity: true, allowCacheElement: true, + domIndexer, }); }); }); @@ -49,7 +94,6 @@ describe('createEditorContext - checkZoomScale', () => { const isDarkMode = 'DARKMODE' as any; const defaultFormat = 'DEFAULTFORMAT' as any; const darkColorHandler = 'DARKHANDLER' as any; - const addDelimiterForEntity = 'ADDDELIMITER' as any; beforeEach(() => { getComputedStyleSpy = jasmine.createSpy('getComputedStyleSpy'); @@ -68,9 +112,11 @@ describe('createEditorContext - checkZoomScale', () => { lifecycle: { isDarkMode, }, - defaultFormat, + format: { + defaultFormat, + }, darkColorHandler, - addDelimiterForEntity, + cache: {}, } as any) as ContentModelEditorCore; }); @@ -86,9 +132,10 @@ describe('createEditorContext - checkZoomScale', () => { isDarkMode, defaultFormat, darkColorHandler, - addDelimiterForEntity, + addDelimiterForEntity: true, zoomScale: 1, allowCacheElement: true, + domIndexer: undefined, }); }); @@ -104,9 +151,10 @@ describe('createEditorContext - checkZoomScale', () => { isDarkMode, defaultFormat, darkColorHandler, - addDelimiterForEntity, + addDelimiterForEntity: true, zoomScale: 2, allowCacheElement: true, + domIndexer: undefined, }); }); @@ -122,9 +170,10 @@ describe('createEditorContext - checkZoomScale', () => { isDarkMode, defaultFormat, darkColorHandler, - addDelimiterForEntity, + addDelimiterForEntity: true, zoomScale: 0.5, allowCacheElement: true, + domIndexer: undefined, }); }); }); @@ -137,7 +186,6 @@ describe('createEditorContext - checkRootDir', () => { const isDarkMode = 'DARKMODE' as any; const defaultFormat = 'DEFAULTFORMAT' as any; const darkColorHandler = 'DARKHANDLER' as any; - const addDelimiterForEntity = 'ADDDELIMITER' as any; beforeEach(() => { getComputedStyleSpy = jasmine.createSpy('getComputedStyleSpy'); @@ -156,9 +204,11 @@ describe('createEditorContext - checkRootDir', () => { lifecycle: { isDarkMode, }, - defaultFormat, + format: { + defaultFormat, + }, darkColorHandler, - addDelimiterForEntity, + cache: {}, } as any) as ContentModelEditorCore; }); @@ -173,8 +223,9 @@ describe('createEditorContext - checkRootDir', () => { isDarkMode, defaultFormat, darkColorHandler, - addDelimiterForEntity, + addDelimiterForEntity: true, allowCacheElement: true, + domIndexer: undefined, }); }); @@ -189,9 +240,10 @@ describe('createEditorContext - checkRootDir', () => { isDarkMode, defaultFormat, darkColorHandler, - addDelimiterForEntity, + addDelimiterForEntity: true, isRootRtl: true, allowCacheElement: true, + domIndexer: undefined, }); }); }); diff --git a/packages-content-model/roosterjs-content-model-editor/test/editor/coreApi/setContentModelTest.ts b/packages-content-model/roosterjs-content-model-editor/test/editor/coreApi/setContentModelTest.ts index 91afa5bd6b9..c7d5198daef 100644 --- a/packages-content-model/roosterjs-content-model-editor/test/editor/coreApi/setContentModelTest.ts +++ b/packages-content-model/roosterjs-content-model-editor/test/editor/coreApi/setContentModelTest.ts @@ -17,8 +17,8 @@ describe('setContentModel', () => { let createEditorContext: jasmine.Spy; let createModelToDomContextSpy: jasmine.Spy; let createModelToDomContextWithConfigSpy: jasmine.Spy; - let select: jasmine.Spy; - let getSelectionRange: jasmine.Spy; + let setDOMSelectionSpy: jasmine.Spy; + let getDOMSelectionSpy: jasmine.Spy; beforeEach(() => { contentModelToDomSpy = spyOn(contentModelToDom, 'contentModelToDom').and.returnValue( @@ -35,15 +35,15 @@ describe('setContentModel', () => { createModelToDomContext, 'createModelToDomContextWithConfig' ).and.returnValue(mockedContext); - select = jasmine.createSpy('select'); - getSelectionRange = jasmine.createSpy('getSelectionRange'); + setDOMSelectionSpy = jasmine.createSpy('setDOMSelection'); + getDOMSelectionSpy = jasmine.createSpy('getDOMSelection'); core = ({ contentDiv: mockedDiv, api: { createEditorContext, - select, - getSelectionRange, + setDOMSelection: setDOMSelectionSpy, + getDOMSelection: getDOMSelectionSpy, }, lifecycle: {}, defaultModelToDomConfig: mockedConfig, @@ -66,7 +66,9 @@ describe('setContentModel', () => { mockedContext, undefined ); - expect(select).toHaveBeenCalledWith(core, mockedRange); + expect(setDOMSelectionSpy).toHaveBeenCalledWith(core, mockedRange); + expect(core.cache.cachedSelection).toBe(mockedRange); + expect(core.cache.cachedModel).toBe(mockedModel); }); it('with default option, no shadow edit', () => { @@ -83,7 +85,7 @@ describe('setContentModel', () => { mockedContext, undefined ); - expect(select).toHaveBeenCalledWith(core, mockedRange); + expect(setDOMSelectionSpy).toHaveBeenCalledWith(core, mockedRange); }); it('with default option, no shadow edit, with additional option', () => { @@ -105,7 +107,7 @@ describe('setContentModel', () => { mockedContext, undefined ); - expect(select).toHaveBeenCalledWith(core, mockedRange); + expect(setDOMSelectionSpy).toHaveBeenCalledWith(core, mockedRange); }); it('no default option, with shadow edit', () => { @@ -124,6 +126,6 @@ describe('setContentModel', () => { mockedContext, undefined ); - expect(select).not.toHaveBeenCalled(); + expect(setDOMSelectionSpy).not.toHaveBeenCalled(); }); }); diff --git a/packages-content-model/roosterjs-content-model-editor/test/editor/plugins/ContentModelEditPluginTest.ts b/packages-content-model/roosterjs-content-model-editor/test/editor/corePlugins/ContentModelEditPluginTest.ts similarity index 86% rename from packages-content-model/roosterjs-content-model-editor/test/editor/plugins/ContentModelEditPluginTest.ts rename to packages-content-model/roosterjs-content-model-editor/test/editor/corePlugins/ContentModelEditPluginTest.ts index b1a482b0f6a..b945ef3d633 100644 --- a/packages-content-model/roosterjs-content-model-editor/test/editor/plugins/ContentModelEditPluginTest.ts +++ b/packages-content-model/roosterjs-content-model-editor/test/editor/corePlugins/ContentModelEditPluginTest.ts @@ -1,18 +1,14 @@ import * as keyboardDelete from '../../../lib/publicApi/editing/keyboardDelete'; -import ContentModelEditPlugin from '../../../lib/editor/plugins/ContentModelEditPlugin'; +import ContentModelEditPlugin from '../../../lib/editor/corePlugins/ContentModelEditPlugin'; import { EntityOperation, Keys, PluginEventType } from 'roosterjs-editor-types'; import { IContentModelEditor } from '../../../lib/publicTypes/IContentModelEditor'; describe('ContentModelEditPlugin', () => { let editor: IContentModelEditor; - let invalidateCache: jasmine.Spy; beforeEach(() => { - invalidateCache = jasmine.createSpy('invalidateCache'); - editor = ({ - invalidateCache, - getSelectionRangeEx: () => + getDOMSelection: () => ({ type: -1, } as any), // Force return invalid range to go through content model code @@ -38,7 +34,6 @@ describe('ContentModelEditPlugin', () => { }); expect(keyboardDeleteSpy).toHaveBeenCalledWith(editor, rawEvent); - expect(invalidateCache).not.toHaveBeenCalled(); }); it('Delete', () => { @@ -53,7 +48,6 @@ describe('ContentModelEditPlugin', () => { }); expect(keyboardDeleteSpy).toHaveBeenCalledWith(editor, rawEvent); - expect(invalidateCache).not.toHaveBeenCalled(); }); it('Other key', () => { @@ -68,7 +62,6 @@ describe('ContentModelEditPlugin', () => { }); expect(keyboardDeleteSpy).not.toHaveBeenCalled(); - expect(invalidateCache).not.toHaveBeenCalled(); }); it('Default prevented', () => { @@ -82,7 +75,6 @@ describe('ContentModelEditPlugin', () => { }); expect(keyboardDeleteSpy).not.toHaveBeenCalled(); - expect(invalidateCache).toHaveBeenCalled(); }); it('Trigger entity event first', () => { @@ -118,7 +110,6 @@ describe('ContentModelEditPlugin', () => { expect(keyboardDeleteSpy).toHaveBeenCalledWith(editor, { which: Keys.DELETE, } as any); - expect(invalidateCache).not.toHaveBeenCalled(); }); it('SelectionChanged event should clear cached model', () => { @@ -129,8 +120,6 @@ describe('ContentModelEditPlugin', () => { eventType: PluginEventType.SelectionChanged, selectionRangeEx: null!, }); - - expect(invalidateCache).not.toHaveBeenCalled(); }); it('keyboardDelete returns false', () => { @@ -143,8 +132,6 @@ describe('ContentModelEditPlugin', () => { eventType: PluginEventType.SelectionChanged, selectionRangeEx: null!, }); - - expect(invalidateCache).not.toHaveBeenCalled(); }); }); }); diff --git a/packages-content-model/roosterjs-content-model-editor/test/editor/plugins/ContentModelFormatPluginTest.ts b/packages-content-model/roosterjs-content-model-editor/test/editor/corePlugins/ContentModelFormatPluginTest.ts similarity index 83% rename from packages-content-model/roosterjs-content-model-editor/test/editor/plugins/ContentModelFormatPluginTest.ts rename to packages-content-model/roosterjs-content-model-editor/test/editor/corePlugins/ContentModelFormatPluginTest.ts index c882c102b8b..6380c7072cd 100644 --- a/packages-content-model/roosterjs-content-model-editor/test/editor/plugins/ContentModelFormatPluginTest.ts +++ b/packages-content-model/roosterjs-content-model-editor/test/editor/corePlugins/ContentModelFormatPluginTest.ts @@ -1,9 +1,9 @@ import * as formatWithContentModel from '../../../lib/publicApi/utils/formatWithContentModel'; import * as pendingFormat from '../../../lib/modelApi/format/pendingFormat'; -import ContentModelFormatPlugin from '../../../lib/editor/plugins/ContentModelFormatPlugin'; -import { ChangeSource, PluginEventType, SelectionRangeTypes } from 'roosterjs-editor-types'; +import ContentModelFormatPlugin from '../../../lib/editor/corePlugins/ContentModelFormatPlugin'; +import { ChangeSource, PluginEventType } from 'roosterjs-editor-types'; +import { ContentModelFormatPluginState } from '../../../lib/publicTypes/pluginState/ContentModelFormatPluginState'; import { IContentModelEditor } from '../../../lib/publicTypes/IContentModelEditor'; -import { Position } from 'roosterjs-editor-dom'; import { addSegment, createContentModelDocument, @@ -19,10 +19,11 @@ describe('ContentModelFormatPlugin', () => { const editor = ({ cacheContentModel: () => {}, isDarkMode: () => false, - getContentModelDefaultFormat: () => ({}), } as any) as IContentModelEditor; - const plugin = new ContentModelFormatPlugin(); - + const state = { + defaultFormat: {}, + }; + const plugin = new ContentModelFormatPlugin(state); plugin.initialize(editor); plugin.onPluginEvent({ @@ -49,9 +50,11 @@ describe('ContentModelFormatPlugin', () => { setContentModel, isInIME: () => false, cacheContentModel: () => {}, - getContentModelDefaultFormat: () => ({}), } as any) as IContentModelEditor; - const plugin = new ContentModelFormatPlugin(); + const state = { + defaultFormat: {}, + }; + const plugin = new ContentModelFormatPlugin(state); const model = createContentModelDocument(); plugin.initialize(editor); @@ -83,10 +86,11 @@ describe('ContentModelFormatPlugin', () => { createContentModel: () => model, setContentModel, cacheContentModel: () => {}, - getContentModelDefaultFormat: () => ({}), } as any) as IContentModelEditor; - const plugin = new ContentModelFormatPlugin(); - + const state = { + defaultFormat: {}, + }; + const plugin = new ContentModelFormatPlugin(state); plugin.initialize(editor); plugin.onPluginEvent({ eventType: PluginEventType.Input, @@ -116,10 +120,11 @@ describe('ContentModelFormatPlugin', () => { setContentModel, isInIME: () => false, cacheContentModel: () => {}, - getContentModelDefaultFormat: () => ({}), } as any) as IContentModelEditor; - const plugin = new ContentModelFormatPlugin(); - + const state = { + defaultFormat: {}, + }; + const plugin = new ContentModelFormatPlugin(state); plugin.initialize(editor); plugin.onPluginEvent({ eventType: PluginEventType.Input, @@ -158,10 +163,12 @@ describe('ContentModelFormatPlugin', () => { cacheContentModel: () => {}, isDarkMode: () => false, triggerPluginEvent: jasmine.createSpy('triggerPluginEvent'), - getContentModelDefaultFormat: () => ({}), + getVisibleViewport: jasmine.createSpy('getVisibleViewport'), } as any) as IContentModelEditor; - const plugin = new ContentModelFormatPlugin(); - + const state = { + defaultFormat: {}, + }; + const plugin = new ContentModelFormatPlugin(state); plugin.initialize(editor); plugin.onPluginEvent({ eventType: PluginEventType.Input, @@ -208,6 +215,7 @@ describe('ContentModelFormatPlugin', () => { const setContentModel = jasmine.createSpy('setContentModel'); const triggerPluginEvent = jasmine.createSpy('triggerPluginEvent'); + const getVisibleViewport = jasmine.createSpy('getVisibleViewport'); const model = createContentModelDocument(); const text = createText('test a test', { fontFamily: 'Arial' }); const marker = createSelectionMarker(); @@ -224,10 +232,13 @@ describe('ContentModelFormatPlugin', () => { }, cacheContentModel: () => {}, isDarkMode: () => false, - getContentModelDefaultFormat: () => ({}), triggerPluginEvent, + getVisibleViewport, } as any) as IContentModelEditor; - const plugin = new ContentModelFormatPlugin(); + const state = { + defaultFormat: {}, + }; + const plugin = new ContentModelFormatPlugin(state); plugin.initialize(editor); plugin.onPluginEvent({ @@ -238,7 +249,7 @@ describe('ContentModelFormatPlugin', () => { expect(triggerPluginEvent).toHaveBeenCalledWith(PluginEventType.ContentChanged, { contentModel: model, - rangeEx: undefined, + selection: undefined, data: undefined, source: ChangeSource.Format, additionalData: { @@ -294,10 +305,11 @@ describe('ContentModelFormatPlugin', () => { createContentModel: () => model, setContentModel, cacheContentModel: () => {}, - getContentModelDefaultFormat: () => ({}), } as any) as IContentModelEditor; - const plugin = new ContentModelFormatPlugin(); - + const state = { + defaultFormat: {}, + }; + const plugin = new ContentModelFormatPlugin(state); plugin.initialize(editor); plugin.onPluginEvent({ eventType: PluginEventType.KeyDown, @@ -327,10 +339,11 @@ describe('ContentModelFormatPlugin', () => { callback(); }, cacheContentModel: () => {}, - getContentModelDefaultFormat: () => ({}), } as any) as IContentModelEditor; - const plugin = new ContentModelFormatPlugin(); - + const state = { + defaultFormat: {}, + }; + const plugin = new ContentModelFormatPlugin(state); plugin.initialize(editor); plugin.onPluginEvent({ eventType: PluginEventType.ContentChanged, @@ -358,9 +371,11 @@ describe('ContentModelFormatPlugin', () => { createContentModel: () => model, setContentModel, cacheContentModel: () => {}, - getContentModelDefaultFormat: () => ({}), } as any) as IContentModelEditor; - const plugin = new ContentModelFormatPlugin(); + const state = { + defaultFormat: {}, + }; + const plugin = new ContentModelFormatPlugin(state); plugin.initialize(editor); plugin.onPluginEvent({ @@ -389,9 +404,11 @@ describe('ContentModelFormatPlugin', () => { createContentModel: () => model, setContentModel, cacheContentModel: () => {}, - getContentModelDefaultFormat: () => ({}), } as any) as IContentModelEditor; - const plugin = new ContentModelFormatPlugin(); + const state = { + defaultFormat: {}, + }; + const plugin = new ContentModelFormatPlugin(state); plugin.initialize(editor); plugin.onPluginEvent({ @@ -409,7 +426,7 @@ describe('ContentModelFormatPlugin', () => { describe('ContentModelFormatPlugin for default format', () => { let editor: IContentModelEditor; let contentDiv: HTMLDivElement; - let getSelectionRangeEx: jasmine.Spy; + let getDOMSelection: jasmine.Spy; let getPendingFormatSpy: jasmine.Spy; let setPendingFormatSpy: jasmine.Spy; let cacheContentModelSpy: jasmine.Spy; @@ -418,7 +435,7 @@ describe('ContentModelFormatPlugin for default format', () => { beforeEach(() => { setPendingFormatSpy = spyOn(pendingFormat, 'setPendingFormat'); getPendingFormatSpy = spyOn(pendingFormat, 'getPendingFormat'); - getSelectionRangeEx = jasmine.createSpy('getSelectionRangeEx'); + getDOMSelection = jasmine.createSpy('getDOMSelection'); cacheContentModelSpy = jasmine.createSpy('cacheContentModel'); addUndoSnapshotSpy = jasmine.createSpy('addUndoSnapshot'); @@ -426,28 +443,26 @@ describe('ContentModelFormatPlugin for default format', () => { editor = ({ contains: (e: Node) => contentDiv != e && contentDiv.contains(e), - getSelectionRangeEx, - getContentModelDefaultFormat: () => ({ - fontFamily: 'Arial', - }), + getDOMSelection, cacheContentModel: cacheContentModelSpy, addUndoSnapshot: addUndoSnapshotSpy, } as any) as IContentModelEditor; }); it('Collapsed range, text input, under editor directly', () => { - const plugin = new ContentModelFormatPlugin(); + const state: ContentModelFormatPluginState = { + defaultFormat: { fontFamily: 'Arial' }, + }; + const plugin = new ContentModelFormatPlugin(state); const rawEvent = { key: 'a' } as any; - getSelectionRangeEx.and.returnValue({ - type: SelectionRangeTypes.Normal, - ranges: [ - { - collapsed: true, - startContainer: contentDiv, - startOffset: 0, - }, - ], + getDOMSelection.and.returnValue({ + type: 'range', + range: { + collapsed: true, + startContainer: contentDiv, + startOffset: 0, + }, }); spyOn(formatWithContentModel, 'formatWithContentModel').and.callFake( @@ -482,23 +497,25 @@ describe('ContentModelFormatPlugin for default format', () => { expect(setPendingFormatSpy).toHaveBeenCalledWith( editor, { fontFamily: 'Arial' }, - new Position(contentDiv, 0) + contentDiv, + 0 ); }); it('Expanded range, text input, under editor directly', () => { - const plugin = new ContentModelFormatPlugin(); + const state = { + defaultFormat: { fontFamily: 'Arial' }, + }; + const plugin = new ContentModelFormatPlugin(state); const rawEvent = { key: 'a' } as any; - getSelectionRangeEx.and.returnValue({ - type: SelectionRangeTypes.Normal, - ranges: [ - { - collapsed: false, - startContainer: contentDiv, - startOffset: 0, - }, - ], + getDOMSelection.and.returnValue({ + type: 'range', + range: { + collapsed: false, + startContainer: contentDiv, + startOffset: 0, + }, }); spyOn(formatWithContentModel, 'formatWithContentModel').and.callFake( @@ -536,18 +553,19 @@ describe('ContentModelFormatPlugin for default format', () => { }); it('Collapsed range, IME input, under editor directly', () => { - const plugin = new ContentModelFormatPlugin(); + const state = { + defaultFormat: { fontFamily: 'Arial' }, + }; + const plugin = new ContentModelFormatPlugin(state); const rawEvent = { key: 'Process' } as any; - getSelectionRangeEx.and.returnValue({ - type: SelectionRangeTypes.Normal, - ranges: [ - { - collapsed: true, - startContainer: contentDiv, - startOffset: 0, - }, - ], + getDOMSelection.and.returnValue({ + type: 'range', + range: { + collapsed: true, + startContainer: contentDiv, + startOffset: 0, + }, }); spyOn(formatWithContentModel, 'formatWithContentModel').and.callFake( @@ -582,23 +600,25 @@ describe('ContentModelFormatPlugin for default format', () => { expect(setPendingFormatSpy).toHaveBeenCalledWith( editor, { fontFamily: 'Arial' }, - new Position(contentDiv, 0) + contentDiv, + 0 ); }); it('Collapsed range, other input, under editor directly', () => { - const plugin = new ContentModelFormatPlugin(); + const state = { + defaultFormat: { fontFamily: 'Arial' }, + }; + const plugin = new ContentModelFormatPlugin(state); const rawEvent = { key: 'Up' } as any; - getSelectionRangeEx.and.returnValue({ - type: SelectionRangeTypes.Normal, - ranges: [ - { - collapsed: true, - startContainer: contentDiv, - startOffset: 0, - }, - ], + getDOMSelection.and.returnValue({ + type: 'range', + range: { + collapsed: true, + startContainer: contentDiv, + startOffset: 0, + }, }); spyOn(formatWithContentModel, 'formatWithContentModel').and.callFake( @@ -634,21 +654,22 @@ describe('ContentModelFormatPlugin for default format', () => { }); it('Collapsed range, normal input, not under editor directly, no style', () => { - const plugin = new ContentModelFormatPlugin(); + const state = { + defaultFormat: { fontFamily: 'Arial' }, + }; + const plugin = new ContentModelFormatPlugin(state); const rawEvent = { key: 'a' } as any; const div = document.createElement('div'); contentDiv.appendChild(div); - getSelectionRangeEx.and.returnValue({ - type: SelectionRangeTypes.Normal, - ranges: [ - { - collapsed: true, - startContainer: div, - startOffset: 0, - }, - ], + getDOMSelection.and.returnValue({ + type: 'range', + range: { + collapsed: true, + startContainer: div, + startOffset: 0, + }, }); spyOn(formatWithContentModel, 'formatWithContentModel').and.callFake( @@ -679,26 +700,23 @@ describe('ContentModelFormatPlugin for default format', () => { rawEvent, }); - expect(setPendingFormatSpy).toHaveBeenCalledWith( - editor, - { fontFamily: 'Arial' }, - new Position(div, 0) - ); + expect(setPendingFormatSpy).toHaveBeenCalledWith(editor, { fontFamily: 'Arial' }, div, 0); }); it('Collapsed range, text input, under editor directly, has pending format', () => { - const plugin = new ContentModelFormatPlugin(); + const state = { + defaultFormat: { fontFamily: 'Arial' }, + }; + const plugin = new ContentModelFormatPlugin(state); const rawEvent = { key: 'a' } as any; - getSelectionRangeEx.and.returnValue({ - type: SelectionRangeTypes.Normal, - ranges: [ - { - collapsed: true, - startContainer: contentDiv, - startOffset: 0, - }, - ], + getDOMSelection.and.returnValue({ + type: 'range', + range: { + collapsed: true, + startContainer: contentDiv, + startOffset: 0, + }, }); spyOn(formatWithContentModel, 'formatWithContentModel').and.callFake( @@ -737,7 +755,8 @@ describe('ContentModelFormatPlugin for default format', () => { expect(setPendingFormatSpy).toHaveBeenCalledWith( editor, { fontFamily: 'Arial', fontSize: '10pt' }, - new Position(contentDiv, 0) + contentDiv, + 0 ); }); }); 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 a046a61d533..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 @@ -1,17 +1,18 @@ import * as ContentModelCachePlugin from '../../lib/editor/corePlugins/ContentModelCachePlugin'; -import * as ContentModelEditPlugin from '../../lib/editor/plugins/ContentModelEditPlugin'; -import * as ContentModelFormatPlugin from '../../lib/editor/plugins/ContentModelFormatPlugin'; +import * as ContentModelEditPlugin from '../../lib/editor/corePlugins/ContentModelEditPlugin'; +import * as ContentModelFormatPlugin from '../../lib/editor/corePlugins/ContentModelFormatPlugin'; 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 { getSelectionRangeEx } from '../../lib/editor/coreApi/getSelectionRangeEx'; +import { getDOMSelection } from '../../lib/editor/coreApi/getDOMSelection'; import { setContentModel } from '../../lib/editor/coreApi/setContentModel'; +import { setDOMSelection } from '../../lib/editor/coreApi/setDOMSelection'; import { switchShadowEdit } from '../../lib/editor/coreApi/switchShadowEdit'; import { tablePreProcessor } from '../../lib/editor/overrides/tablePreProcessor'; @@ -90,20 +91,22 @@ describe('createContentModelEditorCore', () => { expect(core).toEqual({ lifecycle: { experimentalFeatures: [], - defaultFormat: {}, }, api: { switchShadowEdit, createEditorContext, createContentModel, setContentModel, - getSelectionRangeEx, + getDOMSelection, + setDOMSelection, }, originalApi: { a: 'b', createEditorContext, createContentModel, setContentModel, + getDOMSelection, + setDOMSelection, }, defaultDomToModelOptions: [ { processorOverride: { table: tablePreProcessor } }, @@ -112,20 +115,21 @@ describe('createContentModelEditorCore', () => { defaultModelToDomOptions: [undefined], defaultDomToModelConfig: mockedDomToModelConfig, defaultModelToDomConfig: mockedModelToDomConfig, - defaultFormat: { - fontWeight: undefined, - italic: undefined, - underline: undefined, - fontFamily: undefined, - fontSize: undefined, - textColor: undefined, - backgroundColor: undefined, - }, - addDelimiterForEntity: false, + format: { + defaultFormat: { + fontWeight: undefined, + italic: undefined, + underline: undefined, + fontFamily: undefined, + fontSize: undefined, + textColor: undefined, + backgroundColor: undefined, + }, + }, contentDiv: { style: {}, }, - cache: {}, + cache: { domIndexer: undefined }, copyPaste: { allowedCustomPasteType: [] }, } as any); }); @@ -156,20 +160,22 @@ describe('createContentModelEditorCore', () => { expect(core).toEqual({ lifecycle: { experimentalFeatures: [], - defaultFormat: {}, }, api: { switchShadowEdit, createEditorContext, createContentModel, setContentModel, - getSelectionRangeEx, + getDOMSelection, + setDOMSelection, }, originalApi: { a: 'b', createEditorContext, createContentModel, setContentModel, + getDOMSelection, + setDOMSelection, }, defaultDomToModelOptions: [ { processorOverride: { table: tablePreProcessor } }, @@ -178,39 +184,41 @@ describe('createContentModelEditorCore', () => { defaultModelToDomOptions: [defaultModelToDomOptions], defaultDomToModelConfig: mockedDomToModelConfig, defaultModelToDomConfig: mockedModelToDomConfig, - defaultFormat: { - fontWeight: undefined, - italic: undefined, - underline: undefined, - fontFamily: undefined, - fontSize: undefined, - textColor: undefined, - backgroundColor: undefined, - }, - addDelimiterForEntity: false, + format: { + defaultFormat: { + fontWeight: undefined, + italic: undefined, + underline: undefined, + fontFamily: undefined, + fontSize: undefined, + textColor: undefined, + backgroundColor: undefined, + }, + }, contentDiv: { style: {}, }, - cache: {}, + cache: { + domIndexer: undefined, + }, copyPaste: { allowedCustomPasteType: [] }, } as any); }); it('With default format', () => { - mockedCore.lifecycle.defaultFormat = { - bold: true, - italic: true, - underline: true, - fontFamily: 'Arial', - fontSize: '10pt', - textColor: 'red', - backgroundColor: 'blue', - }; - const options = { corePluginOverride: { copyPaste: copyPastePlugin, }, + defaultFormat: { + bold: true, + italic: true, + underline: true, + fontFamily: 'Arial', + fontSize: '10pt', + textColor: 'red', + backgroundColor: 'blue', + }, }; const core = createContentModelEditorCore(contentDiv, options); @@ -221,32 +229,35 @@ describe('createContentModelEditorCore', () => { typeInContainer: new ContentModelTypeInContainerPlugin(), copyPaste: copyPastePlugin, }, + defaultFormat: { + bold: true, + italic: true, + underline: true, + fontFamily: 'Arial', + fontSize: '10pt', + textColor: 'red', + backgroundColor: 'blue', + }, }); expect(core).toEqual({ lifecycle: { experimentalFeatures: [], - defaultFormat: { - bold: true, - italic: true, - underline: true, - fontFamily: 'Arial', - fontSize: '10pt', - textColor: 'red', - backgroundColor: 'blue', - }, }, api: { switchShadowEdit, createEditorContext, createContentModel, setContentModel, - getSelectionRangeEx, + getDOMSelection, + setDOMSelection, }, originalApi: { a: 'b', createEditorContext, createContentModel, setContentModel, + getDOMSelection, + setDOMSelection, }, defaultDomToModelOptions: [ { processorOverride: { table: tablePreProcessor } }, @@ -255,20 +266,21 @@ describe('createContentModelEditorCore', () => { defaultModelToDomOptions: [undefined], defaultDomToModelConfig: mockedDomToModelConfig, defaultModelToDomConfig: mockedModelToDomConfig, - defaultFormat: { - fontWeight: 'bold', - italic: true, - underline: true, - fontFamily: 'Arial', - fontSize: '10pt', - textColor: 'red', - backgroundColor: 'blue', + format: { + defaultFormat: { + fontWeight: 'bold', + italic: true, + underline: true, + fontFamily: 'Arial', + fontSize: '10pt', + textColor: 'red', + backgroundColor: 'blue', + }, }, - addDelimiterForEntity: false, contentDiv: { style: {}, }, - cache: {}, + cache: { domIndexer: undefined }, copyPaste: { allowedCustomPasteType: [] }, } as any); }); @@ -292,62 +304,58 @@ describe('createContentModelEditorCore', () => { expect(core).toEqual({ lifecycle: { experimentalFeatures: [], - defaultFormat: {}, }, api: { switchShadowEdit: switchShadowEdit, createEditorContext, createContentModel, setContentModel, - getSelectionRangeEx, + getDOMSelection, + setDOMSelection, }, originalApi: { a: 'b', createEditorContext, createContentModel, setContentModel, + getDOMSelection, + setDOMSelection, }, defaultDomToModelOptions: [ { processorOverride: { table: tablePreProcessor } }, undefined, ], defaultModelToDomOptions: [undefined], - defaultFormat: { - fontWeight: undefined, - italic: undefined, - underline: undefined, - fontFamily: undefined, - fontSize: undefined, - textColor: undefined, - backgroundColor: undefined, + format: { + defaultFormat: { + fontWeight: undefined, + italic: undefined, + underline: undefined, + fontFamily: undefined, + fontSize: undefined, + textColor: undefined, + backgroundColor: undefined, + }, }, defaultDomToModelConfig: mockedDomToModelConfig, defaultModelToDomConfig: mockedModelToDomConfig, - addDelimiterForEntity: false, contentDiv: { style: {}, }, - cache: {}, + cache: { domIndexer: undefined }, copyPaste: { allowedCustomPasteType: [] }, } as any); }); - it('Allow entity delimiters', () => { - mockedCore.lifecycle.experimentalFeatures.push( - ExperimentalFeatures.InlineEntityReadOnlyDelimiters - ); - - const options = { + it('Allow dom indexer', () => { + const options: ContentModelEditorOptions = { corePluginOverride: { copyPaste: copyPastePlugin, }, + cacheModel: true, }; - spyOn(isFeatureEnabled, 'isFeatureEnabled').and.callFake( - (features, feature) => feature == ExperimentalFeatures.InlineEntityReadOnlyDelimiters - ); - const core = createContentModelEditorCore(contentDiv, options); expect(createEditorCoreSpy).toHaveBeenCalledWith(contentDiv, { @@ -356,24 +364,27 @@ describe('createContentModelEditorCore', () => { typeInContainer: new ContentModelTypeInContainerPlugin(), copyPaste: copyPastePlugin, }, + cacheModel: true, }); expect(core).toEqual({ lifecycle: { - experimentalFeatures: [ExperimentalFeatures.InlineEntityReadOnlyDelimiters], - defaultFormat: {}, + experimentalFeatures: [], }, api: { switchShadowEdit, createEditorContext, createContentModel, setContentModel, - getSelectionRangeEx, + getDOMSelection, + setDOMSelection, }, originalApi: { a: 'b', createEditorContext, createContentModel, setContentModel, + getDOMSelection, + setDOMSelection, }, defaultDomToModelOptions: [ { processorOverride: { table: tablePreProcessor } }, @@ -382,20 +393,21 @@ describe('createContentModelEditorCore', () => { defaultModelToDomOptions: [undefined], defaultDomToModelConfig: mockedDomToModelConfig, defaultModelToDomConfig: mockedModelToDomConfig, - defaultFormat: { - fontWeight: undefined, - italic: undefined, - underline: undefined, - fontFamily: undefined, - fontSize: undefined, - textColor: undefined, - backgroundColor: undefined, - }, - addDelimiterForEntity: true, + format: { + defaultFormat: { + fontWeight: undefined, + italic: undefined, + underline: undefined, + fontFamily: undefined, + fontSize: undefined, + textColor: undefined, + backgroundColor: undefined, + }, + }, contentDiv: { style: {}, }, - cache: {}, + cache: { domIndexer: contentModelDomIndexer }, copyPaste: { allowedCustomPasteType: [] }, } as any); }); diff --git a/packages-content-model/roosterjs-content-model-editor/test/editor/overrides/tablePreProcessorTest.ts b/packages-content-model/roosterjs-content-model-editor/test/editor/overrides/tablePreProcessorTest.ts index 89441aa5f49..4dd8d30ef19 100644 --- a/packages-content-model/roosterjs-content-model-editor/test/editor/overrides/tablePreProcessorTest.ts +++ b/packages-content-model/roosterjs-content-model-editor/test/editor/overrides/tablePreProcessorTest.ts @@ -1,6 +1,5 @@ import * as tableProcessor from 'roosterjs-content-model-dom/lib/domToModel/processors/tableProcessor'; import { createContentModelDocument, createDomToModelContext } from 'roosterjs-content-model-dom'; -import { SelectionRangeTypes } from 'roosterjs-editor-types'; import { tablePreProcessor } from '../../../lib/editor/overrides/tablePreProcessor'; describe('tablePreProcessor', () => { @@ -18,9 +17,12 @@ describe('tablePreProcessor', () => { blockType: 'Entity', segmentType: 'Entity', format: {}, - id: undefined, - type: undefined, - isReadonly: true, + entityFormat: { + isFakeEntity: true, + id: undefined, + entityType: undefined, + isReadonly: true, + }, wrapper: table, }, ], @@ -57,14 +59,11 @@ describe('tablePreProcessor', () => { tr.appendChild(td); td.appendChild(txt); - context.rangeEx = { - type: SelectionRangeTypes.Normal, - ranges: [ - { - commonAncestorContainer: txt, - } as any, - ], - areAllCollapsed: false, + context.selection = { + type: 'range', + range: { + commonAncestorContainer: txt, + } as any, }; tablePreProcessor(group, table, context); @@ -89,14 +88,11 @@ describe('tablePreProcessor', () => { tr.appendChild(td); td.appendChild(txt); - context.rangeEx = { - type: SelectionRangeTypes.Normal, - ranges: [ - { - commonAncestorContainer: txt, - } as any, - ], - areAllCollapsed: false, + context.selection = { + type: 'range', + range: { + commonAncestorContainer: txt, + } as any, } as any; tablePreProcessor(group, table, context); @@ -121,14 +117,11 @@ describe('tablePreProcessor', () => { tr.appendChild(td); td.appendChild(txt); - context.rangeEx = { - type: SelectionRangeTypes.Normal, - ranges: [ - { - commonAncestorContainer: table, - } as any, - ], - areAllCollapsed: false, + context.selection = { + type: 'range', + range: { + commonAncestorContainer: table, + } as any, } as any; tablePreProcessor(group, table, context); @@ -153,13 +146,9 @@ describe('tablePreProcessor', () => { tr.appendChild(td); td.appendChild(txt); - context.rangeEx = { - type: SelectionRangeTypes.TableSelection, + context.selection = { + type: 'table', table, - coordinates: { - firstCell: {}, - lastCell: {}, - }, } as any; tablePreProcessor(group, table, context); @@ -184,8 +173,8 @@ describe('tablePreProcessor', () => { tr.appendChild(td); td.appendChild(txt); - context.rangeEx = { - type: SelectionRangeTypes.ImageSelection, + context.selection = { + type: 'image', image: txt as any, } as any; diff --git a/packages-content-model/roosterjs-content-model-editor/test/editor/plugins/ContentModelCachePluginTest.ts b/packages-content-model/roosterjs-content-model-editor/test/editor/plugins/ContentModelCachePluginTest.ts new file mode 100644 index 00000000000..73a877e7673 --- /dev/null +++ b/packages-content-model/roosterjs-content-model-editor/test/editor/plugins/ContentModelCachePluginTest.ts @@ -0,0 +1,459 @@ +import { ContentModelCachePluginState } from '../../../lib/publicTypes/pluginState/ContentModelCachePluginState'; +import { ContentModelDomIndexer } from 'roosterjs-content-model-types'; +import { default as ContentModelCachePlugin } from '../../../lib/editor/corePlugins/ContentModelCachePlugin'; +import { IContentModelEditor } from '../../../lib/publicTypes/IContentModelEditor'; +import { Keys, PluginEventType } from 'roosterjs-editor-types'; + +describe('ContentModelCachePlugin', () => { + let plugin: ContentModelCachePlugin; + let state: ContentModelCachePluginState; + let editor: IContentModelEditor; + + let addEventListenerSpy: jasmine.Spy; + let removeEventListenerSpy: jasmine.Spy; + let getDOMSelectionSpy: jasmine.Spy; + let reconcileSelectionSpy: jasmine.Spy; + let isInShadowEditSpy: jasmine.Spy; + let domIndexer: ContentModelDomIndexer; + + function init() { + addEventListenerSpy = jasmine.createSpy('addEventListenerSpy'); + removeEventListenerSpy = jasmine.createSpy('removeEventListener'); + getDOMSelectionSpy = jasmine.createSpy('getDOMSelection'); + reconcileSelectionSpy = jasmine.createSpy('reconcileSelection'); + isInShadowEditSpy = jasmine.createSpy('isInShadowEdit'); + + domIndexer = { + reconcileSelection: reconcileSelectionSpy, + } as any; + + state = {}; + editor = ({ + getDOMSelection: getDOMSelectionSpy, + isInShadowEdit: isInShadowEditSpy, + getDocument: () => { + return { + addEventListener: addEventListenerSpy, + removeEventListener: removeEventListenerSpy, + }; + }, + } as any) as IContentModelEditor; + + plugin = new ContentModelCachePlugin(state); + plugin.initialize(editor); + } + + describe('initialize', () => { + beforeEach(init); + afterEach(() => { + plugin.dispose(); + }); + + it('initialize', () => { + expect(addEventListenerSpy).toHaveBeenCalledWith('selectionchange', jasmine.anything()); + }); + }); + + describe('KeyDown event', () => { + beforeEach(init); + afterEach(() => { + plugin.dispose(); + }); + + it('ENTER key', () => { + plugin.onPluginEvent({ + eventType: PluginEventType.KeyDown, + rawEvent: { + which: Keys.ENTER, + } as any, + }); + + expect(state).toEqual({ + cachedModel: undefined, + cachedSelection: undefined, + }); + }); + + it('Other key without selection', () => { + plugin.onPluginEvent({ + eventType: PluginEventType.KeyDown, + rawEvent: { + which: Keys.B, + key: 'B', + } as any, + }); + + expect(state).toEqual({ cachedModel: undefined, cachedSelection: undefined }); + }); + + it('Other key with collapsed selection', () => { + state.cachedSelection = { + type: 'range', + range: { collapsed: true } as any, + }; + + plugin.onPluginEvent({ + eventType: PluginEventType.KeyDown, + rawEvent: { + which: Keys.B, + } as any, + }); + + expect(state).toEqual({ + cachedSelection: { type: 'range', range: { collapsed: true } as any }, + }); + }); + + it('Expanded selection with text input', () => { + state.cachedSelection = { + type: 'range', + range: { collapsed: false } as any, + }; + + plugin.onPluginEvent({ + eventType: PluginEventType.KeyDown, + rawEvent: { + which: Keys.B, + key: 'B', + } as any, + }); + + expect(state).toEqual({ cachedModel: undefined, cachedSelection: undefined }); + }); + + it('Expanded selection with arrow input', () => { + state.cachedSelection = { + type: 'range', + range: { collapsed: false } as any, + }; + + plugin.onPluginEvent({ + eventType: PluginEventType.KeyDown, + rawEvent: { + which: Keys.UP, + key: 'Up', + } as any, + }); + + expect(state).toEqual({ + cachedSelection: { + type: 'range', + range: { collapsed: false } as any, + }, + }); + }); + + it('Table selection', () => { + state.cachedSelection = { + type: 'table', + } as any; + + plugin.onPluginEvent({ + eventType: PluginEventType.KeyDown, + rawEvent: { + which: Keys.B, + key: 'B', + } as any, + }); + + expect(state).toEqual({ cachedModel: undefined, cachedSelection: undefined }); + }); + + it('Image selection', () => { + state.cachedSelection = { + type: 'image', + } as any; + + plugin.onPluginEvent({ + eventType: PluginEventType.KeyDown, + rawEvent: { + which: Keys.B, + key: 'B', + } as any, + }); + + expect(state).toEqual({ cachedModel: undefined, cachedSelection: undefined }); + }); + + it('Do not clear cache when in shadow edit', () => { + isInShadowEditSpy.and.returnValue(true); + + plugin.onPluginEvent({ + eventType: PluginEventType.KeyDown, + rawEvent: { + which: Keys.ENTER, + } as any, + }); + + expect(state).toEqual({}); + }); + }); + + describe('Input event', () => { + beforeEach(init); + afterEach(() => { + plugin.dispose(); + }); + + it('No cached range, no cached model', () => { + state.cachedModel = undefined; + state.cachedSelection = undefined; + + const selection = 'MockedRange' as any; + getDOMSelectionSpy.and.returnValue(selection); + + plugin.onPluginEvent({ + eventType: PluginEventType.Input, + rawEvent: null!, + }); + + expect(state).toEqual({ + cachedModel: undefined, + cachedSelection: undefined, + }); + }); + + it('No cached range, has cached model', () => { + const selection = 'MockedRange' as any; + const model = 'MockedModel' as any; + + state.cachedModel = model; + state.cachedSelection = undefined; + + getDOMSelectionSpy.and.returnValue(selection); + + plugin.onPluginEvent({ + eventType: PluginEventType.Input, + rawEvent: null!, + }); + + expect(state).toEqual({ + cachedModel: undefined, + cachedSelection: undefined, + }); + }); + + it('No cached range, has cached model, reconcile succeed', () => { + const selection = 'MockedRange' as any; + const model = 'MockedModel' as any; + + state.cachedModel = model; + state.cachedSelection = undefined; + state.domIndexer = domIndexer; + + getDOMSelectionSpy.and.returnValue(selection); + reconcileSelectionSpy.and.returnValue(true); + + plugin.onPluginEvent({ + eventType: PluginEventType.Input, + rawEvent: null!, + }); + + expect(state).toEqual({ + cachedModel: model, + cachedSelection: selection, + domIndexer: domIndexer, + }); + }); + + it('has cached range, has cached model', () => { + const oldRangeEx = 'MockedRangeOld' as any; + const newRangeEx = 'MockedRangeNew' as any; + const model = 'MockedModel' as any; + + state.cachedModel = model; + state.cachedSelection = oldRangeEx; + getDOMSelectionSpy.and.returnValue(newRangeEx); + + plugin.onPluginEvent({ + eventType: PluginEventType.Input, + rawEvent: null!, + }); + + expect(state).toEqual({ + cachedModel: undefined, + cachedSelection: undefined, + }); + }); + + it('has cached range, has cached model, has domIndexer', () => { + const oldRangeEx = 'MockedRangeOld' as any; + const newRangeEx = 'MockedRangeNew' as any; + const model = 'MockedModel' as any; + + state.cachedModel = model; + state.cachedSelection = oldRangeEx; + state.domIndexer = domIndexer; + + getDOMSelectionSpy.and.returnValue(newRangeEx); + reconcileSelectionSpy.and.returnValue(true); + + plugin.onPluginEvent({ + eventType: PluginEventType.Input, + rawEvent: null!, + }); + + expect(state).toEqual({ + cachedModel: model, + cachedSelection: newRangeEx, + domIndexer, + }); + }); + }); + + describe('SelectionChanged', () => { + beforeEach(init); + afterEach(() => { + plugin.dispose(); + }); + + it('Same range', () => { + const selection = 'MockedRange' as any; + const model = 'MockedModel' as any; + + state.cachedModel = model; + state.cachedSelection = selection; + state.domIndexer = domIndexer; + + getDOMSelectionSpy.and.returnValue(selection); + reconcileSelectionSpy.and.returnValue(true); + + plugin.onPluginEvent({ + eventType: PluginEventType.SelectionChanged, + selectionRangeEx: selection, + }); + + expect(state).toEqual({ + cachedModel: model, + cachedSelection: selection, + domIndexer, + }); + expect(reconcileSelectionSpy).not.toHaveBeenCalled(); + }); + + it('Different range', () => { + const oldRangeEx = 'MockedRangeOld' as any; + const newRangeEx = 'MockedRangeNew' as any; + const model = 'MockedModel' as any; + + state.cachedModel = model; + state.cachedSelection = oldRangeEx; + state.domIndexer = domIndexer; + + reconcileSelectionSpy.and.returnValue(true); + getDOMSelectionSpy.and.returnValue(newRangeEx); + + plugin.onPluginEvent({ + eventType: PluginEventType.SelectionChanged, + selectionRangeEx: newRangeEx, + }); + + expect(state).toEqual({ + cachedModel: model, + cachedSelection: newRangeEx, + domIndexer, + }); + expect(reconcileSelectionSpy).toHaveBeenCalledWith(model, newRangeEx, oldRangeEx); + }); + + it('Different range and fail to reconcile', () => { + const oldRangeEx = 'MockedRangeOld' as any; + const newRangeEx = 'MockedRangeNew' as any; + const model = 'MockedModel' as any; + + state.cachedModel = model; + state.cachedSelection = oldRangeEx; + state.domIndexer = domIndexer; + + reconcileSelectionSpy.and.returnValue(false); + getDOMSelectionSpy.and.returnValue(newRangeEx); + + plugin.onPluginEvent({ + eventType: PluginEventType.SelectionChanged, + selectionRangeEx: newRangeEx, + }); + + expect(state).toEqual({ + cachedModel: undefined, + cachedSelection: undefined, + domIndexer, + }); + expect(reconcileSelectionSpy).toHaveBeenCalledWith(model, newRangeEx, oldRangeEx); + }); + }); + + describe('ContentChanged', () => { + beforeEach(init); + afterEach(() => { + plugin.dispose(); + }); + + it('No domIndexer, no model in event', () => { + const model = 'MockedModel' as any; + + state.cachedModel = model; + state.cachedSelection = undefined; + + plugin.onPluginEvent({ + eventType: PluginEventType.ContentChanged, + source: '', + }); + + expect(state).toEqual({ + cachedModel: undefined, + cachedSelection: undefined, + }); + expect(reconcileSelectionSpy).not.toHaveBeenCalled(); + }); + + it('No domIndexer, has model in event', () => { + const oldRangeEx = 'MockedRangeOld' as any; + const newRangeEx = 'MockedRangeNew' as any; + const model = 'MockedModel' as any; + + state.cachedModel = model; + state.cachedSelection = oldRangeEx; + + reconcileSelectionSpy.and.returnValue(true); + + plugin.onPluginEvent({ + eventType: PluginEventType.ContentChanged, + source: '', + contentModel: model, + selection: newRangeEx, + } as any); + + expect(state).toEqual({ + cachedModel: undefined, + cachedSelection: undefined, + }); + expect(reconcileSelectionSpy).not.toHaveBeenCalled(); + }); + + it('Has domIndexer, has model in event', () => { + const oldRangeEx = 'MockedRangeOld' as any; + const newRangeEx = 'MockedRangeNew' as any; + const model = 'MockedModel' as any; + + state.cachedModel = model; + state.cachedSelection = oldRangeEx; + state.domIndexer = domIndexer; + + reconcileSelectionSpy.and.returnValue(true); + + plugin.onPluginEvent({ + eventType: PluginEventType.ContentChanged, + source: '', + contentModel: model, + selection: newRangeEx, + } as any); + + expect(state).toEqual({ + cachedModel: model, + cachedSelection: newRangeEx, + domIndexer, + }); + expect(reconcileSelectionSpy).not.toHaveBeenCalled(); + }); + }); +}); 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 18b40d796db..04dfef7722c 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 @@ -5,11 +5,12 @@ import * as extractClipboardItemsFile from 'roosterjs-editor-dom/lib/clipboard/e import * as iterateSelectionsFile from '../../../lib/modelApi/selection/iterateSelections'; import * as normalizeContentModel from 'roosterjs-content-model-dom/lib/modelApi/common/normalizeContentModel'; import * as PasteFile from '../../../lib/publicApi/utils/paste'; -import { commitEntity } from 'roosterjs-editor-dom'; import { createModelToDomContext } from 'roosterjs-content-model-dom'; +import { createRange } from 'roosterjs-editor-dom'; import { DeleteResult } from '../../../lib/modelApi/edit/utils/DeleteSelectionStep'; +import { DOMSelection } from 'roosterjs-content-model-types'; import { IContentModelEditor } from '../../../lib/publicTypes/IContentModelEditor'; -import createRange, * as createRangeF from 'roosterjs-editor-dom/lib/selection/createRange'; +import { setEntityElementClasses } from 'roosterjs-content-model-dom/test/domUtils/entityUtilTest'; import ContentModelCopyPastePlugin, { onNodeCreated, } from '../../../lib/editor/corePlugins/ContentModelCopyPastePlugin'; @@ -18,8 +19,6 @@ import { ColorTransformDirection, DOMEventHandlerFunction, IEditor, - SelectionRangeEx, - SelectionRangeTypes, } from 'roosterjs-editor-types'; const modelValue = 'model' as any; @@ -36,37 +35,37 @@ describe('ContentModelCopyPastePlugin |', () => { let domEvents: Record = {}; let div: HTMLDivElement; - let selectionRangeExValue: SelectionRangeEx; - let getSelectionRangeEx: jasmine.Spy; + let selectionValue: DOMSelection; + let getDOMSelectionSpy: jasmine.Spy; let createContentModelSpy: jasmine.Spy; let triggerPluginEventSpy: jasmine.Spy; let focusSpy: jasmine.Spy; let undoSnapShotSpy: jasmine.Spy; - let selectSpy: jasmine.Spy; + let setDOMSelectionSpy: jasmine.Spy; let setContentModelSpy: jasmine.Spy; - let getSelectionRange: jasmine.Spy; let isDisposed: jasmine.Spy; let pasteSpy: jasmine.Spy; let cloneModelSpy: jasmine.Spy; let transformToDarkColorSpy: jasmine.Spy; + let getVisibleViewportSpy: jasmine.Spy; beforeEach(() => { div = document.createElement('div'); - getSelectionRangeEx = jasmine - .createSpy('selectRangeExSpy') - .and.callFake(() => selectionRangeExValue); + getDOMSelectionSpy = jasmine + .createSpy('getDOMSelection') + .and.callFake(() => selectionValue); createContentModelSpy = jasmine .createSpy('createContentModelSpy') .and.returnValue(modelValue); triggerPluginEventSpy = jasmine.createSpy('triggerPluginEventSpy'); focusSpy = jasmine.createSpy('focusSpy'); undoSnapShotSpy = jasmine.createSpy('undoSnapShotSpy'); - selectSpy = jasmine.createSpy('selectSpy'); - setContentModelSpy = jasmine.createSpy('setContentModelSpy'); - getSelectionRange = jasmine.createSpy('selectionRange'); + setDOMSelectionSpy = jasmine.createSpy('setDOMSelection'); + setContentModelSpy = jasmine.createSpy('setContentModel'); pasteSpy = jasmine.createSpy('paste_'); isDisposed = jasmine.createSpy('isDisposed'); + getVisibleViewportSpy = jasmine.createSpy('getVisibleViewport'); cloneModelSpy = spyOn(cloneModelFile, 'cloneModel').and.callFake( (model: any) => pasteModelValue @@ -77,7 +76,6 @@ describe('ContentModelCopyPastePlugin |', () => { allowedCustomPasteType, }); editor = ({ - getSelectionRange, addDomEventHandler: ( nameOrMap: string | Record, handler?: DOMEventHandlerFunction @@ -86,7 +84,6 @@ describe('ContentModelCopyPastePlugin |', () => { ? { [nameOrMap]: handler! } : nameOrMap) as any) as Record; }, - getSelectionRangeEx, createContentModel: (options: any) => createContentModelSpy(options), triggerPluginEvent(eventType: any, data: any, broadcast: any) { triggerPluginEventSpy(eventType, data, broadcast); @@ -102,9 +99,8 @@ describe('ContentModelCopyPastePlugin |', () => { callback?.(); undoSnapShotSpy(callback, changeSource, canUndoByBackspace); }, - select(a1: any, a2: any, a3: any, a4: any) { - selectSpy(a1, a2, a3, a4); - }, + getDOMSelection: getDOMSelectionSpy, + setDOMSelection: setDOMSelectionSpy, setContentModel(model: any, option: any) { setContentModelSpy(model, option); }, @@ -129,6 +125,7 @@ describe('ContentModelCopyPastePlugin |', () => { }, transformToDarkColor: transformToDarkColorSpy, isDisposed, + getVisibleViewport: getVisibleViewportSpy, }); plugin.initialize(editor); @@ -136,54 +133,50 @@ describe('ContentModelCopyPastePlugin |', () => { describe('Copy |', () => { it('Selection Collapsed', () => { - selectionRangeExValue = { - type: SelectionRangeTypes.Normal, - ranges: [], - areAllCollapsed: true, + selectionValue = { + type: 'range', + range: { collapsed: true } as any, }; createContentModelSpy.and.callThrough(); triggerPluginEventSpy.and.callThrough(); focusSpy.and.callThrough(); undoSnapShotSpy.and.callThrough(); - selectSpy.and.callThrough(); + setDOMSelectionSpy.and.callThrough(); setContentModelSpy.and.callThrough(); domEvents.copy?.({}); - expect(getSelectionRangeEx).toHaveBeenCalled(); + expect(getDOMSelectionSpy).toHaveBeenCalled(); expect(createContentModelSpy).not.toHaveBeenCalled(); expect(triggerPluginEventSpy).not.toHaveBeenCalled(); expect(focusSpy).not.toHaveBeenCalled(); expect(undoSnapShotSpy).not.toHaveBeenCalled(); - expect(selectSpy).not.toHaveBeenCalled(); + expect(setDOMSelectionSpy).not.toHaveBeenCalled(); expect(setContentModelSpy).not.toHaveBeenCalled(); }); it('Selection not Collapsed and normal selection', () => { // Arrange - selectionRangeExValue = { - type: SelectionRangeTypes.Normal, - ranges: [new Range()], - areAllCollapsed: false, + selectionValue = { + type: 'range', + range: { collapsed: false }, }; spyOn(deleteSelectionsFile, 'deleteSelection'); - spyOn(contentModelToDomFile, 'contentModelToDom').and.returnValue( - selectionRangeExValue - ); + spyOn(contentModelToDomFile, 'contentModelToDom').and.returnValue(selectionValue); spyOn(iterateSelectionsFile, 'iterateSelections').and.returnValue(undefined); triggerPluginEventSpy.and.callThrough(); focusSpy.and.callThrough(); - selectSpy.and.callThrough(); + setDOMSelectionSpy.and.callThrough(); setContentModelSpy.and.callThrough(); // Act domEvents.copy?.({}); // Assert - expect(getSelectionRangeEx).toHaveBeenCalled(); + expect(getDOMSelectionSpy).toHaveBeenCalled(); expect(deleteSelectionsFile.deleteSelection).not.toHaveBeenCalled(); expect(contentModelToDomFile.contentModelToDom).toHaveBeenCalledWith( document, @@ -196,12 +189,7 @@ describe('ContentModelCopyPastePlugin |', () => { expect(triggerPluginEventSpy).toHaveBeenCalledTimes(1); expect(iterateSelectionsFile.iterateSelections).not.toHaveBeenCalled(); expect(focusSpy).toHaveBeenCalled(); - expect(selectSpy).toHaveBeenCalledWith( - selectionRangeExValue, - undefined, - undefined, - undefined - ); + expect(setDOMSelectionSpy).toHaveBeenCalledWith(selectionValue); // On Cut Spy expect(undoSnapShotSpy).not.toHaveBeenCalled(); @@ -213,36 +201,31 @@ describe('ContentModelCopyPastePlugin |', () => { const table = document.createElement('table'); table.id = 'table'; // Arrange - selectionRangeExValue = { - type: SelectionRangeTypes.TableSelection, - ranges: [new Range()], - areAllCollapsed: false, - coordinates: {}, + selectionValue = { + type: 'table', table, }; - spyOn(createRangeF, 'default').and.callThrough(); spyOn(deleteSelectionsFile, 'deleteSelection'); spyOn(contentModelToDomFile, 'contentModelToDom').and.callFake(() => { const container = document.createElement('div'); container.append(table); div.appendChild(container); - return selectionRangeExValue; + return selectionValue; }); spyOn(iterateSelectionsFile, 'iterateSelections').and.returnValue(undefined); triggerPluginEventSpy.and.callThrough(); focusSpy.and.callThrough(); - selectSpy.and.callThrough(); + setDOMSelectionSpy.and.callThrough(); setContentModelSpy.and.callThrough(); // Act domEvents.copy?.({}); // Assert - expect(createRangeF.default).toHaveBeenCalledWith(table.parentElement); - expect(getSelectionRangeEx).toHaveBeenCalled(); + expect(getDOMSelectionSpy).toHaveBeenCalled(); expect(deleteSelectionsFile.deleteSelection).not.toHaveBeenCalled(); expect(contentModelToDomFile.contentModelToDom).toHaveBeenCalledWith( document, @@ -255,12 +238,7 @@ describe('ContentModelCopyPastePlugin |', () => { expect(triggerPluginEventSpy).toHaveBeenCalledTimes(1); expect(iterateSelectionsFile.iterateSelections).toHaveBeenCalled(); expect(focusSpy).toHaveBeenCalled(); - expect(selectSpy).toHaveBeenCalledWith( - selectionRangeExValue, - undefined, - undefined, - undefined - ); + expect(setDOMSelectionSpy).toHaveBeenCalledWith(selectionValue); // On Cut Spy expect(undoSnapShotSpy).not.toHaveBeenCalled(); @@ -271,32 +249,28 @@ describe('ContentModelCopyPastePlugin |', () => { // Arrange const image = document.createElement('image'); image.id = 'image'; - selectionRangeExValue = { - type: SelectionRangeTypes.ImageSelection, - ranges: [new Range()], - areAllCollapsed: false, + selectionValue = { + type: 'image', image, }; - spyOn(createRangeF, 'default').and.callThrough(); spyOn(deleteSelectionsFile, 'deleteSelection'); spyOn(contentModelToDomFile, 'contentModelToDom').and.callFake(() => { div.appendChild(image); - return selectionRangeExValue; + return selectionValue; }); spyOn(iterateSelectionsFile, 'iterateSelections').and.returnValue(undefined); triggerPluginEventSpy.and.callThrough(); focusSpy.and.callThrough(); - selectSpy.and.callThrough(); + setDOMSelectionSpy.and.callThrough(); setContentModelSpy.and.callThrough(); // Act domEvents.copy?.({}); // Assert - expect(createRangeF.default).toHaveBeenCalledWith(image); - expect(getSelectionRangeEx).toHaveBeenCalled(); + expect(getDOMSelectionSpy).toHaveBeenCalled(); expect(deleteSelectionsFile.deleteSelection).not.toHaveBeenCalled(); expect(contentModelToDomFile.contentModelToDom).toHaveBeenCalledWith( document, @@ -308,12 +282,7 @@ describe('ContentModelCopyPastePlugin |', () => { expect(createContentModelSpy).toHaveBeenCalled(); expect(triggerPluginEventSpy).toHaveBeenCalledTimes(1); expect(focusSpy).toHaveBeenCalled(); - expect(selectSpy).toHaveBeenCalledWith( - selectionRangeExValue, - undefined, - undefined, - undefined - ); + expect(setDOMSelectionSpy).toHaveBeenCalledWith(selectionValue); // On Cut Spy expect(undoSnapShotSpy).not.toHaveBeenCalled(); @@ -327,23 +296,22 @@ describe('ContentModelCopyPastePlugin |', () => { document.body.appendChild(wrapper); - commitEntity(wrapper, 'Entity', true, 'Entity'); - selectionRangeExValue = { - type: SelectionRangeTypes.Normal, - ranges: [createRange(wrapper)], - areAllCollapsed: false, + setEntityElementClasses(wrapper, 'Entity', true, 'Entity'); + selectionValue = { + type: 'range', + range: createRange(wrapper), }; spyOn(deleteSelectionsFile, 'deleteSelection'); spyOn(contentModelToDomFile, 'contentModelToDom').and.callFake(() => { div.appendChild(wrapper); - return selectionRangeExValue; + return selectionValue; }); spyOn(iterateSelectionsFile, 'iterateSelections').and.returnValue(undefined); triggerPluginEventSpy.and.callThrough(); focusSpy.and.callThrough(); - selectSpy.and.callThrough(); + setDOMSelectionSpy.and.callThrough(); setContentModelSpy.and.callThrough(); editor.isDarkMode = () => true; @@ -356,7 +324,9 @@ describe('ContentModelCopyPastePlugin |', () => { const cloneEntity = options.includeCachedElement(wrapper, 'entity'); expect(cloneCache).toBeUndefined(); - expect(cloneEntity).toEqual(wrapper); + expect(cloneEntity.outerHTML).toBe( + '' + ); expect(cloneEntity).not.toBe(wrapper); expect(transformToDarkColorSpy).toHaveBeenCalledTimes(1); expect(transformToDarkColorSpy).toHaveBeenCalledWith( @@ -371,7 +341,7 @@ describe('ContentModelCopyPastePlugin |', () => { domEvents.copy?.({}); // Assert - expect(getSelectionRangeEx).toHaveBeenCalled(); + expect(getDOMSelectionSpy).toHaveBeenCalled(); expect(deleteSelectionsFile.deleteSelection).not.toHaveBeenCalled(); expect(contentModelToDomFile.contentModelToDom).toHaveBeenCalledWith( document, @@ -383,12 +353,7 @@ describe('ContentModelCopyPastePlugin |', () => { expect(createContentModelSpy).toHaveBeenCalled(); expect(triggerPluginEventSpy).toHaveBeenCalledTimes(1); expect(focusSpy).toHaveBeenCalled(); - expect(selectSpy).toHaveBeenCalledWith( - selectionRangeExValue, - undefined, - undefined, - undefined - ); + expect(setDOMSelectionSpy).toHaveBeenCalledWith(selectionValue); expect(cloneModelSpy).toHaveBeenCalledTimes(1); // On Cut Spy @@ -401,38 +366,36 @@ describe('ContentModelCopyPastePlugin |', () => { describe('Cut |', () => { it('Selection Collapsed', () => { // Arrange - selectionRangeExValue = { - type: SelectionRangeTypes.Normal, - ranges: [], - areAllCollapsed: true, + selectionValue = { + type: 'range', + range: { collapsed: true } as any, }; createContentModelSpy.and.callThrough(); triggerPluginEventSpy.and.callThrough(); focusSpy.and.callThrough(); undoSnapShotSpy.and.callThrough(); - selectSpy.and.callThrough(); + setDOMSelectionSpy.and.callThrough(); setContentModelSpy.and.callThrough(); // Act domEvents.cut?.({}); // Assert - expect(getSelectionRangeEx).toHaveBeenCalled(); + expect(getDOMSelectionSpy).toHaveBeenCalled(); expect(createContentModelSpy).not.toHaveBeenCalled(); expect(triggerPluginEventSpy).not.toHaveBeenCalled(); expect(focusSpy).not.toHaveBeenCalled(); expect(undoSnapShotSpy).not.toHaveBeenCalled(); - expect(selectSpy).not.toHaveBeenCalled(); + expect(setDOMSelectionSpy).not.toHaveBeenCalled(); expect(setContentModelSpy).not.toHaveBeenCalled(); }); it('Selection not Collapsed', () => { // Arrange - selectionRangeExValue = { - type: SelectionRangeTypes.Normal, - ranges: [new Range()], - areAllCollapsed: false, + selectionValue = { + type: 'range', + range: { collapsed: false }, }; const deleteSelectionSpy = spyOn(deleteSelectionsFile, 'deleteSelection').and.callFake( @@ -444,20 +407,18 @@ describe('ContentModelCopyPastePlugin |', () => { }; } ); - spyOn(contentModelToDomFile, 'contentModelToDom').and.returnValue( - selectionRangeExValue - ); + spyOn(contentModelToDomFile, 'contentModelToDom').and.returnValue(selectionValue); triggerPluginEventSpy.and.callThrough(); focusSpy.and.callThrough(); - selectSpy.and.callThrough(); + setDOMSelectionSpy.and.callThrough(); setContentModelSpy.and.callThrough(); // Act domEvents.cut?.({}); // Assert - expect(getSelectionRangeEx).toHaveBeenCalled(); + expect(getDOMSelectionSpy).toHaveBeenCalled(); expect(deleteSelectionSpy.calls.argsFor(0)[0]).toEqual(modelValue); expect(contentModelToDomFile.contentModelToDom).toHaveBeenCalledWith( document, @@ -469,12 +430,7 @@ describe('ContentModelCopyPastePlugin |', () => { expect(createContentModelSpy).toHaveBeenCalled(); expect(triggerPluginEventSpy).toHaveBeenCalledTimes(2); expect(focusSpy).toHaveBeenCalled(); - expect(selectSpy).toHaveBeenCalledWith( - selectionRangeExValue, - undefined, - undefined, - undefined - ); + expect(setDOMSelectionSpy).toHaveBeenCalledWith(selectionValue); // On Cut Spy expect(undoSnapShotSpy).toHaveBeenCalled(); @@ -485,15 +441,11 @@ describe('ContentModelCopyPastePlugin |', () => { // Arrange const table = document.createElement('table'); table.id = 'table'; - selectionRangeExValue = { - type: SelectionRangeTypes.TableSelection, - ranges: [new Range()], - areAllCollapsed: false, - coordinates: {}, + selectionValue = { + type: 'table', table, }; - spyOn(createRangeF, 'default').and.callThrough(); spyOn(deleteSelectionsFile, 'deleteSelection').and.returnValue({ deleteResult: DeleteResult.Range, insertPoint: null!, @@ -503,22 +455,21 @@ describe('ContentModelCopyPastePlugin |', () => { container.append(table); div.appendChild(container); - return selectionRangeExValue; + return selectionValue; }); spyOn(iterateSelectionsFile, 'iterateSelections').and.returnValue(undefined); spyOn(normalizeContentModel, 'normalizeContentModel'); triggerPluginEventSpy.and.callThrough(); focusSpy.and.callThrough(); - selectSpy.and.callThrough(); + setDOMSelectionSpy.and.callThrough(); setContentModelSpy.and.callThrough(); // Act domEvents.cut?.({}); // Assert - expect(createRangeF.default).toHaveBeenCalledWith(table.parentElement); - expect(getSelectionRangeEx).toHaveBeenCalled(); + expect(getDOMSelectionSpy).toHaveBeenCalled(); expect(contentModelToDomFile.contentModelToDom).toHaveBeenCalledWith( document, div, @@ -530,12 +481,7 @@ describe('ContentModelCopyPastePlugin |', () => { expect(triggerPluginEventSpy).toHaveBeenCalledTimes(2); expect(iterateSelectionsFile.iterateSelections).toHaveBeenCalled(); expect(focusSpy).toHaveBeenCalled(); - expect(selectSpy).toHaveBeenCalledWith( - selectionRangeExValue, - undefined, - undefined, - undefined - ); + expect(setDOMSelectionSpy).toHaveBeenCalledWith(selectionValue); // On Cut Spy expect(undoSnapShotSpy).toHaveBeenCalled(); @@ -548,36 +494,32 @@ describe('ContentModelCopyPastePlugin |', () => { // Arrange const image = document.createElement('image'); image.id = 'image'; - selectionRangeExValue = { - type: SelectionRangeTypes.ImageSelection, - ranges: [new Range()], - areAllCollapsed: false, + selectionValue = { + type: 'image', image, }; - spyOn(createRangeF, 'default').and.callThrough(); spyOn(deleteSelectionsFile, 'deleteSelection').and.returnValue({ deleteResult: DeleteResult.Range, insertPoint: null!, }); spyOn(contentModelToDomFile, 'contentModelToDom').and.callFake(() => { div.appendChild(image); - return selectionRangeExValue; + return selectionValue; }); spyOn(iterateSelectionsFile, 'iterateSelections').and.returnValue(undefined); spyOn(normalizeContentModel, 'normalizeContentModel'); triggerPluginEventSpy.and.callThrough(); focusSpy.and.callThrough(); - selectSpy.and.callThrough(); + setDOMSelectionSpy.and.callThrough(); setContentModelSpy.and.callThrough(); // Act domEvents.cut?.({}); // Assert - expect(createRangeF.default).toHaveBeenCalledWith(image); - expect(getSelectionRangeEx).toHaveBeenCalled(); + expect(getDOMSelectionSpy).toHaveBeenCalled(); expect(contentModelToDomFile.contentModelToDom).toHaveBeenCalledWith( document, div, @@ -588,12 +530,7 @@ describe('ContentModelCopyPastePlugin |', () => { expect(createContentModelSpy).toHaveBeenCalled(); expect(triggerPluginEventSpy).toHaveBeenCalledTimes(2); expect(focusSpy).toHaveBeenCalled(); - expect(selectSpy).toHaveBeenCalledWith( - selectionRangeExValue, - undefined, - undefined, - undefined - ); + expect(setDOMSelectionSpy).toHaveBeenCalledWith(selectionValue); // On Cut Spy expect(undoSnapShotSpy).toHaveBeenCalled(); @@ -607,7 +544,6 @@ describe('ContentModelCopyPastePlugin |', () => { let clipboardData = {}; it('Handle', () => { - editor.isFeatureEnabled = () => true; spyOn(PasteFile, 'default').and.callFake(() => {}); const preventDefaultSpy = jasmine.createSpy('preventDefaultPaste'); let clipboardEvent = { @@ -670,4 +606,28 @@ describe('ContentModelCopyPastePlugin |', () => { expect(preventDefaultSpy).toHaveBeenCalledTimes(1); }); }); + + it('onNodeCreated with table', () => { + const div = document.createElement('div'); + const table = document.createElement('table'); + + div.appendChild(table); + + onNodeCreated(null!, table); + + expect(div.innerHTML).toEqual('
'); + }); + + it('onNodeCreated with readonly element', () => { + const div = document.createElement('div'); + div.contentEditable = 'true'; + + const span = document.createElement('span'); + div.appendChild(span); + span.contentEditable = 'false'; + + onNodeCreated(null!, span); + + expect(div.innerHTML).toBe(''); + }); }); diff --git a/packages-content-model/roosterjs-content-model-editor/test/editor/plugins/ContentModelPastePluginTest.ts b/packages-content-model/roosterjs-content-model-editor/test/editor/plugins/ContentModelPastePluginTest.ts index 93c4849ef4e..33f91e4bd92 100644 --- a/packages-content-model/roosterjs-content-model-editor/test/editor/plugins/ContentModelPastePluginTest.ts +++ b/packages-content-model/roosterjs-content-model-editor/test/editor/plugins/ContentModelPastePluginTest.ts @@ -108,7 +108,7 @@ describe('Content Model Paste Plugin Test', () => { trustedHTMLHandler ); expect(addParser.default).toHaveBeenCalledTimes(DEFAULT_TIMES_ADD_PARSER_CALLED + 3); - expect(setProcessor.setProcessor).toHaveBeenCalledTimes(0); + expect(setProcessor.setProcessor).toHaveBeenCalledTimes(1); expect(chainSanitizerCallbackFile.default).toHaveBeenCalledTimes(1); }); @@ -141,7 +141,7 @@ describe('Content Model Paste Plugin Test', () => { trustedHTMLHandler ); expect(addParser.default).toHaveBeenCalledTimes(DEFAULT_TIMES_ADD_PARSER_CALLED + 1); - expect(setProcessor.setProcessor).toHaveBeenCalledTimes(0); + expect(setProcessor.setProcessor).toHaveBeenCalledTimes(1); expect(chainSanitizerCallbackFile.default).toHaveBeenCalledTimes(1); }); @@ -157,7 +157,7 @@ describe('Content Model Paste Plugin Test', () => { trustedHTMLHandler ); expect(addParser.default).toHaveBeenCalledTimes(DEFAULT_TIMES_ADD_PARSER_CALLED + 1); - expect(setProcessor.setProcessor).toHaveBeenCalledTimes(0); + expect(setProcessor.setProcessor).toHaveBeenCalledTimes(1); expect(chainSanitizerCallbackFile.default).toHaveBeenCalledTimes(1); }); diff --git a/packages-content-model/roosterjs-content-model-editor/test/editor/plugins/paste/e2e/cmPasteFromExcelOnlineTest.ts b/packages-content-model/roosterjs-content-model-editor/test/editor/plugins/paste/e2e/cmPasteFromExcelOnlineTest.ts index 657f14506eb..786f8263b4a 100644 --- a/packages-content-model/roosterjs-content-model-editor/test/editor/plugins/paste/e2e/cmPasteFromExcelOnlineTest.ts +++ b/packages-content-model/roosterjs-content-model-editor/test/editor/plugins/paste/e2e/cmPasteFromExcelOnlineTest.ts @@ -1,8 +1,9 @@ import * as processPastedContentFromExcel from '../../../../../lib/editor/plugins/PastePlugin/Excel/processPastedContentFromExcel'; import paste from '../../../../../lib/publicApi/utils/paste'; import { ClipboardData } from 'roosterjs-editor-types'; +import { expectEqual, initEditor } from './testUtils'; import { IContentModelEditor } from '../../../../../lib/publicTypes/IContentModelEditor'; -import { initEditor } from './testUtils'; +import { itChromeOnly } from 'roosterjs-editor-dom/test/DomTestHelper'; import { tableProcessor } from 'roosterjs-content-model-dom'; const ID = 'CM_Paste_From_ExcelOnline_E2E'; @@ -43,4 +44,1160 @@ describe(ID, () => { expect(processPastedContentFromExcel.processPastedContentFromExcel).toHaveBeenCalled(); }); + + itChromeOnly('E2E Table with table cells with text color', () => { + const CD = ({ + types: ['text/plain', 'text/html'], + text: + 'No.\tID\tWork Item Type\r\n1\tlink\tBug\r\n2\tlink\tBug\r\n3\tlink\tBug\r\n4\tlink\tBug\r\n5\tlink\tBug\r\n6\tlink\tBug\r\n7\tlink\tBug', + image: null, + files: [] as any, + rawHtml: + "\r\n\r\n
No.IDWork Item Type
1linkBug
2linkBug
3linkBug
4linkBug
5linkBug
6linkBug
7linkBug
\r\n\r\n", + customValues: {}, + pasteNativeEvent: true, + snapshotBeforePaste: '

', + }); + + paste(editor, CD); + + const model = editor.createContentModel({ + processorOverride: { + table: tableProcessor, + }, + }); + + expectEqual(model, { + blockGroupType: 'Document', + blocks: [ + { + widths: jasmine.anything() as any, + rows: [ + { + height: jasmine.anything() as any, + cells: [ + { + spanAbove: false, + spanLeft: false, + isHeader: false, + blockGroupType: 'TableCell', + blocks: [ + { + segments: [ + { + text: 'No.', + segmentType: 'Text', + format: { + fontFamily: 'Calibri, sans-serif', + fontSize: '11pt', + fontWeight: '700', + textColor: 'black', + }, + }, + ], + blockType: 'Paragraph', + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + }, + }, + ], + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + borderTop: '0.5pt solid', + borderRight: '0.5pt solid', + borderBottom: '0.5pt solid', + borderLeft: '0.5pt solid', + backgroundColor: 'white', + paddingTop: '1px', + paddingRight: '1px', + paddingLeft: '1px', + verticalAlign: 'middle', + width: '48pt', + height: '28.5pt', + }, + dataset: {}, + }, + { + spanAbove: false, + spanLeft: false, + isHeader: false, + blockGroupType: 'TableCell', + blocks: [ + { + segments: [ + { + text: 'ID', + segmentType: 'Text', + format: { + fontFamily: 'Calibri, sans-serif', + fontSize: '11pt', + fontWeight: '700', + textColor: 'black', + }, + }, + ], + blockType: 'Paragraph', + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + }, + }, + ], + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + borderTop: '0.5pt solid', + borderRight: '0.5pt solid', + borderBottom: '0.5pt solid', + borderLeft: '0.5pt solid', + backgroundColor: 'white', + paddingTop: '1px', + paddingRight: '1px', + paddingLeft: '1px', + verticalAlign: 'middle', + width: '52pt', + }, + dataset: {}, + }, + { + spanAbove: false, + spanLeft: false, + isHeader: false, + blockGroupType: 'TableCell', + blocks: [ + { + segments: [ + { + text: 'Work Item Type', + segmentType: 'Text', + format: { + fontFamily: 'Calibri, sans-serif', + fontSize: '11pt', + fontWeight: '700', + textColor: 'black', + }, + }, + ], + blockType: 'Paragraph', + format: { + textAlign: 'center', + whiteSpace: 'normal', + }, + }, + ], + format: { + textAlign: 'center', + whiteSpace: 'normal', + borderTop: '0.5pt solid', + borderRight: '0.5pt solid', + borderBottom: '0.5pt solid', + borderLeft: '0.5pt solid', + backgroundColor: 'white', + paddingTop: '1px', + paddingRight: '1px', + paddingLeft: '1px', + verticalAlign: 'middle', + width: '57pt', + }, + dataset: {}, + }, + ], + format: {}, + }, + { + height: jasmine.anything() as any, + cells: [ + { + spanAbove: false, + spanLeft: false, + isHeader: false, + blockGroupType: 'TableCell', + blocks: [ + { + segments: [ + { + text: '1', + segmentType: 'Text', + format: { + fontFamily: 'Calibri, sans-serif', + fontSize: '11pt', + fontWeight: '400', + textColor: 'black', + }, + }, + ], + blockType: 'Paragraph', + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + }, + }, + ], + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + borderTop: '0.5pt solid', + borderRight: '0.5pt solid', + borderBottom: '0.5pt solid', + borderLeft: '0.5pt solid', + backgroundColor: 'white', + paddingTop: '1px', + paddingRight: '1px', + paddingLeft: '1px', + verticalAlign: 'middle', + height: '30pt', + }, + dataset: {}, + }, + { + spanAbove: false, + spanLeft: false, + isHeader: false, + blockGroupType: 'TableCell', + blocks: [ + { + segments: [ + { + text: 'link', + segmentType: 'Text', + format: { + fontFamily: 'Calibri, sans-serif', + fontSize: '11pt', + fontWeight: '400', + textColor: 'rgb(5, 99, 193)', + underline: true, + }, + link: { + format: { + underline: true, + href: 'http://www.microsoft.com/', + }, + dataset: {}, + }, + }, + ], + blockType: 'Paragraph', + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + }, + }, + ], + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + borderTop: '0.5pt solid', + borderRight: '0.5pt solid', + borderBottom: '0.5pt solid', + borderLeft: '0.5pt solid', + backgroundColor: 'white', + paddingTop: '1px', + paddingRight: '1px', + paddingLeft: '1px', + verticalAlign: 'middle', + }, + dataset: {}, + }, + { + spanAbove: false, + spanLeft: false, + isHeader: false, + blockGroupType: 'TableCell', + blocks: [ + { + segments: [ + { + text: 'Bug', + segmentType: 'Text', + format: { + fontFamily: 'Calibri, sans-serif', + fontSize: '11pt', + fontWeight: '400', + textColor: 'rgb(219, 219, 219)', + }, + }, + ], + blockType: 'Paragraph', + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + }, + }, + ], + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + borderTop: '0.5pt solid', + borderRight: '0.5pt solid', + borderBottom: '0.5pt solid', + borderLeft: '0.5pt solid', + backgroundColor: 'white', + paddingTop: '1px', + paddingRight: '1px', + paddingLeft: '1px', + verticalAlign: 'middle', + }, + dataset: {}, + }, + ], + format: {}, + }, + { + height: jasmine.anything() as any, + cells: [ + { + spanAbove: false, + spanLeft: false, + isHeader: false, + blockGroupType: 'TableCell', + blocks: [ + { + segments: [ + { + text: '2', + segmentType: 'Text', + format: { + fontFamily: 'Calibri, sans-serif', + fontSize: '11pt', + fontWeight: '400', + textColor: 'black', + }, + }, + ], + blockType: 'Paragraph', + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + }, + }, + ], + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + borderTop: '0.5pt solid', + borderRight: '0.5pt solid', + borderBottom: '0.5pt solid', + borderLeft: '0.5pt solid', + backgroundColor: 'white', + paddingTop: '1px', + paddingRight: '1px', + paddingLeft: '1px', + verticalAlign: 'middle', + height: '30pt', + }, + dataset: {}, + }, + { + spanAbove: false, + spanLeft: false, + isHeader: false, + blockGroupType: 'TableCell', + blocks: [ + { + segments: [ + { + text: 'link', + segmentType: 'Text', + format: { + fontFamily: 'Calibri, sans-serif', + fontSize: '11pt', + fontWeight: '400', + textColor: 'rgb(5, 99, 193)', + underline: true, + }, + link: { + format: { + underline: true, + href: 'http://www.microsoft.com/', + }, + dataset: {}, + }, + }, + ], + blockType: 'Paragraph', + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + }, + }, + ], + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + borderTop: '0.5pt solid', + borderRight: '0.5pt solid', + borderBottom: '0.5pt solid', + borderLeft: '0.5pt solid', + backgroundColor: 'white', + paddingTop: '1px', + paddingRight: '1px', + paddingLeft: '1px', + verticalAlign: 'middle', + }, + dataset: {}, + }, + { + spanAbove: false, + spanLeft: false, + isHeader: false, + blockGroupType: 'TableCell', + blocks: [ + { + segments: [ + { + text: 'Bug', + segmentType: 'Text', + format: { + fontFamily: 'Calibri, sans-serif', + fontSize: '11pt', + fontWeight: '400', + textColor: 'rgb(219, 219, 219)', + }, + }, + ], + blockType: 'Paragraph', + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + }, + }, + ], + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + borderTop: '0.5pt solid', + borderRight: '0.5pt solid', + borderBottom: '0.5pt solid', + borderLeft: '0.5pt solid', + backgroundColor: 'white', + paddingTop: '1px', + paddingRight: '1px', + paddingLeft: '1px', + verticalAlign: 'middle', + }, + dataset: {}, + }, + ], + format: {}, + }, + { + height: jasmine.anything() as any, + cells: [ + { + spanAbove: false, + spanLeft: false, + isHeader: false, + blockGroupType: 'TableCell', + blocks: [ + { + segments: [ + { + text: '3', + segmentType: 'Text', + format: { + fontFamily: 'Calibri, sans-serif', + fontSize: '11pt', + fontWeight: '400', + textColor: 'black', + }, + }, + ], + blockType: 'Paragraph', + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + }, + }, + ], + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + borderTop: '0.5pt solid', + borderRight: '0.5pt solid', + borderBottom: '0.5pt solid', + borderLeft: '0.5pt solid', + backgroundColor: 'white', + paddingTop: '1px', + paddingRight: '1px', + paddingLeft: '1px', + verticalAlign: 'middle', + height: '30pt', + }, + dataset: {}, + }, + { + spanAbove: false, + spanLeft: false, + isHeader: false, + blockGroupType: 'TableCell', + blocks: [ + { + segments: [ + { + text: 'link', + segmentType: 'Text', + format: { + fontFamily: 'Calibri, sans-serif', + fontSize: '11pt', + fontWeight: '400', + textColor: 'rgb(5, 99, 193)', + underline: true, + }, + link: { + format: { + underline: true, + href: 'http://www.microsoft.com/', + }, + dataset: {}, + }, + }, + ], + blockType: 'Paragraph', + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + }, + }, + ], + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + borderTop: '0.5pt solid', + borderRight: '0.5pt solid', + borderBottom: '0.5pt solid', + borderLeft: '0.5pt solid', + backgroundColor: 'white', + paddingTop: '1px', + paddingRight: '1px', + paddingLeft: '1px', + verticalAlign: 'middle', + }, + dataset: {}, + }, + { + spanAbove: false, + spanLeft: false, + isHeader: false, + blockGroupType: 'TableCell', + blocks: [ + { + segments: [ + { + text: 'Bug', + segmentType: 'Text', + format: { + fontFamily: 'Calibri, sans-serif', + fontSize: '11pt', + fontWeight: '400', + textColor: 'rgb(219, 219, 219)', + }, + }, + ], + blockType: 'Paragraph', + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + }, + }, + ], + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + borderTop: '0.5pt solid', + borderRight: '0.5pt solid', + borderBottom: '0.5pt solid', + borderLeft: '0.5pt solid', + backgroundColor: 'white', + paddingTop: '1px', + paddingRight: '1px', + paddingLeft: '1px', + verticalAlign: 'middle', + }, + dataset: {}, + }, + ], + format: {}, + }, + { + height: jasmine.anything() as any, + cells: [ + { + spanAbove: false, + spanLeft: false, + isHeader: false, + blockGroupType: 'TableCell', + blocks: [ + { + segments: [ + { + text: '4', + segmentType: 'Text', + format: { + fontFamily: 'Calibri, sans-serif', + fontSize: '11pt', + fontWeight: '400', + textColor: 'black', + }, + }, + ], + blockType: 'Paragraph', + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + }, + }, + ], + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + borderTop: '0.5pt solid', + borderRight: '0.5pt solid', + borderBottom: '0.5pt solid', + borderLeft: '0.5pt solid', + backgroundColor: 'white', + paddingTop: '1px', + paddingRight: '1px', + paddingLeft: '1px', + verticalAlign: 'middle', + height: '30pt', + }, + dataset: {}, + }, + { + spanAbove: false, + spanLeft: false, + isHeader: false, + blockGroupType: 'TableCell', + blocks: [ + { + segments: [ + { + text: 'link', + segmentType: 'Text', + format: { + fontFamily: 'Calibri, sans-serif', + fontSize: '11pt', + fontWeight: '400', + textColor: 'rgb(5, 99, 193)', + underline: true, + }, + link: { + format: { + underline: true, + href: 'http://www.microsoft.com/', + }, + dataset: {}, + }, + }, + ], + blockType: 'Paragraph', + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + }, + }, + ], + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + borderTop: '0.5pt solid', + borderRight: '0.5pt solid', + borderBottom: '0.5pt solid', + borderLeft: '0.5pt solid', + backgroundColor: 'white', + paddingTop: '1px', + paddingRight: '1px', + paddingLeft: '1px', + verticalAlign: 'middle', + }, + dataset: {}, + }, + { + spanAbove: false, + spanLeft: false, + isHeader: false, + blockGroupType: 'TableCell', + blocks: [ + { + segments: [ + { + text: 'Bug', + segmentType: 'Text', + format: { + fontFamily: 'Calibri, sans-serif', + fontSize: '11pt', + fontWeight: '400', + textColor: 'rgb(219, 219, 219)', + }, + }, + ], + blockType: 'Paragraph', + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + }, + }, + ], + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + borderTop: '0.5pt solid', + borderRight: '0.5pt solid', + borderBottom: '0.5pt solid', + borderLeft: '0.5pt solid', + backgroundColor: 'white', + paddingTop: '1px', + paddingRight: '1px', + paddingLeft: '1px', + verticalAlign: 'middle', + }, + dataset: {}, + }, + ], + format: {}, + }, + { + height: jasmine.anything() as any, + cells: [ + { + spanAbove: false, + spanLeft: false, + isHeader: false, + blockGroupType: 'TableCell', + blocks: [ + { + segments: [ + { + text: '5', + segmentType: 'Text', + format: { + fontFamily: 'Calibri, sans-serif', + fontSize: '11pt', + fontWeight: '400', + textColor: 'black', + }, + }, + ], + blockType: 'Paragraph', + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + }, + }, + ], + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + borderTop: '0.5pt solid', + borderRight: '0.5pt solid', + borderBottom: '0.5pt solid', + borderLeft: '0.5pt solid', + backgroundColor: 'white', + paddingTop: '1px', + paddingRight: '1px', + paddingLeft: '1px', + verticalAlign: 'middle', + height: '30pt', + }, + dataset: {}, + }, + { + spanAbove: false, + spanLeft: false, + isHeader: false, + blockGroupType: 'TableCell', + blocks: [ + { + segments: [ + { + text: 'link', + segmentType: 'Text', + format: { + fontFamily: 'Calibri, sans-serif', + fontSize: '11pt', + fontWeight: '400', + textColor: 'rgb(5, 99, 193)', + underline: true, + }, + link: { + format: { + underline: true, + href: 'http://www.microsoft.com/', + }, + dataset: {}, + }, + }, + ], + blockType: 'Paragraph', + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + }, + }, + ], + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + borderTop: '0.5pt solid', + borderRight: '0.5pt solid', + borderBottom: '0.5pt solid', + borderLeft: '0.5pt solid', + backgroundColor: 'white', + paddingTop: '1px', + paddingRight: '1px', + paddingLeft: '1px', + verticalAlign: 'middle', + }, + dataset: {}, + }, + { + spanAbove: false, + spanLeft: false, + isHeader: false, + blockGroupType: 'TableCell', + blocks: [ + { + segments: [ + { + text: 'Bug', + segmentType: 'Text', + format: { + fontFamily: 'Calibri, sans-serif', + fontSize: '11pt', + fontWeight: '400', + textColor: 'rgb(219, 219, 219)', + }, + }, + ], + blockType: 'Paragraph', + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + }, + }, + ], + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + borderTop: '0.5pt solid', + borderRight: '0.5pt solid', + borderBottom: '0.5pt solid', + borderLeft: '0.5pt solid', + backgroundColor: 'white', + paddingTop: '1px', + paddingRight: '1px', + paddingLeft: '1px', + verticalAlign: 'middle', + }, + dataset: {}, + }, + ], + format: {}, + }, + { + height: jasmine.anything() as any, + cells: [ + { + spanAbove: false, + spanLeft: false, + isHeader: false, + blockGroupType: 'TableCell', + blocks: [ + { + segments: [ + { + text: '6', + segmentType: 'Text', + format: { + fontFamily: 'Calibri, sans-serif', + fontSize: '11pt', + fontWeight: '400', + textColor: 'black', + }, + }, + ], + blockType: 'Paragraph', + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + }, + }, + ], + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + borderTop: '0.5pt solid', + borderRight: '0.5pt solid', + borderBottom: '0.5pt solid', + borderLeft: '0.5pt solid', + backgroundColor: 'white', + paddingTop: '1px', + paddingRight: '1px', + paddingLeft: '1px', + verticalAlign: 'middle', + height: '30pt', + }, + dataset: {}, + }, + { + spanAbove: false, + spanLeft: false, + isHeader: false, + blockGroupType: 'TableCell', + blocks: [ + { + segments: [ + { + text: 'link', + segmentType: 'Text', + format: { + fontFamily: 'Calibri, sans-serif', + fontSize: '11pt', + fontWeight: '400', + textColor: 'rgb(5, 99, 193)', + underline: true, + }, + link: { + format: { + underline: true, + href: 'http://www.microsoft.com/', + }, + dataset: {}, + }, + }, + ], + blockType: 'Paragraph', + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + }, + }, + ], + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + borderTop: '0.5pt solid', + borderRight: '0.5pt solid', + borderBottom: '0.5pt solid', + borderLeft: '0.5pt solid', + backgroundColor: 'white', + paddingTop: '1px', + paddingRight: '1px', + paddingLeft: '1px', + verticalAlign: 'middle', + }, + dataset: {}, + }, + { + spanAbove: false, + spanLeft: false, + isHeader: false, + blockGroupType: 'TableCell', + blocks: [ + { + segments: [ + { + text: 'Bug', + segmentType: 'Text', + format: { + fontFamily: 'Calibri, sans-serif', + fontSize: '11pt', + fontWeight: '400', + textColor: 'rgb(219, 219, 219)', + }, + }, + ], + blockType: 'Paragraph', + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + }, + }, + ], + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + borderTop: '0.5pt solid', + borderRight: '0.5pt solid', + borderBottom: '0.5pt solid', + borderLeft: '0.5pt solid', + backgroundColor: 'white', + paddingTop: '1px', + paddingRight: '1px', + paddingLeft: '1px', + verticalAlign: 'middle', + }, + dataset: {}, + }, + ], + format: {}, + }, + { + height: jasmine.anything() as any, + cells: [ + { + spanAbove: false, + spanLeft: false, + isHeader: false, + blockGroupType: 'TableCell', + blocks: [ + { + segments: [ + { + text: '7', + segmentType: 'Text', + format: { + fontFamily: 'Calibri, sans-serif', + fontSize: '11pt', + fontWeight: '400', + textColor: 'black', + }, + }, + ], + blockType: 'Paragraph', + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + }, + }, + ], + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + borderTop: '0.5pt solid', + borderRight: '0.5pt solid', + borderBottom: '0.5pt solid', + borderLeft: '0.5pt solid', + backgroundColor: 'white', + paddingTop: '1px', + paddingRight: '1px', + paddingLeft: '1px', + verticalAlign: 'middle', + height: '30pt', + }, + dataset: {}, + }, + { + spanAbove: false, + spanLeft: false, + isHeader: false, + blockGroupType: 'TableCell', + blocks: [ + { + segments: [ + { + text: 'link', + segmentType: 'Text', + format: { + fontFamily: 'Calibri, sans-serif', + fontSize: '11pt', + fontWeight: '400', + textColor: 'rgb(5, 99, 193)', + underline: true, + }, + link: { + format: { + underline: true, + href: 'http://www.microsoft.com/', + }, + dataset: {}, + }, + }, + ], + blockType: 'Paragraph', + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + }, + }, + ], + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + borderTop: '0.5pt solid', + borderRight: '0.5pt solid', + borderBottom: '0.5pt solid', + borderLeft: '0.5pt solid', + backgroundColor: 'white', + paddingTop: '1px', + paddingRight: '1px', + paddingLeft: '1px', + verticalAlign: 'middle', + }, + dataset: {}, + }, + { + spanAbove: false, + spanLeft: false, + isHeader: false, + blockGroupType: 'TableCell', + blocks: [ + { + segments: [ + { + text: 'Bug', + segmentType: 'Text', + format: { + fontFamily: 'Calibri, sans-serif', + fontSize: '11pt', + fontWeight: '400', + textColor: 'rgb(219, 219, 219)', + }, + }, + ], + blockType: 'Paragraph', + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + }, + }, + ], + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + borderTop: '0.5pt solid', + borderRight: '0.5pt solid', + borderBottom: '0.5pt solid', + borderLeft: '0.5pt solid', + backgroundColor: 'white', + paddingTop: '1px', + paddingRight: '1px', + paddingLeft: '1px', + verticalAlign: 'middle', + }, + dataset: {}, + }, + ], + format: {}, + }, + ], + blockType: 'Table', + format: { + width: '157pt' as any, + useBorderBox: true, + borderCollapse: true, + } as any, + dataset: {}, + }, + { + segments: [ + { + isSelected: true, + segmentType: 'SelectionMarker', + format: {}, + }, + { + segmentType: 'Br', + format: {}, + }, + ], + blockType: 'Paragraph', + format: {}, + }, + ], + format: {}, + }); + }); }); diff --git a/packages-content-model/roosterjs-content-model-editor/test/editor/plugins/paste/e2e/cmPasteFromExcelTest.ts b/packages-content-model/roosterjs-content-model-editor/test/editor/plugins/paste/e2e/cmPasteFromExcelTest.ts index c5db86d346e..9881c50ce90 100644 --- a/packages-content-model/roosterjs-content-model-editor/test/editor/plugins/paste/e2e/cmPasteFromExcelTest.ts +++ b/packages-content-model/roosterjs-content-model-editor/test/editor/plugins/paste/e2e/cmPasteFromExcelTest.ts @@ -2,8 +2,9 @@ import * as processPastedContentFromExcel from '../../../../../lib/editor/plugin import paste from '../../../../../lib/publicApi/utils/paste'; import { Browser } from 'roosterjs-editor-dom'; import { ClipboardData } from 'roosterjs-editor-types'; +import { expectEqual, initEditor } from './testUtils'; import { IContentModelEditor } from '../../../../../lib/publicTypes/IContentModelEditor'; -import { initEditor } from './testUtils'; +import { itChromeOnly } from 'roosterjs-editor-dom/test/DomTestHelper'; import { tableProcessor } from 'roosterjs-content-model-dom'; const ID = 'CM_Paste_From_Excel_E2E'; @@ -44,7 +45,7 @@ describe(ID, () => { expect(processPastedContentFromExcel.processPastedContentFromExcel).toHaveBeenCalled(); }); - it('E2E paste a simage', () => { + it('E2E paste as image', () => { if (Browser.isFirefox) { return; } @@ -67,7 +68,10 @@ describe(ID, () => { { segmentType: 'Image', src: 'https://github.com/microsoft/roosterjs', - format: { maxWidth: '100%' }, + format: { + maxWidth: '100px', + maxHeight: '100px', + }, dataset: {}, }, { @@ -91,4 +95,1124 @@ describe(ID, () => { }); expect(processPastedContentFromExcel.processPastedContentFromExcel).not.toHaveBeenCalled(); }); + + itChromeOnly('Copy Table with text color in cell', () => { + const CD = ({ + types: ['image/png', 'text/plain', 'text/html'], + text: + 'No.\tID\tWork Item Type\r\n1\tlink\tBug\r\n2\tlink\tBug\r\n3\tlink\tBug\r\n4\tlink\tBug\r\n5\tlink\tBug\r\n6\tlink\tBug\r\n7\tlink\tBug\r\n', + image: {}, + files: [], + rawHtml: + "\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n\r\n
No.IDWork Item Type
1linkBug
2linkBug
3linkBug
4linkBug
5linkBug
6linkBug
7linkBug
\r\n\r\n\r\n\r\n\r\n", + customValues: {}, + pasteNativeEvent: true, + imageDataUri: '', + snapshotBeforePaste: '
', + }); + + paste(editor, CD); + + const model = editor.createContentModel({ + processorOverride: { + table: tableProcessor, + }, + }); + + expectEqual(model, { + blockGroupType: 'Document', + blocks: [ + { + widths: jasmine.anything() as any, + rows: [ + { + height: jasmine.anything() as any, + cells: [ + { + spanAbove: false, + spanLeft: false, + isHeader: false, + blockGroupType: 'TableCell', + blocks: [ + { + segments: [ + { + text: 'No.', + segmentType: 'Text', + format: { + fontFamily: 'Calibri, sans-serif', + fontSize: '11pt', + fontWeight: '700', + textColor: 'black', + }, + }, + ], + blockType: 'Paragraph', + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + }, + }, + ], + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + borderTop: '0.5pt solid', + borderRight: '0.5pt solid', + borderBottom: '0.5pt solid', + borderLeft: '0.5pt solid', + backgroundColor: 'white', + paddingTop: '1px', + paddingRight: '1px', + paddingLeft: '1px', + verticalAlign: 'middle', + width: '52pt', + height: '28.5pt', + }, + dataset: {}, + }, + { + spanAbove: false, + spanLeft: false, + isHeader: false, + blockGroupType: 'TableCell', + blocks: [ + { + segments: [ + { + text: 'ID', + segmentType: 'Text', + format: { + fontFamily: 'Calibri, sans-serif', + fontSize: '11pt', + fontWeight: '700', + textColor: 'black', + }, + }, + ], + blockType: 'Paragraph', + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + }, + }, + ], + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + borderTop: '0.5pt solid', + borderRight: '0.5pt solid', + borderBottom: '0.5pt solid', + backgroundColor: 'white', + paddingTop: '1px', + paddingRight: '1px', + paddingLeft: '1px', + verticalAlign: 'middle', + width: '56pt', + }, + dataset: {}, + }, + { + spanAbove: false, + spanLeft: false, + isHeader: false, + blockGroupType: 'TableCell', + blocks: [ + { + segments: [ + { + text: 'Work Item Type', + segmentType: 'Text', + format: { + fontFamily: 'Calibri, sans-serif', + fontSize: '11pt', + fontWeight: '700', + textColor: 'black', + }, + }, + ], + blockType: 'Paragraph', + format: { + textAlign: 'center', + whiteSpace: 'normal', + }, + }, + ], + format: { + textAlign: 'center', + whiteSpace: 'normal', + borderTop: '0.5pt solid', + borderRight: '0.5pt solid', + borderBottom: '0.5pt solid', + backgroundColor: 'white', + paddingTop: '1px', + paddingRight: '1px', + paddingLeft: '1px', + verticalAlign: 'middle', + width: '62pt', + }, + dataset: {}, + }, + ], + format: {}, + }, + { + height: jasmine.anything() as any, + cells: [ + { + spanAbove: false, + spanLeft: false, + isHeader: false, + blockGroupType: 'TableCell', + blocks: [ + { + segments: [ + { + text: '1', + segmentType: 'Text', + format: { + fontFamily: 'Calibri, sans-serif', + fontSize: '11pt', + fontWeight: '400', + textColor: 'black', + }, + }, + ], + blockType: 'Paragraph', + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + }, + }, + ], + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + borderRight: '0.5pt solid', + borderBottom: '0.5pt solid', + borderLeft: '0.5pt solid', + backgroundColor: 'white', + paddingTop: '1px', + paddingRight: '1px', + paddingLeft: '1px', + verticalAlign: 'middle', + height: '30pt', + }, + dataset: {}, + }, + { + spanAbove: false, + spanLeft: false, + isHeader: false, + blockGroupType: 'TableCell', + blocks: [ + { + segments: [ + { + text: 'link', + segmentType: 'Text', + format: { + fontFamily: 'Calibri, sans-serif', + fontSize: '11pt', + fontWeight: '400', + textColor: 'rgb(5, 99, 193)', + underline: true, + }, + link: { + format: { + underline: true, + href: 'http://www.microsoft.com/', + }, + dataset: {}, + }, + }, + ], + blockType: 'Paragraph', + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + }, + }, + ], + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + borderRight: '0.5pt solid', + borderBottom: '0.5pt solid', + backgroundColor: 'white', + paddingTop: '1px', + paddingRight: '1px', + paddingLeft: '1px', + verticalAlign: 'middle', + }, + dataset: {}, + }, + { + spanAbove: false, + spanLeft: false, + isHeader: false, + blockGroupType: 'TableCell', + blocks: [ + { + segments: [ + { + text: 'Bug', + segmentType: 'Text', + format: { + fontFamily: 'Calibri, sans-serif', + fontSize: '11pt', + fontWeight: '400', + textColor: 'rgb(219, 219, 219)', + }, + }, + ], + blockType: 'Paragraph', + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + }, + }, + ], + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + borderRight: '0.5pt solid', + borderBottom: '0.5pt solid', + backgroundColor: 'white', + paddingTop: '1px', + paddingRight: '1px', + paddingLeft: '1px', + verticalAlign: 'middle', + }, + dataset: {}, + }, + ], + format: {}, + }, + { + height: jasmine.anything() as any, + cells: [ + { + spanAbove: false, + spanLeft: false, + isHeader: false, + blockGroupType: 'TableCell', + blocks: [ + { + segments: [ + { + text: '2', + segmentType: 'Text', + format: { + fontFamily: 'Calibri, sans-serif', + fontSize: '11pt', + fontWeight: '400', + textColor: 'black', + }, + }, + ], + blockType: 'Paragraph', + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + }, + }, + ], + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + borderRight: '0.5pt solid', + borderBottom: '0.5pt solid', + borderLeft: '0.5pt solid', + backgroundColor: 'white', + paddingTop: '1px', + paddingRight: '1px', + paddingLeft: '1px', + verticalAlign: 'middle', + height: '30pt', + }, + dataset: {}, + }, + { + spanAbove: false, + spanLeft: false, + isHeader: false, + blockGroupType: 'TableCell', + blocks: [ + { + segments: [ + { + text: 'link', + segmentType: 'Text', + format: { + fontFamily: 'Calibri, sans-serif', + fontSize: '11pt', + fontWeight: '400', + textColor: 'rgb(5, 99, 193)', + underline: true, + }, + link: { + format: { + underline: true, + href: 'http://www.microsoft.com/', + }, + dataset: {}, + }, + }, + ], + blockType: 'Paragraph', + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + }, + }, + ], + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + borderRight: '0.5pt solid', + borderBottom: '0.5pt solid', + backgroundColor: 'white', + paddingTop: '1px', + paddingRight: '1px', + paddingLeft: '1px', + verticalAlign: 'middle', + }, + dataset: {}, + }, + { + spanAbove: false, + spanLeft: false, + isHeader: false, + blockGroupType: 'TableCell', + blocks: [ + { + segments: [ + { + text: 'Bug', + segmentType: 'Text', + format: { + fontFamily: 'Calibri, sans-serif', + fontSize: '11pt', + fontWeight: '400', + textColor: 'rgb(219, 219, 219)', + }, + }, + ], + blockType: 'Paragraph', + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + }, + }, + ], + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + borderRight: '0.5pt solid', + borderBottom: '0.5pt solid', + backgroundColor: 'white', + paddingTop: '1px', + paddingRight: '1px', + paddingLeft: '1px', + verticalAlign: 'middle', + }, + dataset: {}, + }, + ], + format: {}, + }, + { + height: jasmine.anything() as any, + cells: [ + { + spanAbove: false, + spanLeft: false, + isHeader: false, + blockGroupType: 'TableCell', + blocks: [ + { + segments: [ + { + text: '3', + segmentType: 'Text', + format: { + fontFamily: 'Calibri, sans-serif', + fontSize: '11pt', + fontWeight: '400', + textColor: 'black', + }, + }, + ], + blockType: 'Paragraph', + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + }, + }, + ], + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + borderRight: '0.5pt solid', + borderBottom: '0.5pt solid', + borderLeft: '0.5pt solid', + backgroundColor: 'white', + paddingTop: '1px', + paddingRight: '1px', + paddingLeft: '1px', + verticalAlign: 'middle', + height: '30pt', + }, + dataset: {}, + }, + { + spanAbove: false, + spanLeft: false, + isHeader: false, + blockGroupType: 'TableCell', + blocks: [ + { + segments: [ + { + text: 'link', + segmentType: 'Text', + format: { + fontFamily: 'Calibri, sans-serif', + fontSize: '11pt', + fontWeight: '400', + textColor: 'rgb(5, 99, 193)', + underline: true, + }, + link: { + format: { + underline: true, + href: 'http://www.microsoft.com/', + }, + dataset: {}, + }, + }, + ], + blockType: 'Paragraph', + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + }, + }, + ], + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + borderRight: '0.5pt solid', + borderBottom: '0.5pt solid', + backgroundColor: 'white', + paddingTop: '1px', + paddingRight: '1px', + paddingLeft: '1px', + verticalAlign: 'middle', + }, + dataset: {}, + }, + { + spanAbove: false, + spanLeft: false, + isHeader: false, + blockGroupType: 'TableCell', + blocks: [ + { + segments: [ + { + text: 'Bug', + segmentType: 'Text', + format: { + fontFamily: 'Calibri, sans-serif', + fontSize: '11pt', + fontWeight: '400', + textColor: 'rgb(219, 219, 219)', + }, + }, + ], + blockType: 'Paragraph', + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + }, + }, + ], + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + borderRight: '0.5pt solid', + borderBottom: '0.5pt solid', + backgroundColor: 'white', + paddingTop: '1px', + paddingRight: '1px', + paddingLeft: '1px', + verticalAlign: 'middle', + }, + dataset: {}, + }, + ], + format: {}, + }, + { + height: jasmine.anything() as any, + cells: [ + { + spanAbove: false, + spanLeft: false, + isHeader: false, + blockGroupType: 'TableCell', + blocks: [ + { + segments: [ + { + text: '4', + segmentType: 'Text', + format: { + fontFamily: 'Calibri, sans-serif', + fontSize: '11pt', + fontWeight: '400', + textColor: 'black', + }, + }, + ], + blockType: 'Paragraph', + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + }, + }, + ], + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + borderRight: '0.5pt solid', + borderBottom: '0.5pt solid', + borderLeft: '0.5pt solid', + backgroundColor: 'white', + paddingTop: '1px', + paddingRight: '1px', + paddingLeft: '1px', + verticalAlign: 'middle', + height: '30pt', + }, + dataset: {}, + }, + { + spanAbove: false, + spanLeft: false, + isHeader: false, + blockGroupType: 'TableCell', + blocks: [ + { + segments: [ + { + text: 'link', + segmentType: 'Text', + format: { + fontFamily: 'Calibri, sans-serif', + fontSize: '11pt', + fontWeight: '400', + textColor: 'rgb(5, 99, 193)', + underline: true, + }, + link: { + format: { + underline: true, + href: 'http://www.microsoft.com/', + }, + dataset: {}, + }, + }, + ], + blockType: 'Paragraph', + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + }, + }, + ], + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + borderRight: '0.5pt solid', + borderBottom: '0.5pt solid', + backgroundColor: 'white', + paddingTop: '1px', + paddingRight: '1px', + paddingLeft: '1px', + verticalAlign: 'middle', + }, + dataset: {}, + }, + { + spanAbove: false, + spanLeft: false, + isHeader: false, + blockGroupType: 'TableCell', + blocks: [ + { + segments: [ + { + text: 'Bug', + segmentType: 'Text', + format: { + fontFamily: 'Calibri, sans-serif', + fontSize: '11pt', + fontWeight: '400', + textColor: 'rgb(219, 219, 219)', + }, + }, + ], + blockType: 'Paragraph', + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + }, + }, + ], + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + borderRight: '0.5pt solid', + borderBottom: '0.5pt solid', + backgroundColor: 'white', + paddingTop: '1px', + paddingRight: '1px', + paddingLeft: '1px', + verticalAlign: 'middle', + }, + dataset: {}, + }, + ], + format: {}, + }, + { + height: jasmine.anything() as any, + cells: [ + { + spanAbove: false, + spanLeft: false, + isHeader: false, + blockGroupType: 'TableCell', + blocks: [ + { + segments: [ + { + text: '5', + segmentType: 'Text', + format: { + fontFamily: 'Calibri, sans-serif', + fontSize: '11pt', + fontWeight: '400', + textColor: 'black', + }, + }, + ], + blockType: 'Paragraph', + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + }, + }, + ], + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + borderRight: '0.5pt solid', + borderBottom: '0.5pt solid', + borderLeft: '0.5pt solid', + backgroundColor: 'white', + paddingTop: '1px', + paddingRight: '1px', + paddingLeft: '1px', + verticalAlign: 'middle', + height: '30pt', + }, + dataset: {}, + }, + { + spanAbove: false, + spanLeft: false, + isHeader: false, + blockGroupType: 'TableCell', + blocks: [ + { + segments: [ + { + text: 'link', + segmentType: 'Text', + format: { + fontFamily: 'Calibri, sans-serif', + fontSize: '11pt', + fontWeight: '400', + textColor: 'rgb(5, 99, 193)', + underline: true, + }, + link: { + format: { + underline: true, + href: 'http://www.microsoft.com/', + }, + dataset: {}, + }, + }, + ], + blockType: 'Paragraph', + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + }, + }, + ], + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + borderRight: '0.5pt solid', + borderBottom: '0.5pt solid', + backgroundColor: 'white', + paddingTop: '1px', + paddingRight: '1px', + paddingLeft: '1px', + verticalAlign: 'middle', + }, + dataset: {}, + }, + { + spanAbove: false, + spanLeft: false, + isHeader: false, + blockGroupType: 'TableCell', + blocks: [ + { + segments: [ + { + text: 'Bug', + segmentType: 'Text', + format: { + fontFamily: 'Calibri, sans-serif', + fontSize: '11pt', + fontWeight: '400', + textColor: 'rgb(219, 219, 219)', + }, + }, + ], + blockType: 'Paragraph', + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + }, + }, + ], + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + borderRight: '0.5pt solid', + borderBottom: '0.5pt solid', + backgroundColor: 'white', + paddingTop: '1px', + paddingRight: '1px', + paddingLeft: '1px', + verticalAlign: 'middle', + }, + dataset: {}, + }, + ], + format: {}, + }, + { + height: jasmine.anything() as any, + cells: [ + { + spanAbove: false, + spanLeft: false, + isHeader: false, + blockGroupType: 'TableCell', + blocks: [ + { + segments: [ + { + text: '6', + segmentType: 'Text', + format: { + fontFamily: 'Calibri, sans-serif', + fontSize: '11pt', + fontWeight: '400', + textColor: 'black', + }, + }, + ], + blockType: 'Paragraph', + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + }, + }, + ], + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + borderRight: '0.5pt solid', + borderBottom: '0.5pt solid', + borderLeft: '0.5pt solid', + backgroundColor: 'white', + paddingTop: '1px', + paddingRight: '1px', + paddingLeft: '1px', + verticalAlign: 'middle', + height: '30pt', + }, + dataset: {}, + }, + { + spanAbove: false, + spanLeft: false, + isHeader: false, + blockGroupType: 'TableCell', + blocks: [ + { + segments: [ + { + text: 'link', + segmentType: 'Text', + format: { + fontFamily: 'Calibri, sans-serif', + fontSize: '11pt', + fontWeight: '400', + textColor: 'rgb(5, 99, 193)', + underline: true, + }, + link: { + format: { + underline: true, + href: 'http://www.microsoft.com/', + }, + dataset: {}, + }, + }, + ], + blockType: 'Paragraph', + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + }, + }, + ], + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + borderRight: '0.5pt solid', + borderBottom: '0.5pt solid', + backgroundColor: 'white', + paddingTop: '1px', + paddingRight: '1px', + paddingLeft: '1px', + verticalAlign: 'middle', + }, + dataset: {}, + }, + { + spanAbove: false, + spanLeft: false, + isHeader: false, + blockGroupType: 'TableCell', + blocks: [ + { + segments: [ + { + text: 'Bug', + segmentType: 'Text', + format: { + fontFamily: 'Calibri, sans-serif', + fontSize: '11pt', + fontWeight: '400', + textColor: 'rgb(219, 219, 219)', + }, + }, + ], + blockType: 'Paragraph', + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + }, + }, + ], + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + borderRight: '0.5pt solid', + borderBottom: '0.5pt solid', + backgroundColor: 'white', + paddingTop: '1px', + paddingRight: '1px', + paddingLeft: '1px', + verticalAlign: 'middle', + }, + dataset: {}, + }, + ], + format: {}, + }, + { + height: jasmine.anything() as any, + cells: [ + { + spanAbove: false, + spanLeft: false, + isHeader: false, + blockGroupType: 'TableCell', + blocks: [ + { + segments: [ + { + text: '7', + segmentType: 'Text', + format: { + fontFamily: 'Calibri, sans-serif', + fontSize: '11pt', + fontWeight: '400', + textColor: 'black', + }, + }, + ], + blockType: 'Paragraph', + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + }, + }, + ], + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + borderRight: '0.5pt solid', + borderBottom: '0.5pt solid', + borderLeft: '0.5pt solid', + backgroundColor: 'white', + paddingTop: '1px', + paddingRight: '1px', + paddingLeft: '1px', + verticalAlign: 'middle', + height: '30pt', + }, + dataset: {}, + }, + { + spanAbove: false, + spanLeft: false, + isHeader: false, + blockGroupType: 'TableCell', + blocks: [ + { + segments: [ + { + text: 'link', + segmentType: 'Text', + format: { + fontFamily: 'Calibri, sans-serif', + fontSize: '11pt', + fontWeight: '400', + textColor: 'rgb(5, 99, 193)', + underline: true, + }, + link: { + format: { + underline: true, + href: 'http://www.microsoft.com/', + }, + dataset: {}, + }, + }, + ], + blockType: 'Paragraph', + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + }, + }, + ], + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + borderRight: '0.5pt solid', + borderBottom: '0.5pt solid', + backgroundColor: 'white', + paddingTop: '1px', + paddingRight: '1px', + paddingLeft: '1px', + verticalAlign: 'middle', + }, + dataset: {}, + }, + { + spanAbove: false, + spanLeft: false, + isHeader: false, + blockGroupType: 'TableCell', + blocks: [ + { + segments: [ + { + text: 'Bug', + segmentType: 'Text', + format: { + fontFamily: 'Calibri, sans-serif', + fontSize: '11pt', + fontWeight: '400', + textColor: 'rgb(219, 219, 219)', + }, + }, + ], + blockType: 'Paragraph', + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + }, + }, + ], + format: { + textAlign: 'center', + whiteSpace: 'nowrap', + borderRight: '0.5pt solid', + borderBottom: '0.5pt solid', + backgroundColor: 'white', + paddingTop: '1px', + paddingRight: '1px', + paddingLeft: '1px', + verticalAlign: 'middle', + }, + dataset: {}, + }, + ], + format: {}, + }, + ], + blockType: 'Table', + format: { + width: jasmine.anything(), + useBorderBox: true, + borderCollapse: true, + } as any, + dataset: {}, + }, + { + segments: [ + { + isSelected: true, + segmentType: 'SelectionMarker', + format: {}, + }, + { + segmentType: 'Br', + format: {}, + }, + ], + blockType: 'Paragraph', + format: {}, + }, + ], + format: {}, + }); + }); }); diff --git a/packages-content-model/roosterjs-content-model-editor/test/editor/plugins/paste/e2e/testUtils.ts b/packages-content-model/roosterjs-content-model-editor/test/editor/plugins/paste/e2e/testUtils.ts index 8780c350cce..a1e3f1d7d77 100644 --- a/packages-content-model/roosterjs-content-model-editor/test/editor/plugins/paste/e2e/testUtils.ts +++ b/packages-content-model/roosterjs-content-model-editor/test/editor/plugins/paste/e2e/testUtils.ts @@ -2,7 +2,6 @@ import ContentModelEditor from '../../../../../lib/editor/ContentModelEditor'; import ContentModelPastePlugin from '../../../../../lib/editor/plugins/PastePlugin/ContentModelPastePlugin'; import { cloneModel } from '../../../../../lib/modelApi/common/cloneModel'; import { ContentModelDocument } from 'roosterjs-content-model-types'; -import { ExperimentalFeatures } from 'roosterjs-editor-types'; import { ContentModelEditorOptions, IContentModelEditor, @@ -15,7 +14,14 @@ export function initEditor(id: string) { let options: ContentModelEditorOptions = { plugins: [new ContentModelPastePlugin()], - experimentalFeatures: [ExperimentalFeatures.ContentModelPaste], + getVisibleViewport: () => { + return { + top: 100, + bottom: 200, + left: 100, + right: 200, + }; + }, }; let editor = new ContentModelEditor(node as HTMLDivElement, options); @@ -24,14 +30,13 @@ export function initEditor(id: string) { } export function expectEqual(model1: ContentModelDocument, model2: ContentModelDocument) { - expect( - /// Remove Cached elements and undefined properties - JSON.parse( - JSON.stringify( - cloneModel(model1, { - includeCachedElement: false, - }) - ) + /// Remove Cached elements and undefined properties + const newModel = JSON.parse( + JSON.stringify( + cloneModel(model1, { + includeCachedElement: false, + }) ) - ).toEqual(model2); + ); + expect(newModel).toEqual(model2); } diff --git a/packages-content-model/roosterjs-content-model-editor/test/editor/plugins/paste/linkParserTest.ts b/packages-content-model/roosterjs-content-model-editor/test/editor/plugins/paste/linkParserTest.ts index 8f6a00b31f4..f55d8ea8888 100644 --- a/packages-content-model/roosterjs-content-model-editor/test/editor/plugins/paste/linkParserTest.ts +++ b/packages-content-model/roosterjs-content-model-editor/test/editor/plugins/paste/linkParserTest.ts @@ -1,12 +1,12 @@ import { ContentModelDocument } from 'roosterjs-content-model-types'; import { createBeforePasteEventMock } from './processPastedContentFromWordDesktopTest'; -import { moveChildNodes } from 'roosterjs-editor-dom'; import { parseLink } from '../../../../lib/editor/plugins/PastePlugin/utils/linkParser'; import { contentModelToDom, createDomToModelContext, createModelToDomContext, domToContentModel, + moveChildNodes, } from 'roosterjs-content-model-dom'; let div: HTMLElement; diff --git a/packages-content-model/roosterjs-content-model-editor/test/editor/plugins/paste/processPastedContentFromExcelTest.ts b/packages-content-model/roosterjs-content-model-editor/test/editor/plugins/paste/processPastedContentFromExcelTest.ts index aea052822e4..8d73b24ae72 100644 --- a/packages-content-model/roosterjs-content-model-editor/test/editor/plugins/paste/processPastedContentFromExcelTest.ts +++ b/packages-content-model/roosterjs-content-model-editor/test/editor/plugins/paste/processPastedContentFromExcelTest.ts @@ -1,5 +1,5 @@ import * as PastePluginFile from '../../../../lib/editor/plugins/PastePlugin/Excel/processPastedContentFromExcel'; -import { Browser, moveChildNodes } from 'roosterjs-editor-dom'; +import { Browser } from 'roosterjs-editor-dom'; import { ContentModelDocument } from 'roosterjs-content-model-types'; import { createBeforePasteEventMock } from './processPastedContentFromWordDesktopTest'; import { processPastedContentFromExcel } from '../../../../lib/editor/plugins/PastePlugin/Excel/processPastedContentFromExcel'; @@ -8,6 +8,7 @@ import { createDomToModelContext, createModelToDomContext, domToContentModel, + moveChildNodes, } from 'roosterjs-content-model-dom'; let div: HTMLElement; diff --git a/packages-content-model/roosterjs-content-model-editor/test/editor/plugins/paste/processPastedContentFromPowerPointTest.ts b/packages-content-model/roosterjs-content-model-editor/test/editor/plugins/paste/processPastedContentFromPowerPointTest.ts index ad701fb55a2..d945fc8adc5 100644 --- a/packages-content-model/roosterjs-content-model-editor/test/editor/plugins/paste/processPastedContentFromPowerPointTest.ts +++ b/packages-content-model/roosterjs-content-model-editor/test/editor/plugins/paste/processPastedContentFromPowerPointTest.ts @@ -1,4 +1,4 @@ -import * as moveChildNodes from 'roosterjs-editor-dom/lib/utils/moveChildNodes'; +import * as moveChildNodes from 'roosterjs-content-model-dom/lib/domUtils/moveChildNodes'; import { createDefaultHtmlSanitizerOptions } from 'roosterjs-editor-dom'; import { processPastedContentFromPowerPoint } from '../../../../lib/editor/plugins/PastePlugin/PowerPoint/processPastedContentFromPowerPoint'; import { @@ -31,7 +31,7 @@ describe('processPastedContentFromPowerPointTest |', () => { beforeEach(() => { ev = getPasteEvent(); image = document.createElement('img'); - spyOn(moveChildNodes, 'default'); + spyOn(moveChildNodes, 'moveChildNodes'); spyOn(window, 'DOMParser').and.returnValue({ parseFromString(string: string, type: DOMParserSupportedType) { doc = (document.createDocumentFragment()); @@ -55,7 +55,7 @@ describe('processPastedContentFromPowerPointTest |', () => { processPastedContentFromPowerPoint(ev, trustedHTMLHandlerMock); expect(window.DOMParser).toHaveBeenCalled(); - expect(moveChildNodes.default).toHaveBeenCalledWith(ev.fragment, doc.body); + expect(moveChildNodes.moveChildNodes).toHaveBeenCalledWith(ev.fragment, doc.body); }); it('Dont Execute, Html✅, Text✅, Image✅', () => { @@ -66,7 +66,7 @@ describe('processPastedContentFromPowerPointTest |', () => { processPastedContentFromPowerPoint(ev, trustedHTMLHandlerMock); expect(window.DOMParser).not.toHaveBeenCalled(); - expect(moveChildNodes.default).not.toHaveBeenCalled(); + expect(moveChildNodes.moveChildNodes).not.toHaveBeenCalled(); }); it('Dont Execute, Html❎, Text❎, Image✅', () => { @@ -77,7 +77,7 @@ describe('processPastedContentFromPowerPointTest |', () => { processPastedContentFromPowerPoint(ev, trustedHTMLHandlerMock); expect(window.DOMParser).not.toHaveBeenCalled(); - expect(moveChildNodes.default).not.toHaveBeenCalled(); + expect(moveChildNodes.moveChildNodes).not.toHaveBeenCalled(); }); it('Dont Execute, Html❎, Text✅, Image✅', () => { @@ -88,7 +88,7 @@ describe('processPastedContentFromPowerPointTest |', () => { processPastedContentFromPowerPoint(ev, trustedHTMLHandlerMock); expect(window.DOMParser).not.toHaveBeenCalled(); - expect(moveChildNodes.default).not.toHaveBeenCalled(); + expect(moveChildNodes.moveChildNodes).not.toHaveBeenCalled(); }); it('Dont Execute, Html✅, Text❎, Image❎', () => { @@ -99,7 +99,7 @@ describe('processPastedContentFromPowerPointTest |', () => { processPastedContentFromPowerPoint(ev, trustedHTMLHandlerMock); expect(window.DOMParser).not.toHaveBeenCalled(); - expect(moveChildNodes.default).not.toHaveBeenCalled(); + expect(moveChildNodes.moveChildNodes).not.toHaveBeenCalled(); }); it('Dont Execute, Html❎, Text❎, Image❎', () => { @@ -110,7 +110,7 @@ describe('processPastedContentFromPowerPointTest |', () => { processPastedContentFromPowerPoint(ev, trustedHTMLHandlerMock); expect(window.DOMParser).not.toHaveBeenCalled(); - expect(moveChildNodes.default).not.toHaveBeenCalled(); + expect(moveChildNodes.moveChildNodes).not.toHaveBeenCalled(); }); it('Dont Execute, Html❎, Text✅, Image❎', () => { @@ -121,6 +121,6 @@ describe('processPastedContentFromPowerPointTest |', () => { processPastedContentFromPowerPoint(ev, trustedHTMLHandlerMock); expect(window.DOMParser).not.toHaveBeenCalled(); - expect(moveChildNodes.default).not.toHaveBeenCalled(); + expect(moveChildNodes.moveChildNodes).not.toHaveBeenCalled(); }); }); diff --git a/packages-content-model/roosterjs-content-model-editor/test/editor/plugins/paste/processPastedContentFromWacTest.ts b/packages-content-model/roosterjs-content-model-editor/test/editor/plugins/paste/processPastedContentFromWacTest.ts index b20f5c1981a..4342cbb7f03 100644 --- a/packages-content-model/roosterjs-content-model-editor/test/editor/plugins/paste/processPastedContentFromWacTest.ts +++ b/packages-content-model/roosterjs-content-model-editor/test/editor/plugins/paste/processPastedContentFromWacTest.ts @@ -1,4 +1,4 @@ -import { Browser, moveChildNodes } from 'roosterjs-editor-dom'; +import { Browser } from 'roosterjs-editor-dom'; import { ContentModelDocument } from 'roosterjs-content-model-types'; import { createBeforePasteEventMock } from './processPastedContentFromWordDesktopTest'; import { itChromeOnly } from 'roosterjs-editor-dom/test/DomTestHelper'; @@ -8,6 +8,7 @@ import { createDomToModelContext, createModelToDomContext, domToContentModel, + moveChildNodes, } from 'roosterjs-content-model-dom'; let div: HTMLElement; @@ -1577,7 +1578,7 @@ describe('wordOnlineHandler', () => { it('Remove temp marker from Word Online', () => { runTest( '

it went:  

  1. Test

  1. Test. 


', - '

it went:  

  1. Test

  2. Test. 


' + '

it went:  

  1. Test

  2. Test. 


' ); }); @@ -2053,7 +2054,25 @@ describe('wordOnlineHandler', () => { segments: [ { segmentType: 'Text', - text: 'Hello  ', + text: 'Hello ', + format: { + letterSpacing: + 'normal', + fontFamily: + '"Segoe UI", "Segoe UI_EmbeddedFont", "Segoe UI_MSFontService", sans-serif', + fontSize: '12pt', + italic: false, + fontWeight: + 'normal', + textColor: + 'rgb(0, 0, 0)', + lineHeight: + '23.7333px', + }, + }, + { + segmentType: 'Text', + text: ' ', format: { letterSpacing: 'normal', @@ -2246,7 +2265,98 @@ describe('wordOnlineHandler', () => { { segmentType: 'Text', text: - '[What changed and how it benefits devs] ', + '[What changed and how it ', + format: { + letterSpacing: + 'normal', + fontFamily: + '"Segoe UI", "Segoe UI_EmbeddedFont", "Segoe UI_MSFontService", sans-serif', + fontSize: '12pt', + italic: false, + fontWeight: + 'normal', + textColor: + 'rgb(0, 0, 0)', + lineHeight: + '23.7333px', + }, + }, + + { + segmentType: 'Text', + text: 'benefits', + format: { + letterSpacing: + 'normal', + fontFamily: + '"Segoe UI", "Segoe UI_EmbeddedFont", "Segoe UI_MSFontService", sans-serif', + fontSize: '12pt', + italic: false, + fontWeight: + 'normal', + textColor: + 'rgb(0, 0, 0)', + lineHeight: + '23.7333px', + }, + }, + { + segmentType: 'Text', + text: ' ', + format: { + letterSpacing: + 'normal', + fontFamily: + '"Segoe UI", "Segoe UI_EmbeddedFont", "Segoe UI_MSFontService", sans-serif', + fontSize: '12pt', + italic: false, + fontWeight: + 'normal', + textColor: + 'rgb(0, 0, 0)', + lineHeight: + '23.7333px', + }, + }, + { + segmentType: 'Text', + text: 'devs', + format: { + letterSpacing: + 'normal', + fontFamily: + '"Segoe UI", "Segoe UI_EmbeddedFont", "Segoe UI_MSFontService", sans-serif', + fontSize: '12pt', + italic: false, + fontWeight: + 'normal', + textColor: + 'rgb(0, 0, 0)', + lineHeight: + '23.7333px', + }, + }, + { + segmentType: 'Text', + text: ']', + format: { + letterSpacing: + 'normal', + fontFamily: + '"Segoe UI", "Segoe UI_EmbeddedFont", "Segoe UI_MSFontService", sans-serif', + fontSize: '12pt', + italic: false, + fontWeight: + 'normal', + textColor: + 'rgb(0, 0, 0)', + lineHeight: + '23.7333px', + }, + }, + { + segmentType: 'Text', + text: ' ', format: { letterSpacing: 'normal', @@ -2338,7 +2448,24 @@ describe('wordOnlineHandler', () => { { segmentType: 'Text', text: - '[Any action needed by devs] ', + '[Any action needed by devs]', + format: { + letterSpacing: + 'normal', + fontFamily: + '"Segoe UI", "Segoe UI_EmbeddedFont", "Segoe UI_MSFontService", sans-serif', + fontSize: '12pt', + italic: false, + fontWeight: + 'normal', + textColor: + 'rgb(0, 0, 0)', + lineHeight: '21px', + }, + }, + { + segmentType: 'Text', + text: ' ', format: { letterSpacing: 'normal', @@ -2479,7 +2606,24 @@ describe('wordOnlineHandler', () => { }, { segmentType: 'Text', - text: '  ', + text: ' ', + format: { + letterSpacing: + 'normal', + fontFamily: + '"Segoe UI", "Segoe UI_EmbeddedFont", "Segoe UI_MSFontService", sans-serif', + fontSize: '12pt', + italic: false, + fontWeight: + 'normal', + textColor: + 'rgb(0, 0, 0)', + lineHeight: '21px', + }, + }, + { + segmentType: 'Text', + text: ' ', format: { letterSpacing: 'normal', diff --git a/packages-content-model/roosterjs-content-model-editor/test/editor/plugins/paste/processPastedContentFromWordDesktopTest.ts b/packages-content-model/roosterjs-content-model-editor/test/editor/plugins/paste/processPastedContentFromWordDesktopTest.ts index 61f5e9d4514..ac5bc8e7320 100644 --- a/packages-content-model/roosterjs-content-model-editor/test/editor/plugins/paste/processPastedContentFromWordDesktopTest.ts +++ b/packages-content-model/roosterjs-content-model-editor/test/editor/plugins/paste/processPastedContentFromWordDesktopTest.ts @@ -2,13 +2,13 @@ import ContentModelBeforePasteEvent from '../../../../lib/publicTypes/event/Cont import { ClipboardData, PluginEventType } from 'roosterjs-editor-types'; import { ContentModelDocument } from 'roosterjs-content-model-types'; import { expectHtml } from 'roosterjs-editor-api/test/TestHelper'; -import { moveChildNodes } from 'roosterjs-editor-dom'; import { processPastedContentFromWordDesktop } from '../../../../lib/editor/plugins/PastePlugin/WordDesktop/processPastedContentFromWordDesktop'; import { contentModelToDom, createDomToModelContext, createModelToDomContext, domToContentModel, + moveChildNodes, } from 'roosterjs-content-model-dom'; describe('processPastedContentFromWordDesktopTest', () => { @@ -92,7 +92,12 @@ describe('processPastedContentFromWordDesktopTest', () => { segments: [ { segmentType: 'Text', - text: 'TestTest', + text: 'Test', + format: {}, + }, + { + segmentType: 'Text', + text: 'Test', format: {}, }, ], @@ -113,7 +118,12 @@ describe('processPastedContentFromWordDesktopTest', () => { segments: [ { segmentType: 'Text', - text: 'TestTest', + text: 'Test', + format: {}, + }, + { + segmentType: 'Text', + text: 'Test', format: {}, }, ], diff --git a/packages-content-model/roosterjs-content-model-editor/test/editor/utils/contentModelDomIndexerTest.ts b/packages-content-model/roosterjs-content-model-editor/test/editor/utils/contentModelDomIndexerTest.ts new file mode 100644 index 00000000000..469a80ffce4 --- /dev/null +++ b/packages-content-model/roosterjs-content-model-editor/test/editor/utils/contentModelDomIndexerTest.ts @@ -0,0 +1,643 @@ +import * as setSelection from '../../../lib/modelApi/selection/setSelection'; +import { contentModelDomIndexer } from '../../../lib/editor/utils/contentModelDomIndexer'; +import { createRange } from 'roosterjs-editor-dom'; +import { + ContentModelDocument, + ContentModelSegment, + DOMSelection, +} from 'roosterjs-content-model-types'; +import { + createBr, + createContentModelDocument, + createImage, + createParagraph, + createSelectionMarker, + createTable, + createTableCell, + createText, +} from 'roosterjs-content-model-dom'; + +describe('contentModelDomIndexer.onSegment', () => { + it('onSegment', () => { + const node = {} as any; + const paragraph = 'Paragraph' as any; + const segment = 'Segment' as any; + + contentModelDomIndexer.onSegment(node, paragraph, [segment]); + + expect(node).toEqual({ + __roosterjsContentModel: { paragraph: 'Paragraph', segments: ['Segment'] }, + }); + }); +}); + +describe('contentModelDomIndexer.onParagraph', () => { + it('Paragraph, no child', () => { + const node = document.createElement('div'); + + contentModelDomIndexer.onParagraph(node); + + expect(node.outerHTML).toBe('
'); + }); + + it('Indexed paragraph, has single text child', () => { + const node = document.createElement('div'); + const paragraph = createParagraph(); + const text = document.createTextNode('test') as any; + const segment = 'Segment' as any; + + text.__roosterjsContentModel = { + paragraph, + segments: [segment], + }; + node.appendChild(text); + + contentModelDomIndexer.onParagraph(node); + + expect(text.__roosterjsContentModel).toEqual({ + paragraph, + segments: [segment], + }); + expect(node.outerHTML).toBe('
test
'); + }); + + it('Indexed paragraph, has multiple text children', () => { + const node = document.createElement('div'); + const paragraph = createParagraph(); + const text1 = document.createTextNode('test1') as any; + const text2 = document.createTextNode('test2') as any; + const text3 = document.createTextNode('test3') as any; + const segment1 = 'Segment1' as any; + const segment2 = 'Segment2' as any; + const segment3 = 'Segment3' as any; + + text1.__roosterjsContentModel = { + paragraph, + segments: [segment1], + }; + text2.__roosterjsContentModel = { + paragraph, + segments: [segment2], + }; + text3.__roosterjsContentModel = { + paragraph, + segments: [segment3], + }; + node.appendChild(text1); + node.appendChild(text2); + node.appendChild(text3); + + contentModelDomIndexer.onParagraph(node); + + expect(text1.__roosterjsContentModel).toEqual({ + paragraph, + segments: [segment1, segment2, segment3], + }); + expect(text2.__roosterjsContentModel).toEqual({ + paragraph, + segments: [], + }); + expect(text3.__roosterjsContentModel).toEqual({ + paragraph, + segments: [], + }); + expect(node.outerHTML).toBe('
test1test2test3
'); + }); + + it('Indexed paragraph, has multiple text children and HTML element between', () => { + const node = document.createElement('div'); + const paragraph = createParagraph(); + const text1 = document.createTextNode('test1') as any; + const text2 = document.createTextNode('test2') as any; + const span = document.createElement('span'); + const text3 = document.createTextNode('test3') as any; + const text4 = document.createTextNode('test4') as any; + const segment1 = 'Segment1' as any; + const segment2 = 'Segment2' as any; + const segment3 = 'Segment3' as any; + const segment4 = 'Segment4' as any; + + text1.__roosterjsContentModel = { + paragraph, + segments: [segment1], + }; + text2.__roosterjsContentModel = { + paragraph, + segments: [segment2], + }; + text3.__roosterjsContentModel = { + paragraph, + segments: [segment3], + }; + text4.__roosterjsContentModel = { + paragraph, + segments: [segment4], + }; + node.appendChild(text1); + node.appendChild(text2); + node.appendChild(span); + node.appendChild(text3); + node.appendChild(text4); + + contentModelDomIndexer.onParagraph(node); + + expect(text1.__roosterjsContentModel).toEqual({ + paragraph, + segments: [segment1, segment2], + }); + expect(text2.__roosterjsContentModel).toEqual({ + paragraph, + segments: [], + }); + expect(text3.__roosterjsContentModel).toEqual({ + paragraph, + segments: [segment3, segment4], + }); + expect(text4.__roosterjsContentModel).toEqual({ + paragraph, + segments: [], + }); + expect(node.outerHTML).toBe('
test1test2test3test4
'); + }); +}); + +describe('contentModelDomIndexer.onTable', () => { + it('onTable', () => { + const node = {} as any; + const rows = 'ROWS' as any; + const table = { + rows: rows, + } as any; + + contentModelDomIndexer.onTable(node, table); + + expect(node).toEqual({ + __roosterjsContentModel: { tableRows: rows }, + }); + }); +}); + +describe('contentModelDomIndexer.reconcileSelection', () => { + let setSelectionSpy: jasmine.Spy; + let model: ContentModelDocument; + + beforeEach(() => { + model = createContentModelDocument(); + setSelectionSpy = spyOn(setSelection, 'setSelection').and.callThrough(); + }); + + it('no old range, fake range', () => { + const newRangeEx = {} as any; + + const result = contentModelDomIndexer.reconcileSelection(model, newRangeEx); + + expect(result).toBeFalse(); + expect(setSelectionSpy).not.toHaveBeenCalled(); + }); + + it('no old range, normal range on non-indexed text, collapsed', () => { + const node = document.createTextNode('test'); + const newRangeEx: DOMSelection = { + type: 'range', + range: createRange(node, 2), + }; + + const result = contentModelDomIndexer.reconcileSelection(model, newRangeEx); + + expect(result).toBeFalse(); + expect(setSelectionSpy).not.toHaveBeenCalled(); + }); + + it('no old range, normal range on indexed text, collapsed', () => { + const node = document.createTextNode('test') as any; + const newRangeEx: DOMSelection = { + type: 'range', + range: createRange(node, 2), + }; + const paragraph = createParagraph(); + const segment = createText(''); + + paragraph.segments.push(segment); + contentModelDomIndexer.onSegment(node, paragraph, [segment]); + + const result = contentModelDomIndexer.reconcileSelection(model, newRangeEx); + + const segment1: ContentModelSegment = { + segmentType: 'Text', + text: 'te', + format: {}, + }; + const segment2: ContentModelSegment = { + segmentType: 'Text', + text: 'st', + format: {}, + }; + + expect(result).toBeTrue(); + expect(node.__roosterjsContentModel).toEqual({ + paragraph, + segments: [segment1, segment2], + }); + expect(paragraph).toEqual({ + blockType: 'Paragraph', + format: {}, + segments: [ + segment1, + { + segmentType: 'SelectionMarker', + format: {}, + isSelected: true, + }, + segment2, + ], + }); + expect(setSelectionSpy).not.toHaveBeenCalled(); + }); + + it('no old range, normal range on indexed text, expanded on same node', () => { + const node = document.createTextNode('test') as any; + const newRangeEx: DOMSelection = { + type: 'range', + range: createRange(node, 1, node, 3), + }; + const paragraph = createParagraph(); + const segment = createText(''); + + paragraph.segments.push(segment); + contentModelDomIndexer.onSegment(node, paragraph, [segment]); + + const result = contentModelDomIndexer.reconcileSelection(model, newRangeEx); + + const segment1: ContentModelSegment = { + segmentType: 'Text', + text: 't', + format: {}, + }; + const segment2: ContentModelSegment = { + segmentType: 'Text', + text: 'es', + format: {}, + isSelected: true, + }; + const segment3: ContentModelSegment = { + segmentType: 'Text', + text: 't', + format: {}, + }; + + expect(result).toBeTrue(); + expect(node.__roosterjsContentModel).toEqual({ + paragraph, + segments: [segment1, segment2, segment3], + }); + expect(paragraph).toEqual({ + blockType: 'Paragraph', + format: {}, + segments: [segment1, segment2, segment3], + }); + expect(setSelectionSpy).not.toHaveBeenCalled(); + }); + + it('no old range, normal range on indexed text, expanded on different node', () => { + const node1 = document.createTextNode('test1') as any; + const node2 = document.createTextNode('test2') as any; + const parent = document.createElement('div'); + + parent.appendChild(node1); + parent.appendChild(node2); + + const newRangeEx: DOMSelection = { + type: 'range', + range: createRange(node1, 2, node2, 3), + }; + const paragraph = createParagraph(); + const oldSegment1 = createText(''); + const oldSegment2 = createText(''); + + paragraph.segments.push(oldSegment1, oldSegment2); + contentModelDomIndexer.onSegment(node1, paragraph, [oldSegment1]); + contentModelDomIndexer.onSegment(node2, paragraph, [oldSegment2]); + model.blocks.push(paragraph); + + const result = contentModelDomIndexer.reconcileSelection(model, newRangeEx); + + const segment1: ContentModelSegment = { + segmentType: 'Text', + text: 'te', + format: {}, + }; + const segment2: ContentModelSegment = { + segmentType: 'Text', + text: 'st1', + format: {}, + isSelected: true, + }; + const segment3: ContentModelSegment = { + segmentType: 'Text', + text: 'tes', + format: {}, + isSelected: true, + }; + const segment4: ContentModelSegment = { + segmentType: 'Text', + text: 't2', + format: {}, + }; + const marker1 = createSelectionMarker(); + const marker2 = createSelectionMarker(); + + expect(result).toBeTrue(); + expect(node1.__roosterjsContentModel).toEqual({ + paragraph, + segments: [segment1, segment2], + }); + expect(node2.__roosterjsContentModel).toEqual({ + paragraph, + segments: [segment3, segment4], + }); + expect(paragraph).toEqual({ + blockType: 'Paragraph', + format: {}, + segments: [segment1, marker1, segment2, segment3, marker2, segment4], + }); + expect(setSelectionSpy).toHaveBeenCalledWith(model, marker1, marker2); + expect(model).toEqual({ + blockGroupType: 'Document', + blocks: [paragraph], + }); + }); + + it('no old range, normal range on indexed text, expanded on other type of node', () => { + const node1 = document.createTextNode('test1') as any; + const node2 = document.createElement('br') as any; + const parent = document.createElement('div'); + + parent.appendChild(node1); + parent.appendChild(node2); + + const newRangeEx: DOMSelection = { + type: 'range', + range: createRange(node1, 2, parent, 2), + }; + const paragraph = createParagraph(); + const oldSegment1 = createText(''); + const oldSegment2 = createBr(); + + paragraph.segments.push(oldSegment1, oldSegment2); + contentModelDomIndexer.onSegment(node1, paragraph, [oldSegment1]); + contentModelDomIndexer.onSegment(node2, paragraph, [oldSegment2]); + model.blocks.push(paragraph); + + const result = contentModelDomIndexer.reconcileSelection(model, newRangeEx); + + const segment1: ContentModelSegment = { + segmentType: 'Text', + text: 'te', + format: {}, + }; + const segment2: ContentModelSegment = { + segmentType: 'Text', + text: 'st1', + format: {}, + isSelected: true, + }; + + const marker1 = createSelectionMarker(); + const marker2 = createSelectionMarker(); + + expect(result).toBeTrue(); + expect(node1.__roosterjsContentModel).toEqual({ + paragraph, + segments: [segment1, segment2], + }); + expect(node2.__roosterjsContentModel).toEqual({ + paragraph, + segments: [oldSegment2], + }); + expect(paragraph).toEqual({ + blockType: 'Paragraph', + format: {}, + segments: [segment1, marker1, segment2, oldSegment2, marker2], + }); + expect(setSelectionSpy).toHaveBeenCalledWith(model, marker1, marker2); + expect(model).toEqual({ + blockGroupType: 'Document', + blocks: [paragraph], + }); + }); + + it('no old range, image range on indexed text', () => { + const node1 = document.createTextNode('img') as any; + const parent = document.createElement('div'); + + parent.appendChild(node1); + + const newRangeEx: DOMSelection = { + type: 'image', + image: node1, + }; + const paragraph = createParagraph(); + const oldSegment1 = createImage('test'); + + paragraph.segments.push(oldSegment1); + contentModelDomIndexer.onSegment(node1, paragraph, [oldSegment1]); + model.blocks.push(paragraph); + + const result = contentModelDomIndexer.reconcileSelection(model, newRangeEx); + + expect(result).toBeFalse(); + expect(node1.__roosterjsContentModel).toEqual({ + paragraph, + segments: [oldSegment1], + }); + expect(paragraph).toEqual({ + blockType: 'Paragraph', + format: {}, + segments: [oldSegment1], + }); + expect(setSelectionSpy).not.toHaveBeenCalled(); + expect(model).toEqual({ + blockGroupType: 'Document', + blocks: [paragraph], + }); + expect(oldSegment1).toEqual({ + segmentType: 'Image', + src: 'test', + format: {}, + dataset: {}, + }); + }); + + it('no old range, table range on indexed text', () => { + const node1 = document.createTextNode('table') as any; + const parent = document.createElement('div'); + + parent.appendChild(node1); + + const newRangeEx: DOMSelection = { + type: 'table', + table: node1, + firstColumn: 0, + firstRow: 1, + lastColumn: 1, + lastRow: 2, + }; + const tableModel = createTable(3); + const cell00 = createTableCell(); + const cell01 = createTableCell(); + const cell02 = createTableCell(); + const cell10 = createTableCell(); + const cell11 = createTableCell(); + const cell12 = createTableCell(); + const cell20 = createTableCell(); + const cell21 = createTableCell(); + const cell22 = createTableCell(); + tableModel.rows[0].cells.push(cell00, cell01, cell02); + tableModel.rows[1].cells.push(cell10, cell11, cell12); + tableModel.rows[2].cells.push(cell20, cell21, cell22); + + contentModelDomIndexer.onTable(node1, tableModel); + model.blocks.push(tableModel); + + const result = contentModelDomIndexer.reconcileSelection(model, newRangeEx); + + expect(result).toBeFalse(); + expect(node1.__roosterjsContentModel).toEqual({ + tableRows: tableModel.rows, + }); + expect(setSelectionSpy).not.toHaveBeenCalled(); + expect(model).toEqual({ + blockGroupType: 'Document', + blocks: [tableModel], + }); + }); + + it('no old range, collapsed range after last node', () => { + const node = document.createElement('br') as any; + const parent = document.createElement('div'); + + parent.appendChild(node); + + const newRangeEx: DOMSelection = { + type: 'range', + range: createRange(parent, 1), + }; + const paragraph = createParagraph(); + const segment = createBr({ fontFamily: 'Arial' }); + + paragraph.segments.push(segment); + contentModelDomIndexer.onSegment(node, paragraph, [segment]); + + const result = contentModelDomIndexer.reconcileSelection(model, newRangeEx); + + expect(result).toBeTrue(); + expect(node.__roosterjsContentModel).toEqual({ + paragraph, + segments: [segment], + }); + expect(paragraph).toEqual({ + blockType: 'Paragraph', + format: {}, + segments: [segment, createSelectionMarker({ fontFamily: 'Arial' })], + }); + expect(setSelectionSpy).not.toHaveBeenCalled(); + }); + + it('has old range - collapsed, expanded new range', () => { + const node = document.createTextNode('test') as any; + const oldRangeEx: DOMSelection = { + type: 'range', + range: createRange(node, 2), + }; + const newRangeEx: DOMSelection = { + type: 'range', + range: createRange(node, 1, node, 3), + }; + const paragraph = createParagraph(); + const oldSegment1 = createText('te'); + const oldSegment2 = createText('st'); + + paragraph.segments.push(oldSegment1, createSelectionMarker(), oldSegment2); + contentModelDomIndexer.onSegment(node, paragraph, [oldSegment1, oldSegment2]); + + const result = contentModelDomIndexer.reconcileSelection(model, newRangeEx, oldRangeEx); + + const segment1: ContentModelSegment = { + segmentType: 'Text', + text: 't', + format: {}, + }; + const segment2: ContentModelSegment = { + segmentType: 'Text', + text: 'es', + format: {}, + isSelected: true, + }; + const segment3: ContentModelSegment = { + segmentType: 'Text', + text: 't', + format: {}, + }; + + expect(result).toBeTrue(); + expect(node.__roosterjsContentModel).toEqual({ + paragraph, + segments: [segment1, segment2, segment3], + }); + expect(paragraph).toEqual({ + blockType: 'Paragraph', + format: {}, + segments: [segment1, segment2, segment3], + }); + expect(setSelectionSpy).not.toHaveBeenCalled(); + }); + + it('has old range - expanded, expanded new range', () => { + const node = document.createTextNode('test') as any; + const oldRangeEx: DOMSelection = { + type: 'range', + range: createRange(node, 1, node, 3), + }; + const newRangeEx: DOMSelection = { + type: 'range', + range: createRange(node, 2), + }; + const paragraph = createParagraph(); + const oldSegment1: ContentModelSegment = { + segmentType: 'Text', + text: 't', + format: {}, + }; + const oldSegment2: ContentModelSegment = { + segmentType: 'Text', + text: 'es', + format: {}, + isSelected: true, + }; + const oldSegment3: ContentModelSegment = { + segmentType: 'Text', + text: 't', + format: {}, + }; + + paragraph.segments.push(oldSegment1, oldSegment2, oldSegment3); + contentModelDomIndexer.onSegment(node, paragraph, [oldSegment1, oldSegment2, oldSegment3]); + + const result = contentModelDomIndexer.reconcileSelection(model, newRangeEx, oldRangeEx); + + const segment1 = createText('te'); + const segment2 = createText('st'); + + expect(result).toBeTrue(); + expect(node.__roosterjsContentModel).toEqual({ + paragraph, + segments: [segment1, segment2], + }); + expect(paragraph).toEqual({ + blockType: 'Paragraph', + format: {}, + segments: [segment1, createSelectionMarker(), segment2], + }); + expect(setSelectionSpy).toHaveBeenCalled(); + }); +}); diff --git a/packages-content-model/roosterjs-content-model-editor/test/editor/utils/handleKeyboardEventCommonTest.ts b/packages-content-model/roosterjs-content-model-editor/test/editor/utils/handleKeyboardEventCommonTest.ts index c98997eef24..34bdc29154e 100644 --- a/packages-content-model/roosterjs-content-model-editor/test/editor/utils/handleKeyboardEventCommonTest.ts +++ b/packages-content-model/roosterjs-content-model-editor/test/editor/utils/handleKeyboardEventCommonTest.ts @@ -42,7 +42,11 @@ describe('handleKeyboardEventResult', () => { const mockedModel = 'MODEL' as any; const which = 'WHICH' as any; (mockedEvent).which = which; - const context: FormatWithContentModelContext = { newEntities: [], deletedEntities: [] }; + const context: FormatWithContentModelContext = { + newEntities: [], + deletedEntities: [], + newImages: [], + }; const result = handleKeyboardEventResult( mockedEditor, mockedModel, @@ -64,7 +68,11 @@ describe('handleKeyboardEventResult', () => { it('DeleteResult.NotDeleted', () => { const mockedModel = 'MODEL' as any; - const context: FormatWithContentModelContext = { newEntities: [], deletedEntities: [] }; + const context: FormatWithContentModelContext = { + newEntities: [], + deletedEntities: [], + newImages: [], + }; const result = handleKeyboardEventResult( mockedEditor, mockedModel, @@ -84,7 +92,11 @@ describe('handleKeyboardEventResult', () => { it('DeleteResult.Range', () => { const mockedModel = 'MODEL' as any; - const context: FormatWithContentModelContext = { newEntities: [], deletedEntities: [] }; + const context: FormatWithContentModelContext = { + newEntities: [], + deletedEntities: [], + newImages: [], + }; const result = handleKeyboardEventResult( mockedEditor, mockedModel, @@ -106,7 +118,11 @@ describe('handleKeyboardEventResult', () => { it('DeleteResult.NothingToDelete', () => { const mockedModel = 'MODEL' as any; - const context: FormatWithContentModelContext = { newEntities: [], deletedEntities: [] }; + const context: FormatWithContentModelContext = { + newEntities: [], + deletedEntities: [], + newImages: [], + }; const result = handleKeyboardEventResult( mockedEditor, mockedModel, diff --git a/packages-content-model/roosterjs-content-model-editor/test/modelApi/common/cloneModelTest.ts b/packages-content-model/roosterjs-content-model-editor/test/modelApi/common/cloneModelTest.ts index c83e226848c..017f0e37797 100644 --- a/packages-content-model/roosterjs-content-model-editor/test/modelApi/common/cloneModelTest.ts +++ b/packages-content-model/roosterjs-content-model-editor/test/modelApi/common/cloneModelTest.ts @@ -107,9 +107,11 @@ describe('cloneModel', () => { segmentType: 'Entity', blockType: 'Entity', format: {}, - id: 'e1', + entityFormat: { + id: 'e1', + isReadonly: true, + }, wrapper: document.createElement('span'), - isReadonly: true, }, { segmentType: 'General', @@ -140,9 +142,11 @@ describe('cloneModel', () => { segmentType: 'Entity', blockType: 'Entity', format: { underline: true }, - id: 'e2', + entityFormat: { + id: 'e2', + isReadonly: true, + }, wrapper: document.createElement('span'), - isReadonly: true, }, ], }); @@ -445,9 +449,11 @@ describe('cloneModel', () => { blockType: 'Entity', format: {}, wrapper: span, - isReadonly: true, - type: undefined, - id: undefined, + entityFormat: { + isReadonly: true, + entityType: undefined, + id: undefined, + }, segmentType: 'Entity', isSelected: undefined, }, @@ -494,9 +500,11 @@ describe('cloneModel', () => { blockType: 'Entity', format: {}, wrapper: span, - isReadonly: true, - type: undefined, - id: undefined, + entityFormat: { + isReadonly: true, + entityType: undefined, + id: undefined, + }, segmentType: 'Entity', isSelected: undefined, }, diff --git a/packages-content-model/roosterjs-content-model-editor/test/modelApi/common/mergeModelTest.ts b/packages-content-model/roosterjs-content-model-editor/test/modelApi/common/mergeModelTest.ts index b0cd8ab9fda..d5938d16914 100644 --- a/packages-content-model/roosterjs-content-model-editor/test/modelApi/common/mergeModelTest.ts +++ b/packages-content-model/roosterjs-content-model-editor/test/modelApi/common/mergeModelTest.ts @@ -1,6 +1,6 @@ import * as applyTableFormat from '../../../lib/modelApi/table/applyTableFormat'; import * as normalizeTable from '../../../lib/modelApi/table/normalizeTable'; -import { ContentModelDocument } from 'roosterjs-content-model-types'; +import { ContentModelDocument, ContentModelImage } from 'roosterjs-content-model-types'; import { EntityOperation } from 'roosterjs-editor-types'; import { FormatWithContentModelContext } from '../../../lib/publicTypes/parameter/FormatWithContentModelContext'; import { mergeModel } from '../../../lib/modelApi/common/mergeModel'; @@ -28,7 +28,11 @@ describe('mergeModel', () => { para.segments.push(marker); majorModel.blocks.push(para); - mergeModel(majorModel, sourceModel, { newEntities: [], deletedEntities: [] }); + mergeModel(majorModel, sourceModel, { + newEntities: [], + deletedEntities: [], + newImages: [], + }); expect(majorModel).toEqual({ blockGroupType: 'Document', @@ -68,7 +72,11 @@ describe('mergeModel', () => { para2.segments.push(text1, text2); sourceModel.blocks.push(para2); - mergeModel(majorModel, sourceModel, { newEntities: [], deletedEntities: [] }); + mergeModel(majorModel, sourceModel, { + newEntities: [], + deletedEntities: [], + newImages: [], + }); expect(majorModel).toEqual({ blockGroupType: 'Document', @@ -118,7 +126,11 @@ describe('mergeModel', () => { majorModel.blocks.push(para1); sourceModel.blocks.push(para2); - mergeModel(majorModel, sourceModel, { newEntities: [], deletedEntities: [] }); + mergeModel(majorModel, sourceModel, { + newEntities: [], + deletedEntities: [], + newImages: [], + }); expect(majorModel).toEqual({ blockGroupType: 'Document', @@ -197,7 +209,11 @@ describe('mergeModel', () => { sourceModel.blocks.push(newPara1); sourceModel.blocks.push(newPara2); - mergeModel(majorModel, sourceModel, { newEntities: [], deletedEntities: [] }); + mergeModel(majorModel, sourceModel, { + newEntities: [], + deletedEntities: [], + newImages: [], + }); expect(majorModel).toEqual({ blockGroupType: 'Document', @@ -292,7 +308,11 @@ describe('mergeModel', () => { sourceModel.blocks.push(newPara2); sourceModel.blocks.push(newPara3); - mergeModel(majorModel, sourceModel, { newEntities: [], deletedEntities: [] }); + mergeModel(majorModel, sourceModel, { + newEntities: [], + deletedEntities: [], + newImages: [], + }); expect(majorModel).toEqual({ blockGroupType: 'Document', @@ -439,7 +459,11 @@ describe('mergeModel', () => { sourceModel.blocks.push(newList1); sourceModel.blocks.push(newList2); - mergeModel(majorModel, sourceModel, { newEntities: [], deletedEntities: [] }); + mergeModel(majorModel, sourceModel, { + newEntities: [], + deletedEntities: [], + newImages: [], + }); expect(majorModel).toEqual({ blockGroupType: 'Document', @@ -605,7 +629,11 @@ describe('mergeModel', () => { sourceModel.blocks.push(newList1); sourceModel.blocks.push(newList2); - mergeModel(majorModel, sourceModel, { newEntities: [], deletedEntities: [] }); + mergeModel(majorModel, sourceModel, { + newEntities: [], + deletedEntities: [], + newImages: [], + }); expect(majorModel).toEqual({ blockGroupType: 'Document', @@ -793,7 +821,11 @@ describe('mergeModel', () => { sourceModel.blocks.push(newTable1); - mergeModel(majorModel, sourceModel, { newEntities: [], deletedEntities: [] }); + mergeModel(majorModel, sourceModel, { + newEntities: [], + deletedEntities: [], + newImages: [], + }); expect(majorModel).toEqual({ blockGroupType: 'Document', @@ -894,7 +926,11 @@ describe('mergeModel', () => { spyOn(applyTableFormat, 'applyTableFormat'); spyOn(normalizeTable, 'normalizeTable'); - mergeModel(majorModel, sourceModel, { newEntities: [], deletedEntities: [] }); + mergeModel(majorModel, sourceModel, { + newEntities: [], + deletedEntities: [], + newImages: [], + }); expect(normalizeTable.normalizeTable).not.toHaveBeenCalled(); expect(majorModel).toEqual({ @@ -1014,7 +1050,7 @@ describe('mergeModel', () => { mergeModel( majorModel, sourceModel, - { newEntities: [], deletedEntities: [] }, + { newEntities: [], deletedEntities: [], newImages: [] }, { mergeTable: true, } @@ -1156,7 +1192,7 @@ describe('mergeModel', () => { mergeModel( majorModel, sourceModel, - { newEntities: [], deletedEntities: [] }, + { newEntities: [], deletedEntities: [], newImages: [] }, { mergeTable: true, } @@ -1287,7 +1323,7 @@ describe('mergeModel', () => { mergeModel( majorModel, sourceModel, - { newEntities: [], deletedEntities: [] }, + { newEntities: [], deletedEntities: [], newImages: [] }, { mergeTable: true, } @@ -1401,7 +1437,7 @@ describe('mergeModel', () => { mergeModel( majorModel, sourceModel, - { newEntities: [], deletedEntities: [] }, + { newEntities: [], deletedEntities: [], newImages: [] }, { insertPosition: { marker: marker2, @@ -1484,7 +1520,7 @@ describe('mergeModel', () => { mergeModel( majorModel, sourceModel, - { newEntities: [], deletedEntities: [] }, + { newEntities: [], deletedEntities: [], newImages: [] }, { mergeFormat: 'mergeAll', } @@ -1546,7 +1582,7 @@ describe('mergeModel', () => { mergeModel( majorModel, sourceModel, - { newEntities: [], deletedEntities: [] }, + { newEntities: [], deletedEntities: [], newImages: [] }, { mergeFormat: 'keepSourceEmphasisFormat', } @@ -1614,7 +1650,7 @@ describe('mergeModel', () => { mergeModel( majorModel, sourceModel, - { newEntities: [], deletedEntities: [] }, + { newEntities: [], deletedEntities: [], newImages: [] }, { mergeFormat: 'keepSourceEmphasisFormat', } @@ -1709,7 +1745,7 @@ describe('mergeModel', () => { mergeModel( majorModel, sourceModel, - { newEntities: [], deletedEntities: [] }, + { newEntities: [], deletedEntities: [], newImages: [] }, { mergeFormat: 'keepSourceEmphasisFormat', } @@ -1785,7 +1821,11 @@ describe('mergeModel', () => { sourceModel.blocks.push(divider); - mergeModel(majorModel, sourceModel, { newEntities: [], deletedEntities: [] }); + mergeModel(majorModel, sourceModel, { + newEntities: [], + deletedEntities: [], + newImages: [], + }); expect(majorModel).toEqual({ blockGroupType: 'Document', @@ -1852,7 +1892,11 @@ describe('mergeModel', () => { sourceModel.blocks.push(newPara1); sourceModel.blocks.push(newPara2); - mergeModel(majorModel, sourceModel, { newEntities: [], deletedEntities: [] }); + mergeModel(majorModel, sourceModel, { + newEntities: [], + deletedEntities: [], + newImages: [], + }); expect(majorModel).toEqual({ blockGroupType: 'Document', @@ -1952,7 +1996,7 @@ describe('mergeModel', () => { mergeModel( majorModel, sourceModel, - { newEntities: [], deletedEntities: [] }, + { newEntities: [], deletedEntities: [], newImages: [] }, { mergeFormat: 'keepSourceEmphasisFormat', } @@ -2033,7 +2077,7 @@ describe('mergeModel', () => { mergeModel( majorModel, sourceModel, - { newEntities: [], deletedEntities: [] }, + { newEntities: [], deletedEntities: [], newImages: [] }, { mergeFormat: 'mergeAll', } @@ -2130,7 +2174,7 @@ describe('mergeModel', () => { mergeModel( majorModel, sourceModel, - { newEntities: [], deletedEntities: [] }, + { newEntities: [], deletedEntities: [], newImages: [] }, { mergeFormat: 'none', } @@ -2320,7 +2364,7 @@ describe('mergeModel', () => { mergeModel( majorModel, sourceModel, - { newEntities: [], deletedEntities: [] }, + { newEntities: [], deletedEntities: [], newImages: [] }, { mergeFormat: 'mergeAll', } @@ -2851,7 +2895,7 @@ describe('mergeModel', () => { majorModel.blocks.push(para1); const sourceModel: ContentModelDocument = createContentModelDocument(); - const newEntity = createEntity(document.createElement('div'), false, undefined, { + const newEntity = createEntity(document.createElement('div'), false, { fontFamily: 'Corbel', fontSize: '20px', backgroundColor: 'blue', @@ -2860,6 +2904,7 @@ describe('mergeModel', () => { }); const context: FormatWithContentModelContext = { deletedEntities: [], + newImages: [], newEntities: [], }; @@ -2891,9 +2936,11 @@ describe('mergeModel', () => { textColor: 'aliceblue', italic: true, }, - id: undefined, - type: undefined, - isReadonly: false, + entityFormat: { + id: undefined, + entityType: undefined, + isReadonly: false, + }, wrapper: newEntity.wrapper, }, { @@ -2920,13 +2967,14 @@ describe('mergeModel', () => { expect(context).toEqual({ newEntities: [newEntity], deletedEntities: [], + newImages: [], }); }); it('Merge and replace inline entities', () => { const majorModel = createContentModelDocument(); const para1 = createParagraph(); - const sourceEntity = createEntity('wrapper1' as any, true, 'E0'); + const sourceEntity = createEntity('wrapper1' as any, true, undefined, 'E0'); const sourceBr = createBr(); sourceEntity.isSelected = true; @@ -2935,8 +2983,8 @@ describe('mergeModel', () => { const sourceModel: ContentModelDocument = createContentModelDocument(); const newPara = createParagraph(); - const newEntity1 = createEntity('wrapper2' as any, true, 'E1'); - const newEntity2 = createEntity('wrapper2' as any, true, 'E2'); + const newEntity1 = createEntity('wrapper2' as any, true, undefined, 'E1'); + const newEntity2 = createEntity('wrapper2' as any, true, undefined, 'E2'); const text = createText('test'); newPara.segments.push(newEntity1, text, newEntity2); @@ -2944,6 +2992,7 @@ describe('mergeModel', () => { const context: FormatWithContentModelContext = { deletedEntities: [], + newImages: [], newEntities: [], }; mergeModel(majorModel, sourceModel, context); @@ -2975,6 +3024,201 @@ describe('mergeModel', () => { operation: EntityOperation.Overwrite, }, ], + newImages: [], + }); + }); + + it('Merge Image', () => { + const majorModel = createContentModelDocument(); + const newImage: ContentModelImage = { + segmentType: 'Image', + src: 'test', + format: {}, + dataset: {}, + }; + const sourceModel: ContentModelDocument = { + blockGroupType: 'Document', + blocks: [ + { + blockType: 'Paragraph', + segments: [newImage], + format: {}, + }, + ], + format: {}, + }; + const para1 = createParagraph(); + const marker = createSelectionMarker(); + + para1.segments.push(marker); + majorModel.blocks.push(para1); + + const context: FormatWithContentModelContext = { + deletedEntities: [], + newImages: [], + newEntities: [], + }; + + mergeModel(majorModel, sourceModel, context, { + mergeFormat: 'mergeAll', + }); + + expect(majorModel).toEqual({ + blockGroupType: 'Document', + blocks: [ + { + blockType: 'Paragraph', + segments: [ + newImage, + { + segmentType: 'SelectionMarker', + isSelected: true, + format: {}, + }, + ], + format: {}, + }, + ], + }); + + expect(context).toEqual({ + deletedEntities: [], + newEntities: [], + newImages: [newImage], + }); + }); + + it('Merge two Images', () => { + const majorModel = createContentModelDocument(); + const newImage: ContentModelImage = { + segmentType: 'Image', + src: 'test', + format: {}, + dataset: {}, + }; + const newImage1: ContentModelImage = { + segmentType: 'Image', + src: 'test1', + format: {}, + dataset: {}, + }; + const sourceModel: ContentModelDocument = { + blockGroupType: 'Document', + blocks: [ + { + blockType: 'Paragraph', + segments: [newImage, newImage1], + format: {}, + }, + ], + format: {}, + }; + const para1 = createParagraph(); + const marker = createSelectionMarker(); + + para1.segments.push(marker); + majorModel.blocks.push(para1); + + const context: FormatWithContentModelContext = { + deletedEntities: [], + newImages: [], + newEntities: [], + }; + + mergeModel(majorModel, sourceModel, context, { + mergeFormat: 'mergeAll', + }); + + expect(majorModel).toEqual({ + blockGroupType: 'Document', + blocks: [ + { + blockType: 'Paragraph', + segments: [ + newImage, + newImage1, + { + segmentType: 'SelectionMarker', + isSelected: true, + format: {}, + }, + ], + format: {}, + }, + ], + }); + + expect(context).toEqual({ + deletedEntities: [], + newEntities: [], + newImages: [newImage, newImage1], + }); + }); + + it('Merge into a paragraph with image', () => { + const majorModel = createContentModelDocument(); + const newImage: ContentModelImage = { + segmentType: 'Image', + src: 'test', + format: {}, + dataset: {}, + }; + const sourceModel: ContentModelDocument = { + blockGroupType: 'Document', + blocks: [ + { + blockType: 'Paragraph', + segments: [newImage], + format: {}, + }, + ], + format: {}, + }; + const para1 = createParagraph(); + const image: ContentModelImage = { + segmentType: 'Image', + src: 'test1', + format: {}, + dataset: {}, + }; + const marker = createSelectionMarker(); + + para1.segments.push(image, marker); + majorModel.blocks.push(para1); + + const context: FormatWithContentModelContext = { + deletedEntities: [], + newEntities: [], + newImages: [image], + }; + + mergeModel(majorModel, sourceModel, context, { + mergeFormat: 'mergeAll', + }); + + expect(majorModel).toEqual({ + blockGroupType: 'Document', + blocks: [ + { + blockType: 'Paragraph', + segments: [ + image, + newImage, + { + segmentType: 'SelectionMarker', + isSelected: true, + format: {}, + }, + ], + format: {}, + }, + ], + }); + + expect(context).toEqual({ + deletedEntities: [], + newEntities: [], + newImages: [image, newImage], }); }); }); diff --git a/packages-content-model/roosterjs-content-model-editor/test/modelApi/edit/deleteSelectionTest.ts b/packages-content-model/roosterjs-content-model-editor/test/modelApi/edit/deleteSelectionTest.ts index abf0459143e..ee50e04cca0 100644 --- a/packages-content-model/roosterjs-content-model-editor/test/modelApi/edit/deleteSelectionTest.ts +++ b/packages-content-model/roosterjs-content-model-editor/test/modelApi/edit/deleteSelectionTest.ts @@ -469,7 +469,7 @@ describe('deleteSelection - selectionOnly', () => { it('Entity selection, no callback', () => { const model = createContentModelDocument(); const wrapper = 'WRAPPER' as any; - const entity = createEntity(wrapper, true); + const entity = createEntity(wrapper); model.blocks.push(entity); entity.isSelected = true; @@ -521,13 +521,17 @@ describe('deleteSelection - selectionOnly', () => { it('Entity selection, callback returns false', () => { const model = createContentModelDocument(); const wrapper = 'WRAPPER' as any; - const entity = createEntity(wrapper, true); + const entity = createEntity(wrapper); const deletedEntities: DeletedEntity[] = []; model.blocks.push(entity); entity.isSelected = true; - const result = deleteSelection(model, [], { newEntities: [], deletedEntities }); + const result = deleteSelection(model, [], { + newEntities: [], + deletedEntities, + newImages: [], + }); expect(result.deleteResult).toBe(DeleteResult.Range); expect(result.insertPoint).toEqual({ @@ -576,13 +580,17 @@ describe('deleteSelection - selectionOnly', () => { it('Entity selection, callback returns true', () => { const model = createContentModelDocument(); const wrapper = 'WRAPPER' as any; - const entity = createEntity(wrapper, true); + const entity = createEntity(wrapper); model.blocks.push(entity); entity.isSelected = true; const deletedEntities: DeletedEntity[] = []; - const result = deleteSelection(model, [], { newEntities: [], deletedEntities }); + const result = deleteSelection(model, [], { + newEntities: [], + deletedEntities, + newImages: [], + }); expect(result.deleteResult).toBe(DeleteResult.Range); expect(result.insertPoint).toEqual({ @@ -1436,7 +1444,7 @@ describe('deleteSelection - forward', () => { const marker = createSelectionMarker({ fontSize: '10px' }); const br = createBr(); const wrapper = 'WRAPPER' as any; - const entity = createEntity(wrapper, true); + const entity = createEntity(wrapper); para.segments.push(marker, br); model.blocks.push(para, entity); @@ -1478,7 +1486,7 @@ describe('deleteSelection - forward', () => { const marker = createSelectionMarker({ fontSize: '10px' }); const br = createBr(); const wrapper = 'WRAPPER' as any; - const entity = createEntity(wrapper, true); + const entity = createEntity(wrapper); para.segments.push(marker, br); model.blocks.push(para, entity); @@ -1487,6 +1495,7 @@ describe('deleteSelection - forward', () => { const result = deleteSelection(model, [forwardDeleteCollapsedSelection], { newEntities: [], deletedEntities, + newImages: [], }); expect(result.deleteResult).toBe(DeleteResult.Range); @@ -1525,7 +1534,7 @@ describe('deleteSelection - forward', () => { const marker = createSelectionMarker({ fontSize: '10px' }); const br = createBr(); const wrapper = 'WRAPPER' as any; - const entity = createEntity(wrapper, true); + const entity = createEntity(wrapper); para.segments.push(marker, br); model.blocks.push(para, entity); @@ -1534,6 +1543,7 @@ describe('deleteSelection - forward', () => { const result = deleteSelection(model, [forwardDeleteCollapsedSelection], { newEntities: [], deletedEntities, + newImages: [], }); expect(result.deleteResult).toBe(DeleteResult.Range); @@ -3192,7 +3202,7 @@ describe('deleteSelection - backward', () => { const marker = createSelectionMarker({ fontSize: '10px' }); const br = createBr(); const wrapper = 'WRAPPER' as any; - const entity = createEntity(wrapper, true); + const entity = createEntity(wrapper); para.segments.push(marker, br); model.blocks.push(entity, para); @@ -3234,7 +3244,7 @@ describe('deleteSelection - backward', () => { const marker = createSelectionMarker({ fontSize: '10px' }); const br = createBr(); const wrapper = 'WRAPPER' as any; - const entity = createEntity(wrapper, true); + const entity = createEntity(wrapper); para.segments.push(marker, br); model.blocks.push(entity, para); @@ -3243,6 +3253,7 @@ describe('deleteSelection - backward', () => { const result = deleteSelection(model, [backwardDeleteCollapsedSelection], { newEntities: [], deletedEntities, + newImages: [], }); expect(result.deleteResult).toBe(DeleteResult.Range); @@ -3281,7 +3292,7 @@ describe('deleteSelection - backward', () => { const marker = createSelectionMarker({ fontSize: '10px' }); const br = createBr(); const wrapper = 'WRAPPER' as any; - const entity = createEntity(wrapper, true); + const entity = createEntity(wrapper); para.segments.push(marker, br); model.blocks.push(entity, para); @@ -3291,6 +3302,7 @@ describe('deleteSelection - backward', () => { const result = deleteSelection(model, [backwardDeleteCollapsedSelection], { newEntities, deletedEntities, + newImages: [], }); expect(result.deleteResult).toBe(DeleteResult.Range); diff --git a/packages-content-model/roosterjs-content-model-editor/test/modelApi/entity/insertEntityModelTest.ts b/packages-content-model/roosterjs-content-model-editor/test/modelApi/entity/insertEntityModelTest.ts index a6ea985581d..1c59a012554 100644 --- a/packages-content-model/roosterjs-content-model-editor/test/modelApi/entity/insertEntityModelTest.ts +++ b/packages-content-model/roosterjs-content-model-editor/test/modelApi/entity/insertEntityModelTest.ts @@ -416,7 +416,7 @@ describe('insertEntityModel, block element, not focus after entity', () => { }); it('Before another entity', () => { - const entity2 = createEntity({} as any, true); + const entity2 = createEntity({} as any); const br = createBr(); runTest( @@ -978,7 +978,7 @@ describe('insertEntityModel, block element, focus after entity', () => { }); it('Before another entity', () => { - const entity2 = createEntity({} as any, true); + const entity2 = createEntity({} as any); runTest( () => { @@ -1516,7 +1516,7 @@ describe('insertEntityModel, inline element, not focus after entity', () => { }); it('Before another entity', () => { - const entity2 = createEntity({} as any, true); + const entity2 = createEntity({} as any); runTest( () => { @@ -2033,7 +2033,7 @@ describe('insertEntityModel, inline element, focus after entity', () => { }); it('Before another entity', () => { - const entity2 = createEntity({} as any, true); + const entity2 = createEntity({} as any); runTest( () => { diff --git a/packages-content-model/roosterjs-content-model-editor/test/modelApi/format/pendingFormatTest.ts b/packages-content-model/roosterjs-content-model-editor/test/modelApi/format/pendingFormatTest.ts index 1b61b885a1d..61f88ab0d05 100644 --- a/packages-content-model/roosterjs-content-model-editor/test/modelApi/format/pendingFormatTest.ts +++ b/packages-content-model/roosterjs-content-model-editor/test/modelApi/format/pendingFormatTest.ts @@ -37,14 +37,16 @@ describe('pendingFormat.setPendingFormat', () => { const div = document.createElement('div'); const editor = new ContentModelEditor(div); const mockedFormat = 'FORMAT' as any; - const mockedPosition = 'POSITION' as any; + const mockedContainer = 'C' as any; + const mockedOffset = 'O' as any; - setPendingFormat(editor, mockedFormat, mockedPosition); + setPendingFormat(editor, mockedFormat, mockedContainer, mockedOffset); expect((editor as any).core.lifecycle.customData.__ContentModelPendingFormat.value).toEqual( { format: mockedFormat, - position: mockedPosition, + posContainer: mockedContainer, + posOffset: mockedOffset, } ); }); @@ -55,12 +57,14 @@ describe('pendingFormat.clearPendingFormat', () => { const div = document.createElement('div'); const editor = new ContentModelEditor(div); const mockedFormat = 'FORMAT' as any; - const mockedPosition = 'POSITION' as any; + const mockedContainer = 'C' as any; + const mockedOffset = 'O' as any; (editor as any).core.lifecycle.customData.__ContentModelPendingFormat = { value: { format: mockedFormat, - position: mockedPosition, + posContainer: mockedContainer, + posOffset: mockedOffset, }, }; @@ -69,7 +73,8 @@ describe('pendingFormat.clearPendingFormat', () => { expect((editor as any).core.lifecycle.customData.__ContentModelPendingFormat.value).toEqual( { format: null, - position: null, + posContainer: null, + posOffset: null, } ); }); @@ -80,26 +85,23 @@ describe('pendingFormat.canApplyPendingFormat', () => { const div = document.createElement('div'); const editor = new ContentModelEditor(div); const mockedFormat = 'FORMAT' as any; - const mockedPosition = 'POSITION' as any; - const equalTo = jasmine.createSpy('equalto').and.returnValue(true); - const mockedPosition2 = { - equalTo, - }; + const mockedContainer = 'C' as any; + const mockedOffset = 'O' as any; - editor.getFocusedPosition = () => mockedPosition2 as any; + editor.getFocusedPosition = () => ({ node: mockedContainer, offset: mockedOffset } as any); (editor as any).core.lifecycle.customData.__ContentModelPendingFormat = { value: { format: mockedFormat, - position: mockedPosition, + posContainer: mockedContainer, + posOffset: mockedOffset, }, }; const result = canApplyPendingFormat(editor); expect(result).toBeTrue(); - expect(equalTo).toHaveBeenCalledWith(mockedPosition); }); it('no pending format', () => { @@ -146,11 +148,12 @@ describe('pendingFormat.canApplyPendingFormat', () => { const div = document.createElement('div'); const editor = new ContentModelEditor(div); const mockedFormat = 'FORMAT' as any; - const mockedPosition = 'POSITION' as any; + const mockedContainer1 = 'C1'; + const mockedContainer2 = 'C2'; - const equalTo = jasmine.createSpy('equalto').and.returnValue(false); const mockedPosition2 = { - equalTo, + node: mockedContainer2, + offset: 1, }; editor.getFocusedPosition = () => mockedPosition2 as any; @@ -158,13 +161,13 @@ describe('pendingFormat.canApplyPendingFormat', () => { (editor as any).core.lifecycle.customData.__ContentModelPendingFormat = { value: { format: mockedFormat, - position: mockedPosition, + posContainer: mockedContainer1, + posOffset: 0, }, }; const result = canApplyPendingFormat(editor); expect(result).toBeFalse(); - expect(equalTo).toHaveBeenCalledWith(mockedPosition); }); }); diff --git a/packages-content-model/roosterjs-content-model-editor/test/modelApi/list/setListTypeTest.ts b/packages-content-model/roosterjs-content-model-editor/test/modelApi/list/setListTypeTest.ts index 44130311d61..0774263e390 100644 --- a/packages-content-model/roosterjs-content-model-editor/test/modelApi/list/setListTypeTest.ts +++ b/packages-content-model/roosterjs-content-model-editor/test/modelApi/list/setListTypeTest.ts @@ -8,6 +8,8 @@ import { createListItem, createListLevel, createParagraph, + createTable, + createTableCell, createText, } from 'roosterjs-content-model-dom'; @@ -540,6 +542,115 @@ describe('indent', () => { }); }); + it('do not turn on list for table', () => { + const group = createContentModelDocument(); + const para1 = createParagraph(); + const table = createTable(1); + const para2 = createParagraph(); + const text1 = createText('test1'); + const text2 = createText('test2'); + const cell = createTableCell(); + + para1.segments.push(text1); + para2.segments.push(text2); + table.rows[0].cells.push(cell); + group.blocks.push(para1, table, para2); + + text1.isSelected = true; + text2.isSelected = true; + cell.isSelected = true; + + const result = setListType(group, 'OL'); + + expect(result).toBeTrue(); + expect(group).toEqual({ + blockGroupType: 'Document', + blocks: [ + { + blockType: 'BlockGroup', + blockGroupType: 'ListItem', + blocks: [para1], + levels: [ + { + listType: 'OL', + dataset: {}, + format: { + startNumberOverride: 1, + direction: undefined, + textAlign: undefined, + marginTop: undefined, + }, + }, + ], + formatHolder: { + segmentType: 'SelectionMarker', + isSelected: true, + format: { + fontFamily: undefined, + fontSize: undefined, + textColor: undefined, + }, + }, + format: {}, + }, + { + blockType: 'BlockGroup', + blockGroupType: 'ListItem', + blocks: [table], + levels: [ + { + listType: 'OL', + dataset: {}, + format: { + startNumberOverride: undefined, + direction: undefined, + textAlign: undefined, + marginTop: undefined, + }, + }, + ], + formatHolder: { + segmentType: 'SelectionMarker', + isSelected: true, + format: { + fontFamily: undefined, + fontSize: undefined, + textColor: undefined, + }, + }, + format: {}, + }, + { + blockType: 'BlockGroup', + blockGroupType: 'ListItem', + blocks: [para2], + levels: [ + { + listType: 'OL', + dataset: {}, + format: { + marginTop: undefined, + direction: undefined, + textAlign: undefined, + startNumberOverride: undefined, + }, + }, + ], + formatHolder: { + segmentType: 'SelectionMarker', + isSelected: true, + format: { + fontFamily: undefined, + fontSize: undefined, + textColor: undefined, + }, + }, + format: {}, + }, + ], + }); + }); + it('Change style type to existing list', () => { const group = createContentModelDocument(); const list1 = createListItem([createListLevel('OL')]); diff --git a/packages-content-model/roosterjs-content-model-editor/test/modelApi/selection/areSameRangeExTest.ts b/packages-content-model/roosterjs-content-model-editor/test/modelApi/selection/areSameRangeExTest.ts new file mode 100644 index 00000000000..63f5cdbcac7 --- /dev/null +++ b/packages-content-model/roosterjs-content-model-editor/test/modelApi/selection/areSameRangeExTest.ts @@ -0,0 +1,360 @@ +import { areSameRangeEx } from '../../../lib/modelApi/selection/areSameRangeEx'; +import { DOMSelection } from 'roosterjs-content-model-types'; + +describe('areSameRangeEx', () => { + const startContainer = 'MockedStartContainer' as any; + const endContainer = 'MockedEndContainer' as any; + const startOffset = 1; + const endOffset = 2; + const table = 'MockedTable' as any; + const image = 'MockedImage' as any; + + function runTest(r1: DOMSelection, r2: DOMSelection, result: boolean) { + expect(areSameRangeEx(r1, r2)).toBe(result); + } + + it('Same object', () => { + const r = 'MockedRange' as any; + runTest(r, r, true); + }); + + it('Same normal range', () => { + runTest( + { + type: 'range', + range: { + startContainer, + endContainer, + startOffset, + endOffset, + } as any, + }, + { + type: 'range', + range: { + startContainer, + endContainer, + startOffset, + endOffset, + } as any, + }, + true + ); + }); + + it('Same table range', () => { + runTest( + { + type: 'table', + table, + firstColumn: 1, + lastColumn: 3, + firstRow: 2, + lastRow: 4, + }, + { + type: 'table', + table, + firstColumn: 1, + lastColumn: 3, + firstRow: 2, + lastRow: 4, + }, + true + ); + }); + + it('Same image range', () => { + runTest( + { + type: 'image', + image, + }, + { + type: 'image', + image, + }, + true + ); + }); + + it('normal range and table range', () => { + runTest( + { + type: 'range', + range: { + startContainer, + endContainer, + startOffset, + endOffset, + } as any, + }, + { + type: 'table', + table, + firstColumn: 1, + lastColumn: 3, + firstRow: 2, + lastRow: 4, + }, + false + ); + }); + + it('normal range and image range', () => { + runTest( + { + type: 'range', + range: { + startContainer, + endContainer, + startOffset, + endOffset, + } as any, + }, + { + type: 'image', + image, + }, + false + ); + }); + + it('table range and image range', () => { + runTest( + { + type: 'table', + table, + firstColumn: 1, + firstRow: 2, + lastColumn: 3, + lastRow: 4, + }, + { + type: 'image', + image, + }, + false + ); + }); + + it('different normal range - 1', () => { + runTest( + { + type: 'range', + range: { + startContainer, + endContainer, + startOffset, + endOffset, + } as any, + }, + { + type: 'range', + range: { + startContainer: 'Container 2' as any, + endContainer, + startOffset, + endOffset, + } as any, + }, + false + ); + }); + + it('different normal range - 2', () => { + runTest( + { + type: 'range', + range: { + startContainer, + endContainer, + startOffset, + endOffset, + } as any, + }, + { + type: 'range', + range: { + startContainer, + endContainer: 'Container 2' as any, + startOffset, + endOffset, + } as any, + }, + false + ); + }); + + it('different normal range - 3', () => { + runTest( + { + type: 'range', + range: { + startContainer, + endContainer, + startOffset, + endOffset, + } as any, + }, + { + type: 'range', + range: { + startContainer, + endContainer, + startOffset: 3, + endOffset, + } as any, + }, + false + ); + }); + + it('different normal range - 4', () => { + runTest( + { + type: 'range', + range: { + startContainer, + endContainer, + startOffset, + endOffset, + } as any, + }, + { + type: 'range', + range: { + startContainer, + endContainer, + startOffset, + endOffset: 4, + } as any, + }, + false + ); + }); + + it('different table range - 1', () => { + runTest( + { + type: 'table', + table, + firstColumn: 1, + firstRow: 2, + lastColumn: 3, + lastRow: 4, + }, + { + type: 'table', + table: 'Table2' as any, + firstColumn: 1, + firstRow: 2, + lastColumn: 3, + lastRow: 4, + }, + false + ); + }); + + it('different table range - 2', () => { + runTest( + { + type: 'table', + table, + firstColumn: 1, + firstRow: 2, + lastColumn: 3, + lastRow: 4, + }, + { + type: 'table', + table, + firstColumn: 0, + firstRow: 2, + lastColumn: 3, + lastRow: 4, + }, + false + ); + }); + + it('different table range - 2', () => { + runTest( + { + type: 'table', + table, + firstColumn: 1, + firstRow: 2, + lastColumn: 3, + lastRow: 4, + }, + { + type: 'table', + table, + firstColumn: 1, + firstRow: 0, + lastColumn: 3, + lastRow: 4, + }, + false + ); + }); + + it('different table range - 3', () => { + runTest( + { + type: 'table', + table, + firstColumn: 1, + firstRow: 2, + lastColumn: 3, + lastRow: 4, + }, + { + type: 'table', + table, + firstColumn: 1, + firstRow: 2, + lastColumn: 0, + lastRow: 4, + }, + false + ); + }); + + it('different table range - 4', () => { + runTest( + { + type: 'table', + table, + firstColumn: 1, + firstRow: 2, + lastColumn: 3, + lastRow: 4, + }, + { + type: 'table', + table, + firstColumn: 1, + firstRow: 2, + lastColumn: 3, + lastRow: 0, + }, + false + ); + }); + + it('different image range', () => { + runTest( + { + type: 'image', + image, + }, + { + type: 'image', + image: 'Image 2' as any, + }, + false + ); + }); +}); diff --git a/packages-content-model/roosterjs-content-model-editor/test/modelApi/selection/collectSelectionsTest.ts b/packages-content-model/roosterjs-content-model-editor/test/modelApi/selection/collectSelectionsTest.ts index cb7806b1f0d..a9080dc1aa4 100644 --- a/packages-content-model/roosterjs-content-model-editor/test/modelApi/selection/collectSelectionsTest.ts +++ b/packages-content-model/roosterjs-content-model-editor/test/modelApi/selection/collectSelectionsTest.ts @@ -186,7 +186,7 @@ describe('getSelectedSegmentsAndParagraphs', () => { }); it('Include editable entity, but filter out readonly entity', () => { - const e1 = createEntity(null!, true); + const e1 = createEntity(null!); const e2 = createEntity(null!, false); const p1 = createParagraph(); diff --git a/packages-content-model/roosterjs-content-model-editor/test/modelApi/selection/iterateSelectionsTest.ts b/packages-content-model/roosterjs-content-model-editor/test/modelApi/selection/iterateSelectionsTest.ts index c5d95c461d8..fba405cde0f 100644 --- a/packages-content-model/roosterjs-content-model-editor/test/modelApi/selection/iterateSelectionsTest.ts +++ b/packages-content-model/roosterjs-content-model-editor/test/modelApi/selection/iterateSelectionsTest.ts @@ -1329,7 +1329,7 @@ describe('iterateSelections', () => { it('With selected entity', () => { const doc = createContentModelDocument(); const para = createParagraph(); - const entity = createEntity(null!, true); + const entity = createEntity(null!); entity.isSelected = true; diff --git a/packages-content-model/roosterjs-content-model-editor/test/modelApi/selection/setSelectionTest.ts b/packages-content-model/roosterjs-content-model-editor/test/modelApi/selection/setSelectionTest.ts index d16ee9a219c..f6bcf753a2f 100644 --- a/packages-content-model/roosterjs-content-model-editor/test/modelApi/selection/setSelectionTest.ts +++ b/packages-content-model/roosterjs-content-model-editor/test/modelApi/selection/setSelectionTest.ts @@ -1,5 +1,6 @@ import { setSelection } from '../../../lib/modelApi/selection/setSelection'; import { + createBr, createContentModelDocument, createDivider, createGeneralSegment, @@ -725,4 +726,99 @@ describe('setSelection', () => { ], }); }); + + it('Update table selection', () => { + const model = createContentModelDocument(); + const table = createTable(3); + + const cell00 = createTableCell(); + const cell01 = createTableCell(); + const cell02 = createTableCell(); + const cell10 = createTableCell(); + const cell11 = createTableCell(); + const cell12 = createTableCell(); + const cell20 = createTableCell(); + const cell21 = createTableCell(); + const cell22 = createTableCell(); + + table.rows[0].cells.push(cell00, cell01, cell02); + table.rows[1].cells.push(cell10, cell11, cell12); + table.rows[2].cells.push(cell20, cell21, cell22); + + cell00.isSelected = true; + cell01.isSelected = true; + cell10.isSelected = true; + cell11.isSelected = true; + + model.blocks.push(table); + + setSelection(model, cell11, cell22); + + expect(cell00.isSelected).toBeFalsy(); + expect(cell01.isSelected).toBeFalsy(); + expect(cell02.isSelected).toBeFalsy(); + expect(cell10.isSelected).toBeFalsy(); + expect(cell11.isSelected).toBeTrue(); + expect(cell12.isSelected).toBeTrue(); + expect(cell20.isSelected).toBeFalsy(); + expect(cell21.isSelected).toBeTrue(); + expect(cell22.isSelected).toBeTrue(); + }); + + it('Clear table selection', () => { + const model = createContentModelDocument(); + const table = createTable(3); + + const cell00 = createTableCell(); + const cell01 = createTableCell(); + const cell02 = createTableCell(); + const cell10 = createTableCell(); + const cell11 = createTableCell(); + const cell12 = createTableCell(); + const cell20 = createTableCell(); + const cell21 = createTableCell(); + const cell22 = createTableCell(); + + table.rows[0].cells.push(cell00, cell01, cell02); + table.rows[1].cells.push(cell10, cell11, cell12); + table.rows[2].cells.push(cell20, cell21, cell22); + + cell00.isSelected = true; + cell01.isSelected = true; + cell10.isSelected = true; + cell11.isSelected = true; + + model.blocks.push(table); + + setSelection(model); + + expect(cell00.isSelected).toBeFalsy(); + expect(cell01.isSelected).toBeFalsy(); + expect(cell02.isSelected).toBeFalsy(); + expect(cell10.isSelected).toBeFalsy(); + expect(cell11.isSelected).toBeFalsy(); + expect(cell12.isSelected).toBeFalsy(); + expect(cell20.isSelected).toBeFalsy(); + expect(cell21.isSelected).toBeFalsy(); + expect(cell22.isSelected).toBeFalsy(); + }); + + it('Clear selection under table', () => { + const model = createContentModelDocument(); + const table = createTable(1); + const cell = createTableCell(); + const para = createParagraph(); + const segment = createBr(); + + segment.isSelected = true; + para.segments.push(segment); + cell.blocks.push(para); + table.rows[0].cells.push(cell); + model.blocks.push(table); + + setSelection(model); + + expect(cell.isSelected).toBeFalsy(); + expect(segment.isSelected).toBeFalsy(); + }); }); diff --git a/packages-content-model/roosterjs-content-model-editor/test/publicApi/block/paragraphTestCommon.ts b/packages-content-model/roosterjs-content-model-editor/test/publicApi/block/paragraphTestCommon.ts index 19b1e82ac6d..e5f4dd98010 100644 --- a/packages-content-model/roosterjs-content-model-editor/test/publicApi/block/paragraphTestCommon.ts +++ b/packages-content-model/roosterjs-content-model-editor/test/publicApi/block/paragraphTestCommon.ts @@ -19,6 +19,7 @@ export function paragraphTestCommon( expect(model).toEqual(result); }); const triggerPluginEvent = jasmine.createSpy('triggerPluginEvent'); + const getVisibleViewport = jasmine.createSpy('getVisibleViewport'); const editor = ({ createContentModel: () => model, addUndoSnapshot, @@ -28,6 +29,7 @@ export function paragraphTestCommon( getFocusedPosition: () => ({}), isDarkMode: () => false, triggerPluginEvent, + getVisibleViewport, } as any) as IContentModelEditor; executionCallback(editor); diff --git a/packages-content-model/roosterjs-content-model-editor/test/publicApi/block/setAlignmentTest.ts b/packages-content-model/roosterjs-content-model-editor/test/publicApi/block/setAlignmentTest.ts index 071a5d55e1c..124af97e439 100644 --- a/packages-content-model/roosterjs-content-model-editor/test/publicApi/block/setAlignmentTest.ts +++ b/packages-content-model/roosterjs-content-model-editor/test/publicApi/block/setAlignmentTest.ts @@ -17,7 +17,9 @@ describe('setAlignment', () => { ) { paragraphTestCommon( 'setAlignment', - editor => setAlignment(editor, 'right'), + editor => { + setAlignment(editor, 'right'); + }, model, result, calledTimes @@ -415,11 +417,13 @@ describe('setAlignment in table', () => { let setContentModel: jasmine.Spy; let createContentModel: jasmine.Spy; let triggerPluginEvent: jasmine.Spy; + let getVisibleViewport: jasmine.Spy; beforeEach(() => { setContentModel = jasmine.createSpy('setContentModel'); createContentModel = jasmine.createSpy('createContentModel'); triggerPluginEvent = jasmine.createSpy('triggerPluginEvent'); + getVisibleViewport = jasmine.createSpy('getVisibleViewport'); spyOn(normalizeTable, 'normalizeTable'); @@ -430,6 +434,7 @@ describe('setAlignment in table', () => { createContentModel, isDarkMode: () => false, triggerPluginEvent, + getVisibleViewport, } as any) as IContentModelEditor; }); @@ -813,11 +818,13 @@ describe('setAlignment in list', () => { let setContentModel: jasmine.Spy; let createContentModel: jasmine.Spy; let triggerPluginEvent: jasmine.Spy; + let getVisibleViewport: jasmine.Spy; beforeEach(() => { setContentModel = jasmine.createSpy('setContentModel'); createContentModel = jasmine.createSpy('createContentModel'); triggerPluginEvent = jasmine.createSpy('triggerPluginEvent'); + getVisibleViewport = jasmine.createSpy('getVisibleViewport'); editor = ({ focus: () => {}, @@ -826,6 +833,7 @@ describe('setAlignment in list', () => { createContentModel, isDarkMode: () => false, triggerPluginEvent, + getVisibleViewport, } as any) as IContentModelEditor; }); diff --git a/packages-content-model/roosterjs-content-model-editor/test/publicApi/editing/editingTestCommon.ts b/packages-content-model/roosterjs-content-model-editor/test/publicApi/editing/editingTestCommon.ts index 3ae6329b1b8..f8de272c86a 100644 --- a/packages-content-model/roosterjs-content-model-editor/test/publicApi/editing/editingTestCommon.ts +++ b/packages-content-model/roosterjs-content-model-editor/test/publicApi/editing/editingTestCommon.ts @@ -14,6 +14,7 @@ export function editingTestCommon( spyOn(pendingFormat, 'getPendingFormat').and.returnValue(null); const triggerPluginEvent = jasmine.createSpy('triggerPluginEvent'); + const getVisibleViewport = jasmine.createSpy('getVisibleViewport'); const triggerContentChangedEvent = jasmine.createSpy('triggerContentChangedEvent'); const addUndoSnapshot = jasmine @@ -38,6 +39,7 @@ export function editingTestCommon( isDisposed: () => false, getFocusedPosition: () => null! as NodePosition, triggerContentChangedEvent, + getVisibleViewport, isDarkMode: () => false, } as any) as IContentModelEditor; diff --git a/packages-content-model/roosterjs-content-model-editor/test/publicApi/editing/keyboardDeleteTest.ts b/packages-content-model/roosterjs-content-model-editor/test/publicApi/editing/keyboardDeleteTest.ts index f94c51cbdb5..2150875bd2a 100644 --- a/packages-content-model/roosterjs-content-model-editor/test/publicApi/editing/keyboardDeleteTest.ts +++ b/packages-content-model/roosterjs-content-model-editor/test/publicApi/editing/keyboardDeleteTest.ts @@ -2,8 +2,8 @@ import * as deleteSelection from '../../../lib/modelApi/edit/deleteSelection'; import * as formatWithContentModel from '../../../lib/publicApi/utils/formatWithContentModel'; import * as handleKeyboardEventResult from '../../../lib/editor/utils/handleKeyboardEventCommon'; import keyboardDelete from '../../../lib/publicApi/editing/keyboardDelete'; -import { ChangeSource, Keys, SelectionRangeEx, SelectionRangeTypes } from 'roosterjs-editor-types'; -import { ContentModelDocument } from 'roosterjs-content-model-types'; +import { ChangeSource, Keys } from 'roosterjs-editor-types'; +import { ContentModelDocument, DOMSelection } from 'roosterjs-content-model-types'; import { deleteAllSegmentBefore } from '../../../lib/modelApi/edit/deleteSteps/deleteAllSegmentBefore'; import { editingTestCommon } from './editingTestCommon'; import { IContentModelEditor } from '../../../lib/publicTypes/IContentModelEditor'; @@ -52,13 +52,11 @@ describe('keyboardDelete', () => { newEditor => { editor = newEditor; - editor.getSelectionRangeEx = () => ({ - type: SelectionRangeTypes.Normal, - ranges: [ - { - collapsed: false, - }, - ], + editor.getDOMSelection = () => ({ + type: 'range', + range: { + collapsed: false, + }, }); const result = keyboardDelete(editor, mockedEvent); @@ -73,6 +71,7 @@ describe('keyboardDelete', () => { newEntities: [], deletedEntities: [], rawEvent: mockedEvent, + newImages: [], skipUndoSnapshot: true, }); } @@ -377,9 +376,9 @@ describe('keyboardDelete', () => { const editor = ({ addUndoSnapshot, - getSelectionRangeEx: () => ({ - type: SelectionRangeTypes.Normal, - ranges: [{ collapsed: false }], + getDOMSelection: () => ({ + type: 'range', + range: { collapsed: false }, }), } as any) as IContentModelEditor; const which = Keys.DELETE; @@ -401,9 +400,9 @@ describe('keyboardDelete', () => { const preventDefault = jasmine.createSpy('preventDefault'); const editor = { - getSelectionRangeEx: () => ({ - type: SelectionRangeTypes.Normal, - ranges: [{ collapsed: false }], + getDOMSelection: () => ({ + type: 'range', + range: { collapsed: false }, }), } as any; const which = Keys.BACKSPACE; @@ -422,19 +421,16 @@ describe('keyboardDelete', () => { it('No need to delete - Backspace', () => { const rawEvent = { which: Keys.BACKSPACE } as any; - const range: SelectionRangeEx = { - type: SelectionRangeTypes.Normal, - ranges: [ - ({ - collapsed: true, - startContainer: document.createTextNode('test'), - startOffset: 2, - } as any) as Range, - ], - areAllCollapsed: true, + const range: DOMSelection = { + type: 'range', + range: ({ + collapsed: true, + startContainer: document.createTextNode('test'), + startOffset: 2, + } as any) as Range, }; const editor = { - getSelectionRangeEx: () => range, + getDOMSelection: () => range, } as any; const formatWithContentModelSpy = spyOn(formatWithContentModel, 'formatWithContentModel'); @@ -446,19 +442,16 @@ describe('keyboardDelete', () => { it('No need to delete - Delete', () => { const rawEvent = { which: Keys.DELETE } as any; - const range: SelectionRangeEx = { - type: SelectionRangeTypes.Normal, - ranges: [ - ({ - collapsed: true, - startContainer: document.createTextNode('test'), - startOffset: 2, - } as any) as Range, - ], - areAllCollapsed: true, + const range: DOMSelection = { + type: 'range', + range: ({ + collapsed: true, + startContainer: document.createTextNode('test'), + startOffset: 2, + } as any) as Range, }; const editor = { - getSelectionRangeEx: () => range, + getDOMSelection: () => range, } as any; const formatWithContentModelSpy = spyOn(formatWithContentModel, 'formatWithContentModel'); @@ -470,20 +463,17 @@ describe('keyboardDelete', () => { it('Backspace from the beginning', () => { const rawEvent = { which: Keys.BACKSPACE } as any; - const range: SelectionRangeEx = { - type: SelectionRangeTypes.Normal, - ranges: [ - ({ - collapsed: true, - startContainer: document.createTextNode('test'), - startOffset: 0, - } as any) as Range, - ], - areAllCollapsed: true, + const range: DOMSelection = { + type: 'range', + range: ({ + collapsed: true, + startContainer: document.createTextNode('test'), + startOffset: 0, + } as any) as Range, }; const editor = { - getSelectionRangeEx: () => range, + getDOMSelection: () => range, } as any; const formatWithContentModelSpy = spyOn(formatWithContentModel, 'formatWithContentModel'); @@ -495,20 +485,17 @@ describe('keyboardDelete', () => { it('Delete from the last', () => { const rawEvent = { which: Keys.DELETE } as any; - const range: SelectionRangeEx = { - type: SelectionRangeTypes.Normal, - ranges: [ - ({ - collapsed: true, - startContainer: document.createTextNode('test'), - startOffset: 4, - } as any) as Range, - ], - areAllCollapsed: true, + const range: DOMSelection = { + type: 'range', + range: ({ + collapsed: true, + startContainer: document.createTextNode('test'), + startOffset: 4, + } as any) as Range, }; const editor = { - getSelectionRangeEx: () => range, + getDOMSelection: () => range, } as any; const formatWithContentModelSpy = spyOn(formatWithContentModel, 'formatWithContentModel'); diff --git a/packages-content-model/roosterjs-content-model-editor/test/publicApi/entity/insertEntityTest.ts b/packages-content-model/roosterjs-content-model-editor/test/publicApi/entity/insertEntityTest.ts index 66ec3b8aa7c..7188ee3abb4 100644 --- a/packages-content-model/roosterjs-content-model-editor/test/publicApi/entity/insertEntityTest.ts +++ b/packages-content-model/roosterjs-content-model-editor/test/publicApi/entity/insertEntityTest.ts @@ -1,6 +1,4 @@ -import * as commitEntity from 'roosterjs-editor-dom/lib/entity/commitEntity'; import * as formatWithContentModel from '../../../lib/publicApi/utils/formatWithContentModel'; -import * as getEntityFromElement from 'roosterjs-editor-dom/lib/entity/getEntityFromElement'; import * as insertEntityModel from '../../../lib/modelApi/entity/insertEntityModel'; import * as normalizeContentModel from 'roosterjs-content-model-dom/lib/modelApi/common/normalizeContentModel'; import insertEntity from '../../../lib/publicApi/entity/insertEntity'; @@ -13,14 +11,11 @@ describe('insertEntity', () => { let context: FormatWithContentModelContext; let wrapper: HTMLElement; const model = 'MockedModel' as any; - const newEntity = 'MockedEntity' as any; let formatWithContentModelSpy: jasmine.Spy; - let getEntityFromElementSpy: jasmine.Spy; let triggerContentChangedEventSpy: jasmine.Spy; let getDocumentSpy: jasmine.Spy; let createElementSpy: jasmine.Spy; - let commitEntitySpy: jasmine.Spy; let setPropertySpy: jasmine.Spy; let appendChildSpy: jasmine.Spy; let insertEntityModelSpy: jasmine.Spy; @@ -35,6 +30,7 @@ describe('insertEntity', () => { context = { newEntities: [], deletedEntities: [], + newImages: [], }; setPropertySpy = jasmine.createSpy('setPropertySpy'); @@ -57,8 +53,6 @@ describe('insertEntity', () => { formatter(model, context); options?.getChangeData?.(); }); - getEntityFromElementSpy = spyOn(getEntityFromElement, 'default').and.returnValue(newEntity); - commitEntitySpy = spyOn(commitEntity, 'default'); triggerContentChangedEventSpy = jasmine.createSpy('triggerContentChangedEventSpy'); createElementSpy = jasmine.createSpy('createElementSpy').and.returnValue(wrapper); getDocumentSpy = jasmine.createSpy('getDocumentSpy').and.returnValue({ @@ -80,7 +74,6 @@ describe('insertEntity', () => { expect(createElementSpy).toHaveBeenCalledWith('span'); expect(setPropertySpy).toHaveBeenCalledWith('display', 'inline-block'); expect(appendChildSpy).not.toHaveBeenCalled(); - expect(commitEntitySpy).toHaveBeenCalledWith(wrapper, type, true); expect(formatWithContentModelSpy.calls.argsFor(0)[0]).toBe(editor); expect(formatWithContentModelSpy.calls.argsFor(0)[1]).toBe(apiName); expect(formatWithContentModelSpy.calls.argsFor(0)[3].changeSource).toEqual( @@ -92,9 +85,11 @@ describe('insertEntity', () => { segmentType: 'Entity', blockType: 'Entity', format: {}, - id: undefined, - type: type, - isReadonly: true, + entityFormat: { + id: undefined, + entityType: type, + isReadonly: true, + }, wrapper: wrapper, }, 'begin', @@ -102,12 +97,21 @@ describe('insertEntity', () => { undefined, context ); - expect(getEntityFromElementSpy).toHaveBeenCalledWith(wrapper); expect(triggerContentChangedEventSpy).not.toHaveBeenCalled(); expect(transformToDarkColorSpy).not.toHaveBeenCalled(); expect(normalizeContentModelSpy).toHaveBeenCalled(); - expect(entity).toBe(newEntity); + expect(entity).toEqual({ + segmentType: 'Entity', + blockType: 'Entity', + format: {}, + entityFormat: { + id: undefined, + entityType: type, + isReadonly: true, + }, + wrapper: wrapper, + }); }); it('block inline entity to root', () => { @@ -116,7 +120,6 @@ describe('insertEntity', () => { expect(createElementSpy).toHaveBeenCalledWith('div'); expect(setPropertySpy).toHaveBeenCalledWith('display', null); expect(appendChildSpy).not.toHaveBeenCalled(); - expect(commitEntitySpy).toHaveBeenCalledWith(wrapper, type, true); expect(formatWithContentModelSpy.calls.argsFor(0)[0]).toBe(editor); expect(formatWithContentModelSpy.calls.argsFor(0)[1]).toBe(apiName); expect(formatWithContentModelSpy.calls.argsFor(0)[3].changeSource).toEqual( @@ -128,9 +131,11 @@ describe('insertEntity', () => { segmentType: 'Entity', blockType: 'Entity', format: {}, - id: undefined, - type: type, - isReadonly: true, + entityFormat: { + id: undefined, + entityType: type, + isReadonly: true, + }, wrapper: wrapper, }, 'root', @@ -138,12 +143,21 @@ describe('insertEntity', () => { undefined, context ); - expect(getEntityFromElementSpy).toHaveBeenCalledWith(wrapper); expect(triggerContentChangedEventSpy).not.toHaveBeenCalled(); expect(transformToDarkColorSpy).not.toHaveBeenCalled(); expect(normalizeContentModelSpy).toHaveBeenCalled(); - expect(entity).toBe(newEntity); + expect(entity).toEqual({ + segmentType: 'Entity', + blockType: 'Entity', + format: {}, + entityFormat: { + id: undefined, + entityType: type, + isReadonly: true, + }, + wrapper: wrapper, + }); }); it('block inline entity with more options', () => { @@ -159,7 +173,6 @@ describe('insertEntity', () => { expect(createElementSpy).toHaveBeenCalledWith('div'); expect(setPropertySpy).toHaveBeenCalledWith('display', 'none'); expect(appendChildSpy).toHaveBeenCalledWith(contentNode); - expect(commitEntitySpy).toHaveBeenCalledWith(wrapper, type, true); expect(formatWithContentModelSpy.calls.argsFor(0)[0]).toBe(editor); expect(formatWithContentModelSpy.calls.argsFor(0)[1]).toBe(apiName); expect(formatWithContentModelSpy.calls.argsFor(0)[3].changeSource).toEqual( @@ -172,9 +185,11 @@ describe('insertEntity', () => { segmentType: 'Entity', blockType: 'Entity', format: {}, - id: undefined, - type: type, - isReadonly: true, + entityFormat: { + id: undefined, + entityType: type, + isReadonly: true, + }, wrapper: wrapper, }, 'focus', @@ -182,12 +197,21 @@ describe('insertEntity', () => { true, context ); - expect(getEntityFromElementSpy).toHaveBeenCalledWith(wrapper); expect(triggerContentChangedEventSpy).not.toHaveBeenCalled(); expect(transformToDarkColorSpy).not.toHaveBeenCalled(); expect(normalizeContentModelSpy).toHaveBeenCalled(); - expect(entity).toBe(newEntity); + expect(entity).toEqual({ + segmentType: 'Entity', + blockType: 'Entity', + format: {}, + entityFormat: { + id: undefined, + entityType: type, + isReadonly: true, + }, + wrapper: wrapper, + }); }); it('In dark mode', () => { @@ -198,7 +222,6 @@ describe('insertEntity', () => { expect(createElementSpy).toHaveBeenCalledWith('span'); expect(setPropertySpy).toHaveBeenCalledWith('display', 'inline-block'); expect(appendChildSpy).not.toHaveBeenCalled(); - expect(commitEntitySpy).toHaveBeenCalledWith(wrapper, type, true); expect(formatWithContentModelSpy.calls.argsFor(0)[0]).toBe(editor); expect(formatWithContentModelSpy.calls.argsFor(0)[1]).toBe(apiName); expect(formatWithContentModelSpy.calls.argsFor(0)[3].changeSource).toEqual( @@ -210,9 +233,11 @@ describe('insertEntity', () => { segmentType: 'Entity', blockType: 'Entity', format: {}, - id: undefined, - type: type, - isReadonly: true, + entityFormat: { + id: undefined, + entityType: type, + isReadonly: true, + }, wrapper: wrapper, }, 'begin', @@ -220,7 +245,6 @@ describe('insertEntity', () => { undefined, context ); - expect(getEntityFromElementSpy).toHaveBeenCalledWith(wrapper); expect(triggerContentChangedEventSpy).not.toHaveBeenCalled(); expect(normalizeContentModelSpy).toHaveBeenCalled(); @@ -229,13 +253,25 @@ describe('insertEntity', () => { segmentType: 'Entity', blockType: 'Entity', format: {}, - id: undefined, - type: 'Entity', - isReadonly: true, + entityFormat: { + id: undefined, + entityType: 'Entity', + isReadonly: true, + }, wrapper, }, ]); - expect(entity).toBe(newEntity); + expect(entity).toEqual({ + segmentType: 'Entity', + blockType: 'Entity', + format: {}, + entityFormat: { + id: undefined, + entityType: type, + isReadonly: true, + }, + wrapper: wrapper, + }); }); }); diff --git a/packages-content-model/roosterjs-content-model-editor/test/publicApi/format/applyPendingFormatTest.ts b/packages-content-model/roosterjs-content-model-editor/test/publicApi/format/applyPendingFormatTest.ts index 89d881397da..fe370b86b14 100644 --- a/packages-content-model/roosterjs-content-model-editor/test/publicApi/format/applyPendingFormatTest.ts +++ b/packages-content-model/roosterjs-content-model-editor/test/publicApi/format/applyPendingFormatTest.ts @@ -50,6 +50,7 @@ describe('applyPendingFormat', () => { callback(model, { newEntities: [], deletedEntities: [], + newImages: [], }); } ); @@ -119,7 +120,7 @@ describe('applyPendingFormat', () => { spyOn(formatWithContentModel, 'formatWithContentModel').and.callFake( (_, apiName, callback) => { expect(apiName).toEqual('applyPendingFormat'); - callback(model, { newEntities: [], deletedEntities: [] }); + callback(model, { newEntities: [], deletedEntities: [], newImages: [] }); } ); spyOn(iterateSelections, 'iterateSelections').and.callFake((_, callback) => { @@ -179,7 +180,7 @@ describe('applyPendingFormat', () => { spyOn(formatWithContentModel, 'formatWithContentModel').and.callFake( (_, apiName, callback) => { expect(apiName).toEqual('applyPendingFormat'); - callback(model, { newEntities: [], deletedEntities: [] }); + callback(model, { newEntities: [], deletedEntities: [], newImages: [] }); } ); spyOn(iterateSelections, 'iterateSelections').and.callFake((_, callback) => { @@ -237,7 +238,7 @@ describe('applyPendingFormat', () => { spyOn(formatWithContentModel, 'formatWithContentModel').and.callFake( (_, apiName, callback) => { expect(apiName).toEqual('applyPendingFormat'); - callback(model, { newEntities: [], deletedEntities: [] }); + callback(model, { newEntities: [], deletedEntities: [], newImages: [] }); } ); spyOn(iterateSelections, 'iterateSelections').and.callFake((_, callback) => { @@ -282,7 +283,7 @@ describe('applyPendingFormat', () => { spyOn(formatWithContentModel, 'formatWithContentModel').and.callFake( (_, apiName, callback) => { expect(apiName).toEqual('applyPendingFormat'); - callback(model, { newEntities: [], deletedEntities: [] }); + callback(model, { newEntities: [], deletedEntities: [], newImages: [] }); } ); spyOn(iterateSelections, 'iterateSelections').and.callFake((_, callback) => { diff --git a/packages-content-model/roosterjs-content-model-editor/test/publicApi/format/clearFormatTest.ts b/packages-content-model/roosterjs-content-model-editor/test/publicApi/format/clearFormatTest.ts index e1c986404bf..7eddbeba0bf 100644 --- a/packages-content-model/roosterjs-content-model-editor/test/publicApi/format/clearFormatTest.ts +++ b/packages-content-model/roosterjs-content-model-editor/test/publicApi/format/clearFormatTest.ts @@ -7,13 +7,15 @@ import { IContentModelEditor } from '../../../lib/publicTypes/IContentModelEdito describe('clearFormat', () => { it('Clear format', () => { - const editor = ({} as any) as IContentModelEditor; + const editor = ({ + focus: () => {}, + } as any) as IContentModelEditor; const model = ('Model' as any) as ContentModelDocument; spyOn(formatWithContentModel, 'formatWithContentModel').and.callFake( (_, apiName, callback) => { expect(apiName).toEqual('clearFormat'); - callback(model, { newEntities: [], deletedEntities: [] }); + callback(model, { newEntities: [], deletedEntities: [], newImages: [] }); } ); spyOn(clearModelFormat, 'clearModelFormat'); diff --git a/packages-content-model/roosterjs-content-model-editor/test/publicApi/format/getFormatStateTest.ts b/packages-content-model/roosterjs-content-model-editor/test/publicApi/format/getFormatStateTest.ts index bb228f4fae5..7e9982e4a4b 100644 --- a/packages-content-model/roosterjs-content-model-editor/test/publicApi/format/getFormatStateTest.ts +++ b/packages-content-model/roosterjs-content-model-editor/test/publicApi/format/getFormatStateTest.ts @@ -1,11 +1,11 @@ import * as getPendingFormat from '../../../lib/modelApi/format/pendingFormat'; import * as retrieveModelFormatState from '../../../lib/modelApi/common/retrieveModelFormatState'; +import { DomToModelContext } from 'roosterjs-content-model-types'; +import { FormatState } from 'roosterjs-editor-types'; +import { IContentModelEditor } from '../../../lib/publicTypes/IContentModelEditor'; import getFormatState, { reducedModelChildProcessor, } from '../../../lib/publicApi/format/getFormatState'; -import { DomToModelContext } from 'roosterjs-content-model-types'; -import { FormatState, SelectionRangeTypes } from 'roosterjs-editor-types'; -import { IContentModelEditor } from '../../../lib/publicTypes/IContentModelEditor'; import { createContentModelDocument, createDomToModelContext, @@ -49,14 +49,11 @@ describe('getFormatState', () => { }); if (selectedNode) { - context.rangeEx = { - type: SelectionRangeTypes.Normal, - ranges: [ - { - commonAncestorContainer: selectedNode, - } as any, - ], - areAllCollapsed: false, + context.selection = { + type: 'range', + range: { + commonAncestorContainer: selectedNode, + } as any, }; } @@ -213,14 +210,11 @@ describe('reducedModelChildProcessor', () => { div.appendChild(span); span.textContent = 'test'; - context.rangeEx = { - type: SelectionRangeTypes.Normal, - ranges: [ - { - commonAncestorContainer: span, - } as any, - ], - areAllCollapsed: false, + context.selection = { + type: 'range', + range: { + commonAncestorContainer: span, + } as any, }; reducedModelChildProcessor(doc, div, context); @@ -257,14 +251,11 @@ describe('reducedModelChildProcessor', () => { span1.textContent = 'test1'; span2.textContent = 'test2'; span3.textContent = 'test3'; - context.rangeEx = { - type: SelectionRangeTypes.Normal, - ranges: [ - { - commonAncestorContainer: span2, - } as any, - ], - areAllCollapsed: false, + context.selection = { + type: 'range', + range: { + commonAncestorContainer: span2, + } as any, }; reducedModelChildProcessor(doc, div, context); @@ -301,14 +292,11 @@ describe('reducedModelChildProcessor', () => { span1.textContent = 'test1'; span2.innerHTML = '
line1
line2
'; span3.textContent = 'test3'; - context.rangeEx = { - type: SelectionRangeTypes.Normal, - ranges: [ - { - commonAncestorContainer: span2, - } as any, - ], - areAllCollapsed: false, + context.selection = { + type: 'range', + range: { + commonAncestorContainer: span2, + } as any, }; reducedModelChildProcessor(doc, div, context); @@ -372,14 +360,11 @@ describe('reducedModelChildProcessor', () => { span2.innerHTML = '
line1
line2
'; span3.textContent = 'test3'; - context.rangeEx = { - type: SelectionRangeTypes.Normal, - ranges: [ - { - commonAncestorContainer: span2, - } as any, - ], - areAllCollapsed: false, + context.selection = { + type: 'range', + range: { + commonAncestorContainer: span2, + } as any, }; reducedModelChildProcessor(doc, div1, context); @@ -421,14 +406,11 @@ describe('reducedModelChildProcessor', () => { const div = document.createElement('div'); div.innerHTML = 'aa
test1test2
bb'; - context.rangeEx = { - type: SelectionRangeTypes.Normal, - ranges: [ - { - commonAncestorContainer: div.querySelector('#selection') as HTMLElement, - } as any, - ], - areAllCollapsed: false, + context.selection = { + type: 'range', + range: { + commonAncestorContainer: div.querySelector('#selection') as HTMLElement, + } as any, }; reducedModelChildProcessor(doc, div, context); diff --git a/packages-content-model/roosterjs-content-model-editor/test/publicApi/image/changeImageTest.ts b/packages-content-model/roosterjs-content-model-editor/test/publicApi/image/changeImageTest.ts index ef2114b91e4..8e6207581a6 100644 --- a/packages-content-model/roosterjs-content-model-editor/test/publicApi/image/changeImageTest.ts +++ b/packages-content-model/roosterjs-content-model-editor/test/publicApi/image/changeImageTest.ts @@ -21,7 +21,7 @@ describe('changeImage', () => { calledTimes: number ) { const addUndoSnapshot = jasmine - .createSpy() + .createSpy('addUndoSnapshot') .and.callFake( (callback: () => void, source: string, canUndoByBackspace, param: any) => { expect(source).toBe(undefined!); @@ -38,8 +38,11 @@ describe('changeImage', () => { const image = document.createElement('img'); - const getSelectionRangeEx = jasmine.createSpy().and.returnValues({ type: 2, image: image }); + const getDOMSelection = jasmine + .createSpy() + .and.returnValues({ type: 'image', image: image }); const triggerPluginEvent = jasmine.createSpy().and.callThrough(); + const getVisibleViewport = jasmine.createSpy().and.callThrough(); const editor = ({ createContentModel: () => model, @@ -48,8 +51,9 @@ describe('changeImage', () => { setContentModel, isDisposed: () => false, getDocument: () => document, - getSelectionRangeEx, + getDOMSelection, triggerPluginEvent, + getVisibleViewport, isDarkMode: () => false, } as any) as IContentModelEditor; diff --git a/packages-content-model/roosterjs-content-model-editor/test/publicApi/image/insertImageTest.ts b/packages-content-model/roosterjs-content-model-editor/test/publicApi/image/insertImageTest.ts index ff99ffad3e3..be0bc6b4518 100644 --- a/packages-content-model/roosterjs-content-model-editor/test/publicApi/image/insertImageTest.ts +++ b/packages-content-model/roosterjs-content-model-editor/test/publicApi/image/insertImageTest.ts @@ -31,6 +31,7 @@ describe('insertImage', () => { expect(model).toEqual(result); }); const triggerPluginEvent = jasmine.createSpy('triggerPluginEvent'); + const getVisibleViewport = jasmine.createSpy('getVisibleViewport'); const editor = ({ createContentModel: () => model, addUndoSnapshot, @@ -40,6 +41,7 @@ describe('insertImage', () => { getDocument: () => document, isDarkMode: () => false, triggerPluginEvent, + getVisibleViewport, } as any) as IContentModelEditor; executionCallback(editor); diff --git a/packages-content-model/roosterjs-content-model-editor/test/publicApi/link/adjustLinkSelectionTest.ts b/packages-content-model/roosterjs-content-model-editor/test/publicApi/link/adjustLinkSelectionTest.ts index 097c0b4e358..28ce8e237be 100644 --- a/packages-content-model/roosterjs-content-model-editor/test/publicApi/link/adjustLinkSelectionTest.ts +++ b/packages-content-model/roosterjs-content-model-editor/test/publicApi/link/adjustLinkSelectionTest.ts @@ -16,11 +16,13 @@ describe('adjustLinkSelection', () => { let setContentModel: jasmine.Spy; let createContentModel: jasmine.Spy; let triggerPluginEvent: jasmine.Spy; + let getVisibleViewport: jasmine.Spy; beforeEach(() => { setContentModel = jasmine.createSpy('setContentModel'); createContentModel = jasmine.createSpy('createContentModel'); triggerPluginEvent = jasmine.createSpy('triggerPluginEvent'); + getVisibleViewport = jasmine.createSpy('getVisibleViewport'); editor = ({ focus: () => {}, @@ -29,6 +31,7 @@ describe('adjustLinkSelection', () => { createContentModel, isDarkMode: () => false, triggerPluginEvent, + getVisibleViewport, } as any) as IContentModelEditor; }); diff --git a/packages-content-model/roosterjs-content-model-editor/test/publicApi/link/insertLinkTest.ts b/packages-content-model/roosterjs-content-model-editor/test/publicApi/link/insertLinkTest.ts index c66c2a55a03..a4d42bf19e6 100644 --- a/packages-content-model/roosterjs-content-model-editor/test/publicApi/link/insertLinkTest.ts +++ b/packages-content-model/roosterjs-content-model-editor/test/publicApi/link/insertLinkTest.ts @@ -16,11 +16,13 @@ describe('insertLink', () => { let setContentModel: jasmine.Spy; let createContentModel: jasmine.Spy; let triggerPluginEvent: jasmine.Spy; + let getVisibleViewport: jasmine.Spy; beforeEach(() => { setContentModel = jasmine.createSpy('setContentModel'); createContentModel = jasmine.createSpy('createContentModel'); triggerPluginEvent = jasmine.createSpy('triggerPluginEvent'); + getVisibleViewport = jasmine.createSpy('getVisibleViewport'); editor = ({ focus: () => {}, @@ -31,6 +33,7 @@ describe('insertLink', () => { getFocusedPosition: () => ({}), isDarkMode: () => false, triggerPluginEvent, + getVisibleViewport, } as any) as IContentModelEditor; }); @@ -230,7 +233,10 @@ describe('insertLink', () => { format: {}, src: 'test', dataset: {}, - link, + link: { + dataset: link.dataset, + format: { ...link.format, underline: false }, + }, isSelected: true, }, { @@ -335,7 +341,7 @@ describe('insertLink', () => { formatApiName: 'insertLink', }, contentModel: jasmine.anything(), - rangeEx: jasmine.anything(), + selection: jasmine.anything(), }); document.body.removeChild(div); diff --git a/packages-content-model/roosterjs-content-model-editor/test/publicApi/link/removeLinkTest.ts b/packages-content-model/roosterjs-content-model-editor/test/publicApi/link/removeLinkTest.ts index 122885ca79f..f4f36884304 100644 --- a/packages-content-model/roosterjs-content-model-editor/test/publicApi/link/removeLinkTest.ts +++ b/packages-content-model/roosterjs-content-model-editor/test/publicApi/link/removeLinkTest.ts @@ -14,11 +14,13 @@ describe('removeLink', () => { let setContentModel: jasmine.Spy; let createContentModel: jasmine.Spy; let triggerPluginEvent: jasmine.Spy; + let getVisibleViewport: jasmine.Spy; beforeEach(() => { setContentModel = jasmine.createSpy('setContentModel'); createContentModel = jasmine.createSpy('createContentModel'); triggerPluginEvent = jasmine.createSpy('triggerPluginEvent'); + getVisibleViewport = jasmine.createSpy('getVisibleViewport'); editor = ({ focus: () => {}, @@ -27,6 +29,7 @@ describe('removeLink', () => { createContentModel, isDarkMode: () => false, triggerPluginEvent, + getVisibleViewport, } as any) as IContentModelEditor; }); diff --git a/packages-content-model/roosterjs-content-model-editor/test/publicApi/list/setListStartNumberTest.ts b/packages-content-model/roosterjs-content-model-editor/test/publicApi/list/setListStartNumberTest.ts index 360653666db..1aec33f79ae 100644 --- a/packages-content-model/roosterjs-content-model-editor/test/publicApi/list/setListStartNumberTest.ts +++ b/packages-content-model/roosterjs-content-model-editor/test/publicApi/list/setListStartNumberTest.ts @@ -11,13 +11,22 @@ describe('setListStartNumber', () => { spyOn(formatWithContentModel, 'formatWithContentModel').and.callFake( (editor, apiName, callback) => { expect(apiName).toBe('setListStartNumber'); - const result = callback(input, { newEntities: [], deletedEntities: [] }); + const result = callback(input, { + newEntities: [], + deletedEntities: [], + newImages: [], + }); expect(result).toBe(expectedResult); } ); - setListStartNumber(null!, 2); + setListStartNumber( + { + focus: () => {}, + } as any, + 2 + ); expect(formatWithContentModel.formatWithContentModel).toHaveBeenCalledTimes(1); expect(input).toEqual(expectedModel); diff --git a/packages-content-model/roosterjs-content-model-editor/test/publicApi/list/setListStyleTest.ts b/packages-content-model/roosterjs-content-model-editor/test/publicApi/list/setListStyleTest.ts index fd10b6896f3..14efd88584e 100644 --- a/packages-content-model/roosterjs-content-model-editor/test/publicApi/list/setListStyleTest.ts +++ b/packages-content-model/roosterjs-content-model-editor/test/publicApi/list/setListStyleTest.ts @@ -12,13 +12,22 @@ describe('setListStyle', () => { spyOn(formatWithContentModel, 'formatWithContentModel').and.callFake( (editor, apiName, callback) => { expect(apiName).toBe('setListStyle'); - const result = callback(input, { newEntities: [], deletedEntities: [] }); + const result = callback(input, { + newEntities: [], + deletedEntities: [], + newImages: [], + }); expect(result).toBe(expectedResult); } ); - setListStyle(null!, style); + setListStyle( + { + focus: () => {}, + } as any, + style + ); expect(formatWithContentModel.formatWithContentModel).toHaveBeenCalledTimes(1); expect(input).toEqual(expectedModel); diff --git a/packages-content-model/roosterjs-content-model-editor/test/publicApi/list/toggleBulletTest.ts b/packages-content-model/roosterjs-content-model-editor/test/publicApi/list/toggleBulletTest.ts index 0dd1152f83b..7538416b763 100644 --- a/packages-content-model/roosterjs-content-model-editor/test/publicApi/list/toggleBulletTest.ts +++ b/packages-content-model/roosterjs-content-model-editor/test/publicApi/list/toggleBulletTest.ts @@ -11,6 +11,7 @@ describe('toggleBullet', () => { let focus: jasmine.Spy; let mockedModel: ContentModelDocument; let triggerPluginEvent: jasmine.Spy; + let getVisibleViewport: jasmine.Spy; beforeEach(() => { mockedModel = ({} as any) as ContentModelDocument; @@ -20,6 +21,7 @@ describe('toggleBullet', () => { setContentModel = jasmine.createSpy('setContentModel'); triggerPluginEvent = jasmine.createSpy('triggerPluginEvent'); focus = jasmine.createSpy('focus'); + getVisibleViewport = jasmine.createSpy('getVisibleViewport'); editor = ({ focus, @@ -30,6 +32,7 @@ describe('toggleBullet', () => { getFocusedPosition: () => ({}), isDarkMode: () => false, triggerPluginEvent, + getVisibleViewport, } as any) as IContentModelEditor; spyOn(setListType, 'setListType').and.returnValue(true); diff --git a/packages-content-model/roosterjs-content-model-editor/test/publicApi/list/toggleNumberingTest.ts b/packages-content-model/roosterjs-content-model-editor/test/publicApi/list/toggleNumberingTest.ts index c1a2a884cbb..927b1385bbf 100644 --- a/packages-content-model/roosterjs-content-model-editor/test/publicApi/list/toggleNumberingTest.ts +++ b/packages-content-model/roosterjs-content-model-editor/test/publicApi/list/toggleNumberingTest.ts @@ -11,6 +11,7 @@ describe('toggleNumbering', () => { let triggerPluginEvent: jasmine.Spy; let focus: jasmine.Spy; let mockedModel: ContentModelDocument; + let getVisibleViewport: jasmine.Spy; beforeEach(() => { mockedModel = ({} as any) as ContentModelDocument; @@ -20,6 +21,7 @@ describe('toggleNumbering', () => { setContentModel = jasmine.createSpy('setContentModel'); triggerPluginEvent = jasmine.createSpy('triggerPluginEvent'); focus = jasmine.createSpy('focus'); + getVisibleViewport = jasmine.createSpy('getVisibleViewport'); editor = ({ focus, @@ -30,6 +32,7 @@ describe('toggleNumbering', () => { getFocusedPosition: () => ({}), isDarkMode: () => false, triggerPluginEvent, + getVisibleViewport, } as any) as IContentModelEditor; spyOn(setListType, 'setListType').and.returnValue(true); diff --git a/packages-content-model/roosterjs-content-model-editor/test/publicApi/segment/changeFontSizeTest.ts b/packages-content-model/roosterjs-content-model-editor/test/publicApi/segment/changeFontSizeTest.ts index 96fee777b2b..0ea70c15776 100644 --- a/packages-content-model/roosterjs-content-model-editor/test/publicApi/segment/changeFontSizeTest.ts +++ b/packages-content-model/roosterjs-content-model-editor/test/publicApi/segment/changeFontSizeTest.ts @@ -5,7 +5,6 @@ import { createDomToModelContext, domToContentModel } from 'roosterjs-content-mo import { createRange } from 'roosterjs-editor-dom'; import { IContentModelEditor } from '../../../lib/publicTypes/IContentModelEditor'; import { segmentTestCommon } from './segmentTestCommon'; -import { SelectionRangeTypes } from 'roosterjs-editor-types'; describe('changeFontSize', () => { function runTest( @@ -16,7 +15,9 @@ describe('changeFontSize', () => { ) { segmentTestCommon( 'changeFontSize', - editor => changeFontSize(editor, change), + editor => { + changeFontSize(editor, change); + }, model, result, calledTimes @@ -330,6 +331,7 @@ describe('changeFontSize', () => { spyOn(pendingFormat, 'setPendingFormat'); spyOn(pendingFormat, 'getPendingFormat').and.returnValue(null); const triggerPluginEvent = jasmine.createSpy('triggerPluginEvent'); + const getVisibleViewport = jasmine.createSpy('getVisibleViewport'); const addUndoSnapshot = jasmine.createSpy().and.callFake((callback: () => void) => { callback(); @@ -345,15 +347,15 @@ describe('changeFontSize', () => { const editor = ({ createContentModel: (option: any) => domToContentModel(div, createDomToModelContext(undefined), { - type: SelectionRangeTypes.Normal, - ranges: [createRange(sub)], - areAllCollapsed: false, + type: 'range', + range: createRange(sub), }), addUndoSnapshot, focus: jasmine.createSpy(), setContentModel, isDarkMode: () => false, triggerPluginEvent, + getVisibleViewport, } as any) as IContentModelEditor; changeFontSize(editor, 'increase'); diff --git a/packages-content-model/roosterjs-content-model-editor/test/publicApi/segment/segmentTestCommon.ts b/packages-content-model/roosterjs-content-model-editor/test/publicApi/segment/segmentTestCommon.ts index b2f1adc235a..56c0dbc8b29 100644 --- a/packages-content-model/roosterjs-content-model-editor/test/publicApi/segment/segmentTestCommon.ts +++ b/packages-content-model/roosterjs-content-model-editor/test/publicApi/segment/segmentTestCommon.ts @@ -24,6 +24,7 @@ export function segmentTestCommon( expect(model).toEqual(result); }); const triggerPluginEvent = jasmine.createSpy('triggerPluginEvent'); + const getVisibleViewport = jasmine.createSpy('getVisibleViewport'); const editor = ({ createContentModel: () => model, addUndoSnapshot, @@ -33,6 +34,7 @@ export function segmentTestCommon( getFocusedPosition: () => null as NodePosition, isDarkMode: () => false, triggerPluginEvent, + getVisibleViewport, } as any) as IContentModelEditor; executionCallback(editor); diff --git a/packages-content-model/roosterjs-content-model-editor/test/publicApi/selection/getSelectedSegmentsTest.ts b/packages-content-model/roosterjs-content-model-editor/test/publicApi/selection/getSelectedSegmentsTest.ts index b29fbda0d93..e091fbf6812 100644 --- a/packages-content-model/roosterjs-content-model-editor/test/publicApi/selection/getSelectedSegmentsTest.ts +++ b/packages-content-model/roosterjs-content-model-editor/test/publicApi/selection/getSelectedSegmentsTest.ts @@ -157,7 +157,7 @@ describe('getSelectedSegments', () => { }); it('Include editable entity, but filter out readonly entity', () => { - const e1 = createEntity(null!, true); + const e1 = createEntity(null!); const e2 = createEntity(null!, false); const p1 = createParagraph(); diff --git a/packages-content-model/roosterjs-content-model-editor/test/publicApi/selection/hasSelectionInBlockTest.ts b/packages-content-model/roosterjs-content-model-editor/test/publicApi/selection/hasSelectionInBlockTest.ts index 80e309e7939..788e9e3efea 100644 --- a/packages-content-model/roosterjs-content-model-editor/test/publicApi/selection/hasSelectionInBlockTest.ts +++ b/packages-content-model/roosterjs-content-model-editor/test/publicApi/selection/hasSelectionInBlockTest.ts @@ -250,10 +250,12 @@ describe('hasSelectionInBlock', () => { segmentType: 'Entity', format: {}, isSelected: true, - type: 'entity', - id: 'entity', + entityFormat: { + entityType: 'entity', + id: 'entity', + isReadonly: false, + }, wrapper: null!, - isReadonly: false, }; const result = hasSelectionInBlock(block); diff --git a/packages-content-model/roosterjs-content-model-editor/test/publicApi/table/setTableCellShadeTest.ts b/packages-content-model/roosterjs-content-model-editor/test/publicApi/table/setTableCellShadeTest.ts index 422b6778015..d4df63630a1 100644 --- a/packages-content-model/roosterjs-content-model-editor/test/publicApi/table/setTableCellShadeTest.ts +++ b/packages-content-model/roosterjs-content-model-editor/test/publicApi/table/setTableCellShadeTest.ts @@ -9,11 +9,13 @@ describe('setTableCellShade', () => { let setContentModel: jasmine.Spy; let createContentModel: jasmine.Spy; let triggerPluginEvent: jasmine.Spy; + let getVisibleViewport: jasmine.Spy; beforeEach(() => { setContentModel = jasmine.createSpy('setContentModel'); createContentModel = jasmine.createSpy('createContentModel'); triggerPluginEvent = jasmine.createSpy('triggerPluginEvent'); + getVisibleViewport = jasmine.createSpy('getVisibleViewport'); spyOn(normalizeTable, 'normalizeTable'); @@ -24,6 +26,7 @@ describe('setTableCellShade', () => { createContentModel, isDarkMode: () => false, triggerPluginEvent, + getVisibleViewport, } as any) as IContentModelEditor; }); diff --git a/packages-content-model/roosterjs-content-model-editor/test/publicApi/utils/formatImageWithContentModelTest.ts b/packages-content-model/roosterjs-content-model-editor/test/publicApi/utils/formatImageWithContentModelTest.ts index 734b87fd67e..c27f03bb44e 100644 --- a/packages-content-model/roosterjs-content-model-editor/test/publicApi/utils/formatImageWithContentModelTest.ts +++ b/packages-content-model/roosterjs-content-model-editor/test/publicApi/utils/formatImageWithContentModelTest.ts @@ -20,8 +20,9 @@ describe('formatImageWithContentModel', () => { ) { segmentTestForPluginEvent( 'apiTest', - editor => - formatImageWithContentModel(editor, 'apiTest', callback, shouldCallPluginEvent), + editor => { + formatImageWithContentModel(editor, 'apiTest', callback, shouldCallPluginEvent); + }, model, result, shouldCallPluginEvent, @@ -214,6 +215,7 @@ function segmentTestForPluginEvent( callback(); }); const triggerPluginEvent = jasmine.createSpy().and.callFake(() => {}); + const getVisibleViewport = jasmine.createSpy().and.callFake(() => {}); const setContentModel = jasmine.createSpy().and.callFake((model: ContentModelDocument) => { expect(model).toEqual(result); }); @@ -226,6 +228,7 @@ function segmentTestForPluginEvent( getFocusedPosition: () => null as NodePosition, triggerPluginEvent, isDarkMode: () => false, + getVisibleViewport, } as any) as IContentModelEditor; executionCallback(editor); diff --git a/packages-content-model/roosterjs-content-model-editor/test/publicApi/utils/formatParagraphWithContentModelTest.ts b/packages-content-model/roosterjs-content-model-editor/test/publicApi/utils/formatParagraphWithContentModelTest.ts index 64e5bfe0f64..38760f3ae5e 100644 --- a/packages-content-model/roosterjs-content-model-editor/test/publicApi/utils/formatParagraphWithContentModelTest.ts +++ b/packages-content-model/roosterjs-content-model-editor/test/publicApi/utils/formatParagraphWithContentModelTest.ts @@ -14,14 +14,19 @@ describe('formatParagraphWithContentModel', () => { let setContentModel: jasmine.Spy; let triggerPluginEvent: jasmine.Spy; let focus: jasmine.Spy; + let getVisibleViewport: jasmine.Spy; let model: ContentModelDocument; + const mockedContainer = 'C' as any; + const mockedOffset = 'O' as any; + const apiName = 'mockedApi'; beforeEach(() => { addUndoSnapshot = jasmine.createSpy('addUndoSnapshot').and.callFake(callback => callback()); setContentModel = jasmine.createSpy('setContentModel'); triggerPluginEvent = jasmine.createSpy('triggerPluginEvent'); + getVisibleViewport = jasmine.createSpy('getVisibleViewport'); focus = jasmine.createSpy('focus'); editor = ({ @@ -31,8 +36,9 @@ describe('formatParagraphWithContentModel', () => { setContentModel, isDarkMode: () => false, getCustomData: () => ({}), - getFocusedPosition: () => 'NewPosition', + getFocusedPosition: () => ({ node: mockedContainer, offset: mockedOffset }), triggerPluginEvent, + getVisibleViewport, } as any) as IContentModelEditor; }); @@ -98,16 +104,19 @@ describe('formatParagraphWithContentModel', () => { model.blocks.push(para); let cachedPendingFormat: any = 'PendingFormat'; - let cachedPendingPos: any = 'PendingPos'; + let cachedPendingContainer: any = 'PendingContainer'; + let cachedPendingOffset: any = 'PendingOffset'; spyOn(pendingFormat, 'getPendingFormat').and.returnValue(cachedPendingFormat); - spyOn(pendingFormat, 'setPendingFormat').and.callFake((_, format, pos) => { + spyOn(pendingFormat, 'setPendingFormat').and.callFake((_, format, container, offset) => { cachedPendingFormat = format; - cachedPendingPos = pos; + cachedPendingContainer = container; + cachedPendingOffset = offset; }); spyOn(pendingFormat, 'clearPendingFormat').and.callFake(() => { cachedPendingFormat = null; - cachedPendingPos = null; + cachedPendingContainer = null; + cachedPendingOffset = null; }); formatParagraphWithContentModel( @@ -117,6 +126,7 @@ describe('formatParagraphWithContentModel', () => { ); expect(cachedPendingFormat).toEqual('PendingFormat'); - expect(cachedPendingPos).toEqual('NewPosition'); + expect(cachedPendingContainer).toEqual(mockedContainer); + expect(cachedPendingOffset).toEqual(mockedOffset); }); }); diff --git a/packages-content-model/roosterjs-content-model-editor/test/publicApi/utils/formatSegmentWithContentModelTest.ts b/packages-content-model/roosterjs-content-model-editor/test/publicApi/utils/formatSegmentWithContentModelTest.ts index 65e2906d7e5..c61a6067f43 100644 --- a/packages-content-model/roosterjs-content-model-editor/test/publicApi/utils/formatSegmentWithContentModelTest.ts +++ b/packages-content-model/roosterjs-content-model-editor/test/publicApi/utils/formatSegmentWithContentModelTest.ts @@ -19,6 +19,7 @@ describe('formatSegmentWithContentModel', () => { let getPendingFormat: jasmine.Spy; let setPendingFormat: jasmine.Spy; let triggerPluginEvent: jasmine.Spy; + let getVisibleViewport: jasmine.Spy; const apiName = 'mockedApi'; @@ -26,6 +27,7 @@ describe('formatSegmentWithContentModel', () => { addUndoSnapshot = jasmine.createSpy('addUndoSnapshot').and.callFake(callback => callback()); setContentModel = jasmine.createSpy('setContentModel'); triggerPluginEvent = jasmine.createSpy('triggerPluginEvent'); + getVisibleViewport = jasmine.createSpy('getVisibleViewport'); focus = jasmine.createSpy('focus'); setPendingFormat = spyOn(pendingFormat, 'setPendingFormat'); @@ -39,6 +41,7 @@ describe('formatSegmentWithContentModel', () => { getFocusedPosition: () => null as NodePosition, isDarkMode: () => false, triggerPluginEvent, + getVisibleViewport, } as any) as IContentModelEditor; }); @@ -224,9 +227,10 @@ describe('formatSegmentWithContentModel', () => { para.segments.push(marker); model.blocks.push(para); - const mockedPosition = ('Position' as any) as NodePosition; + const mockedContainer = 'C' as any; + const mockedOffset = 'O' as any; - editor.getFocusedPosition = () => mockedPosition; + editor.getFocusedPosition = () => ({ node: mockedContainer, offset: mockedOffset } as any); formatSegmentWithContentModel(editor, apiName, format => (format.fontFamily = 'test')); expect(model).toEqual({ @@ -257,7 +261,8 @@ describe('formatSegmentWithContentModel', () => { fontSize: '10px', fontFamily: 'test', }, - mockedPosition + mockedContainer, + mockedOffset ); }); diff --git a/packages-content-model/roosterjs-content-model-editor/test/publicApi/utils/formatWithContentModelTest.ts b/packages-content-model/roosterjs-content-model-editor/test/publicApi/utils/formatWithContentModelTest.ts index ec82c1f75b7..0820bb2e5c6 100644 --- a/packages-content-model/roosterjs-content-model-editor/test/publicApi/utils/formatWithContentModelTest.ts +++ b/packages-content-model/roosterjs-content-model-editor/test/publicApi/utils/formatWithContentModelTest.ts @@ -1,6 +1,7 @@ import * as pendingFormat from '../../../lib/modelApi/format/pendingFormat'; import { ChangeSource, EntityOperation, PluginEventType } from 'roosterjs-editor-types'; import { ContentModelDocument } from 'roosterjs-content-model-types'; +import { createImage } from 'roosterjs-content-model-dom'; import { formatWithContentModel } from '../../../lib/publicApi/utils/formatWithContentModel'; import { IContentModelEditor } from '../../../lib/publicTypes/IContentModelEditor'; @@ -9,14 +10,15 @@ describe('formatWithContentModel', () => { let addUndoSnapshot: jasmine.Spy; let createContentModel: jasmine.Spy; let setContentModel: jasmine.Spy; - let focus: jasmine.Spy; let mockedModel: ContentModelDocument; let cacheContentModel: jasmine.Spy; let getFocusedPosition: jasmine.Spy; let triggerPluginEvent: jasmine.Spy; + let getVisibleViewport: jasmine.Spy; const apiName = 'mockedApi'; - const mockedPos = 'POS' as any; + const mockedContainer = 'C' as any; + const mockedOffset = 'O' as any; beforeEach(() => { mockedModel = ({} as any) as ContentModelDocument; @@ -24,19 +26,21 @@ describe('formatWithContentModel', () => { addUndoSnapshot = jasmine.createSpy('addUndoSnapshot').and.callFake(callback => callback()); createContentModel = jasmine.createSpy('createContentModel').and.returnValue(mockedModel); setContentModel = jasmine.createSpy('setContentModel'); - focus = jasmine.createSpy('focus'); cacheContentModel = jasmine.createSpy('cacheContentModel'); - getFocusedPosition = jasmine.createSpy('getFocusedPosition').and.returnValue(mockedPos); + getFocusedPosition = jasmine + .createSpy('getFocusedPosition') + .and.returnValue({ node: mockedContainer, offset: mockedOffset }); triggerPluginEvent = jasmine.createSpy('triggerPluginEvent'); + getVisibleViewport = jasmine.createSpy('getVisibleViewport'); editor = ({ - focus, addUndoSnapshot, createContentModel, setContentModel, cacheContentModel, getFocusedPosition, triggerPluginEvent, + getVisibleViewport, isDarkMode: () => false, } as any) as IContentModelEditor; }); @@ -50,11 +54,11 @@ describe('formatWithContentModel', () => { newEntities: [], deletedEntities: [], rawEvent: undefined, + newImages: [], }); expect(createContentModel).toHaveBeenCalledTimes(1); expect(addUndoSnapshot).not.toHaveBeenCalled(); expect(setContentModel).not.toHaveBeenCalled(); - expect(focus).toHaveBeenCalled(); }); it('Callback return true', () => { @@ -66,6 +70,7 @@ describe('formatWithContentModel', () => { newEntities: [], deletedEntities: [], rawEvent: undefined, + newImages: [], }); expect(createContentModel).toHaveBeenCalledTimes(1); expect(addUndoSnapshot).toHaveBeenCalledTimes(1); @@ -76,7 +81,6 @@ describe('formatWithContentModel', () => { }); expect(setContentModel).toHaveBeenCalledTimes(1); expect(setContentModel).toHaveBeenCalledWith(mockedModel, undefined, undefined); - expect(focus).toHaveBeenCalledTimes(1); }); it('Preserve pending format', () => { @@ -94,6 +98,7 @@ describe('formatWithContentModel', () => { newEntities: [], deletedEntities: [], rawEvent: undefined, + newImages: [], }); expect(createContentModel).toHaveBeenCalledTimes(1); expect(addUndoSnapshot).toHaveBeenCalledTimes(1); @@ -106,7 +111,8 @@ describe('formatWithContentModel', () => { expect(pendingFormat.setPendingFormat).toHaveBeenCalledWith( editor, mockedFormat, - mockedPos + mockedContainer, + mockedOffset ); }); @@ -127,6 +133,7 @@ describe('formatWithContentModel', () => { deletedEntities: [], rawEvent: undefined, skipUndoSnapshot: true, + newImages: [], }); expect(createContentModel).toHaveBeenCalledTimes(1); expect(addUndoSnapshot).not.toHaveBeenCalled(); @@ -141,6 +148,7 @@ describe('formatWithContentModel', () => { newEntities: [], deletedEntities: [], rawEvent: undefined, + newImages: [], }); expect(createContentModel).toHaveBeenCalledTimes(1); expect(addUndoSnapshot).toHaveBeenCalled(); @@ -163,6 +171,7 @@ describe('formatWithContentModel', () => { deletedEntities: [], rawEvent: undefined, skipUndoSnapshot: true, + newImages: [], }); expect(createContentModel).toHaveBeenCalledTimes(1); expect(addUndoSnapshot).not.toHaveBeenCalled(); @@ -178,6 +187,7 @@ describe('formatWithContentModel', () => { newEntities: [], deletedEntities: [], rawEvent: undefined, + newImages: [], }); expect(createContentModel).toHaveBeenCalledTimes(1); expect(addUndoSnapshot).toHaveBeenCalled(); @@ -195,6 +205,7 @@ describe('formatWithContentModel', () => { newEntities: [], deletedEntities: [], rawEvent: undefined, + newImages: [], }); expect(createContentModel).toHaveBeenCalledTimes(1); expect(setContentModel).toHaveBeenCalledWith(mockedModel, undefined, undefined); @@ -203,8 +214,14 @@ describe('formatWithContentModel', () => { }); it('Has entity got deleted', () => { - const entity1 = { id: 'E1', type: 'E', wrapper: {}, isReadonly: true } as any; - const entity2 = { id: 'E2', type: 'E', wrapper: {}, isReadonly: true } as any; + const entity1 = { + entityFormat: { id: 'E1', entityType: 'E', isReadonly: true }, + wrapper: {}, + } as any; + const entity2 = { + entityFormat: { id: 'E2', entityType: 'E', isReadonly: true }, + wrapper: {}, + } as any; const rawEvent = 'RawEvent' as any; formatWithContentModel( @@ -230,12 +247,12 @@ describe('formatWithContentModel', () => { expect(triggerPluginEvent).toHaveBeenCalledTimes(3); expect(triggerPluginEvent).toHaveBeenCalledWith(PluginEventType.EntityOperation, { - entity: entity1, + entity: { id: 'E1', type: 'E', isReadonly: true, wrapper: entity1.wrapper }, operation: EntityOperation.RemoveFromStart, rawEvent: rawEvent, }); expect(triggerPluginEvent).toHaveBeenCalledWith(PluginEventType.EntityOperation, { - entity: entity2, + entity: { id: 'E2', type: 'E', isReadonly: true, wrapper: entity2.wrapper }, operation: EntityOperation.RemoveFromEnd, rawEvent: rawEvent, }); @@ -244,8 +261,14 @@ describe('formatWithContentModel', () => { it('Has new entity in dark mode', () => { const wrapper1 = 'W1' as any; const wrapper2 = 'W2' as any; - const entity1 = { id: 'E1', type: 'E', wrapper: wrapper1, isReadonly: true } as any; - const entity2 = { id: 'E2', type: 'E', wrapper: wrapper2, isReadonly: true } as any; + const entity1 = { + entityFormat: { id: 'E1', entityType: 'E', isReadonly: true }, + wrapper: wrapper1, + } as any; + const entity2 = { + entityFormat: { id: 'E2', entityType: 'E', isReadonly: true }, + wrapper: wrapper2, + } as any; const rawEvent = 'RawEvent' as any; const transformToDarkColorSpy = jasmine.createSpy('transformToDarkColor'); const mockedData = 'DATA'; @@ -269,7 +292,7 @@ describe('formatWithContentModel', () => { expect(triggerPluginEvent).toHaveBeenCalledTimes(1); expect(triggerPluginEvent).toHaveBeenCalledWith(PluginEventType.ContentChanged, { contentModel: mockedModel, - rangeEx: undefined, + selection: undefined, source: ChangeSource.Format, data: mockedData, additionalData: { @@ -290,4 +313,29 @@ describe('formatWithContentModel', () => { expect(createContentModel).toHaveBeenCalledWith(undefined, range); }); + + it('Has image', () => { + const image = createImage('test'); + const rawEvent = 'RawEvent' as any; + const getVisibleViewportSpy = jasmine + .createSpy('getVisibleViewport') + .and.returnValue({ top: 100, bottom: 200, left: 100, right: 200 }); + const mockedData = 'DATA'; + editor.getVisibleViewport = getVisibleViewportSpy; + + formatWithContentModel( + editor, + apiName, + (model, context) => { + context.newImages.push(image); + return true; + }, + { + rawEvent: rawEvent, + getChangeData: () => mockedData, + } + ); + + expect(getVisibleViewportSpy).toHaveBeenCalledTimes(1); + }); }); diff --git a/packages-content-model/roosterjs-content-model-editor/test/publicApi/utils/pasteTest.ts b/packages-content-model/roosterjs-content-model-editor/test/publicApi/utils/pasteTest.ts index f8693667102..932cfeed80e 100644 --- a/packages-content-model/roosterjs-content-model-editor/test/publicApi/utils/pasteTest.ts +++ b/packages-content-model/roosterjs-content-model-editor/test/publicApi/utils/pasteTest.ts @@ -43,6 +43,7 @@ describe('Paste ', () => { let getDocument: jasmine.Spy; let getTrustedHTMLHandler: jasmine.Spy; let triggerPluginEvent: jasmine.Spy; + let getVisibleViewport: jasmine.Spy; const mockedPos = 'POS' as any; @@ -95,6 +96,8 @@ describe('Paste ', () => { getTrustedHTMLHandler = jasmine .createSpy('getTrustedHTMLHandler') .and.returnValue((html: string) => html); + + getVisibleViewport = jasmine.createSpy('getVisibleViewport'); spyOn(mergeModelFile, 'mergeModel').and.callFake(() => (mockedModel = mockedMergeModel)); spyOn(getSelectedSegmentsF, 'default').and.returnValue([ { @@ -116,6 +119,7 @@ describe('Paste ', () => { getDocument, getTrustedHTMLHandler, triggerPluginEvent, + getVisibleViewport, isDarkMode: () => false, } as any) as IContentModelEditor; }); @@ -150,7 +154,7 @@ describe('Paste ', () => { expect(triggerPluginEvent).toHaveBeenCalledTimes(1); expect(triggerPluginEvent).toHaveBeenCalledWith(PluginEventType.ContentChanged, { contentModel: mockedModel, - rangeEx: undefined, + selection: undefined, data: clipboardData, source: ChangeSource.Paste, additionalData: { @@ -220,7 +224,7 @@ describe('paste with content model & paste plugin', () => { pasteF.default(editor!, clipboardData); - expect(setProcessorF.setProcessor).toHaveBeenCalledTimes(0); + expect(setProcessorF.setProcessor).toHaveBeenCalledTimes(1); expect(addParserF.default).toHaveBeenCalledTimes(DEFAULT_TIMES_ADD_PARSER_CALLED + 1); expect(ExcelF.processPastedContentFromExcel).toHaveBeenCalledTimes(1); }); @@ -231,7 +235,7 @@ describe('paste with content model & paste plugin', () => { pasteF.default(editor!, clipboardData); - expect(setProcessorF.setProcessor).toHaveBeenCalledTimes(0); + expect(setProcessorF.setProcessor).toHaveBeenCalledTimes(1); expect(addParserF.default).toHaveBeenCalledTimes(DEFAULT_TIMES_ADD_PARSER_CALLED + 1); expect(ExcelF.processPastedContentFromExcel).toHaveBeenCalledTimes(1); }); @@ -444,7 +448,7 @@ describe('mergePasteContent', () => { pasteF.mergePasteContent( sourceModel, - { newEntities: [], deletedEntities: [] }, + { newEntities: [], deletedEntities: [], newImages: [] }, pasteModel, false /* applyCurrentFormat */, undefined /* customizedMerge */ @@ -453,7 +457,7 @@ describe('mergePasteContent', () => { expect(mergeModelFile.mergeModel).toHaveBeenCalledWith( sourceModel, pasteModel, - { newEntities: [], deletedEntities: [] }, + { newEntities: [], deletedEntities: [], newImages: [] }, { mergeFormat: 'none', mergeTable: true, @@ -532,7 +536,7 @@ describe('mergePasteContent', () => { pasteF.mergePasteContent( sourceModel, - { newEntities: [], deletedEntities: [] }, + { newEntities: [], deletedEntities: [], newImages: [] }, pasteModel, false /* applyCurrentFormat */, customizedMerge /* customizedMerge */ @@ -550,7 +554,7 @@ describe('mergePasteContent', () => { pasteF.mergePasteContent( sourceModel, - { newEntities: [], deletedEntities: [] }, + { newEntities: [], deletedEntities: [], newImages: [] }, pasteModel, true /* applyCurrentFormat */, undefined /* customizedMerge */ @@ -559,7 +563,7 @@ describe('mergePasteContent', () => { expect(mergeModelFile.mergeModel).toHaveBeenCalledWith( sourceModel, pasteModel, - { newEntities: [], deletedEntities: [] }, + { newEntities: [], deletedEntities: [], newImages: [] }, { mergeFormat: 'keepSourceEmphasisFormat', mergeTable: false, diff --git a/packages-content-model/roosterjs-content-model-types/lib/block/ContentModelBlock.ts b/packages-content-model/roosterjs-content-model-types/lib/block/ContentModelBlock.ts index d822562dca5..0224a7c15c3 100644 --- a/packages-content-model/roosterjs-content-model-types/lib/block/ContentModelBlock.ts +++ b/packages-content-model/roosterjs-content-model-types/lib/block/ContentModelBlock.ts @@ -1,10 +1,10 @@ -import { ContentModelDivider } from './ContentModelDivider'; -import { ContentModelEntity } from '../entity/ContentModelEntity'; -import { ContentModelFormatContainer } from '../group/ContentModelFormatContainer'; -import { ContentModelGeneralBlock } from '../group/ContentModelGeneralBlock'; -import { ContentModelListItem } from '../group/ContentModelListItem'; -import { ContentModelParagraph } from './ContentModelParagraph'; -import { ContentModelTable } from './ContentModelTable'; +import type { ContentModelDivider } from './ContentModelDivider'; +import type { ContentModelEntity } from '../entity/ContentModelEntity'; +import type { ContentModelFormatContainer } from '../group/ContentModelFormatContainer'; +import type { ContentModelGeneralBlock } from '../group/ContentModelGeneralBlock'; +import type { ContentModelListItem } from '../group/ContentModelListItem'; +import type { ContentModelParagraph } from './ContentModelParagraph'; +import type { ContentModelTable } from './ContentModelTable'; /** * A union type of Content Model Block diff --git a/packages-content-model/roosterjs-content-model-types/lib/block/ContentModelBlockBase.ts b/packages-content-model/roosterjs-content-model-types/lib/block/ContentModelBlockBase.ts index 786e603687e..352f8f06353 100644 --- a/packages-content-model/roosterjs-content-model-types/lib/block/ContentModelBlockBase.ts +++ b/packages-content-model/roosterjs-content-model-types/lib/block/ContentModelBlockBase.ts @@ -1,6 +1,6 @@ -import { ContentModelBlockFormat } from '../format/ContentModelBlockFormat'; -import { ContentModelBlockType } from '../enum/BlockType'; -import { ContentModelWithFormat } from '../format/ContentModelWithFormat'; +import type { ContentModelBlockFormat } from '../format/ContentModelBlockFormat'; +import type { ContentModelBlockType } from '../enum/BlockType'; +import type { ContentModelWithFormat } from '../format/ContentModelWithFormat'; /** * Base type of a block diff --git a/packages-content-model/roosterjs-content-model-types/lib/block/ContentModelDivider.ts b/packages-content-model/roosterjs-content-model-types/lib/block/ContentModelDivider.ts index ade19655844..ba7742b3d7f 100644 --- a/packages-content-model/roosterjs-content-model-types/lib/block/ContentModelDivider.ts +++ b/packages-content-model/roosterjs-content-model-types/lib/block/ContentModelDivider.ts @@ -1,7 +1,7 @@ -import { ContentModelBlockBase } from './ContentModelBlockBase'; -import { ContentModelBlockWithCache } from './ContentModelBlockWithCache'; -import { ContentModelDividerFormat } from '../format/ContentModelDividerFormat'; -import { Selectable } from '../selection/Selectable'; +import type { ContentModelBlockBase } from './ContentModelBlockBase'; +import type { ContentModelBlockWithCache } from './ContentModelBlockWithCache'; +import type { ContentModelDividerFormat } from '../format/ContentModelDividerFormat'; +import type { Selectable } from '../selection/Selectable'; /** * Content Model of horizontal divider diff --git a/packages-content-model/roosterjs-content-model-types/lib/block/ContentModelParagraph.ts b/packages-content-model/roosterjs-content-model-types/lib/block/ContentModelParagraph.ts index 8eb809ee954..042dbcb140c 100644 --- a/packages-content-model/roosterjs-content-model-types/lib/block/ContentModelParagraph.ts +++ b/packages-content-model/roosterjs-content-model-types/lib/block/ContentModelParagraph.ts @@ -1,8 +1,8 @@ -import { ContentModelBlockBase } from './ContentModelBlockBase'; -import { ContentModelBlockWithCache } from './ContentModelBlockWithCache'; -import { ContentModelParagraphDecorator } from '../decorator/ContentModelParagraphDecorator'; -import { ContentModelSegment } from '../segment/ContentModelSegment'; -import { ContentModelSegmentFormat } from '../format/ContentModelSegmentFormat'; +import type { ContentModelBlockBase } from './ContentModelBlockBase'; +import type { ContentModelBlockWithCache } from './ContentModelBlockWithCache'; +import type { ContentModelParagraphDecorator } from '../decorator/ContentModelParagraphDecorator'; +import type { ContentModelSegment } from '../segment/ContentModelSegment'; +import type { ContentModelSegmentFormat } from '../format/ContentModelSegmentFormat'; /** * Content Model of Paragraph diff --git a/packages-content-model/roosterjs-content-model-types/lib/block/ContentModelTable.ts b/packages-content-model/roosterjs-content-model-types/lib/block/ContentModelTable.ts index d7ee0cbe56c..568f6f7de48 100644 --- a/packages-content-model/roosterjs-content-model-types/lib/block/ContentModelTable.ts +++ b/packages-content-model/roosterjs-content-model-types/lib/block/ContentModelTable.ts @@ -1,9 +1,9 @@ -import { ContentModelBlockBase } from './ContentModelBlockBase'; -import { ContentModelBlockWithCache } from './ContentModelBlockWithCache'; -import { ContentModelTableFormat } from '../format/ContentModelTableFormat'; -import { ContentModelTableRow } from './ContentModelTableRow'; -import { ContentModelWithDataset } from '../format/ContentModelWithDataset'; -import { TableMetadataFormat } from '../format/metadata/TableMetadataFormat'; +import type { ContentModelBlockBase } from './ContentModelBlockBase'; +import type { ContentModelBlockWithCache } from './ContentModelBlockWithCache'; +import type { ContentModelTableFormat } from '../format/ContentModelTableFormat'; +import type { ContentModelTableRow } from './ContentModelTableRow'; +import type { ContentModelWithDataset } from '../format/ContentModelWithDataset'; +import type { TableMetadataFormat } from '../format/metadata/TableMetadataFormat'; /** * Content Model of Table diff --git a/packages-content-model/roosterjs-content-model-types/lib/block/ContentModelTableRow.ts b/packages-content-model/roosterjs-content-model-types/lib/block/ContentModelTableRow.ts index 463e0061c7a..e9dc6e4f188 100644 --- a/packages-content-model/roosterjs-content-model-types/lib/block/ContentModelTableRow.ts +++ b/packages-content-model/roosterjs-content-model-types/lib/block/ContentModelTableRow.ts @@ -1,7 +1,7 @@ -import { ContentModelBlockFormat } from '../format/ContentModelBlockFormat'; -import { ContentModelBlockWithCache } from './ContentModelBlockWithCache'; -import { ContentModelTableCell } from '../group/ContentModelTableCell'; -import { ContentModelWithFormat } from '../format/ContentModelWithFormat'; +import type { ContentModelBlockFormat } from '../format/ContentModelBlockFormat'; +import type { ContentModelBlockWithCache } from './ContentModelBlockWithCache'; +import type { ContentModelTableCell } from '../group/ContentModelTableCell'; +import type { ContentModelWithFormat } from '../format/ContentModelWithFormat'; /** * Content Model of Table diff --git a/packages-content-model/roosterjs-content-model-types/lib/context/ContentModelDomIndexer.ts b/packages-content-model/roosterjs-content-model-types/lib/context/ContentModelDomIndexer.ts new file mode 100644 index 00000000000..c4383e2d3ac --- /dev/null +++ b/packages-content-model/roosterjs-content-model-types/lib/context/ContentModelDomIndexer.ts @@ -0,0 +1,49 @@ +import type { ContentModelDocument } from '../group/ContentModelDocument'; +import type { ContentModelParagraph } from '../block/ContentModelParagraph'; +import type { ContentModelSegment } from '../segment/ContentModelSegment'; +import type { ContentModelTable } from '../block/ContentModelTable'; +import type { DOMSelection } from '../selection/DOMSelection'; + +/** + * Represents an indexer object which provides methods to help build backward relationship + * from DOM node to Content Model + */ +export interface ContentModelDomIndexer { + /** + * Invoked when processing a segment + * @param segmentNode The new DOM node for this segment + * @param paragraph Parent paragraph of this segment + * @param segments The source segments + */ + onSegment: ( + segmentNode: Node, + paragraph: ContentModelParagraph, + segments: ContentModelSegment[] + ) => void; + + /** + * Invoked when new paragraph node is created in DOM tree + * @param paragraphElement The new DOM node for this paragraph + */ + onParagraph: (paragraphElement: HTMLElement) => void; + + /** + * Invoked when new table node is created in DOM tree + * @param tableElement The new DOM node for this table + */ + onTable: (tableElement: HTMLTableElement, tableModel: ContentModelTable) => void; + + /** + * When document content or selection is changed by user, we need to use this function to update the content model + * to reflect the latest document. This process can fail since the selected node may not have a related model data structure. + * @param model Current cached content model + * @param newSelection Latest selection + * @param oldSelection @optional Original selection before this change + * @returns True if reconcile successfully, otherwise false + */ + reconcileSelection: ( + model: ContentModelDocument, + newSelection: DOMSelection, + oldSelection?: DOMSelection + ) => boolean; +} diff --git a/packages-content-model/roosterjs-content-model-types/lib/context/ContentModelHandler.ts b/packages-content-model/roosterjs-content-model-types/lib/context/ContentModelHandler.ts index ad68a9422cf..d50ad80e3f8 100644 --- a/packages-content-model/roosterjs-content-model-types/lib/context/ContentModelHandler.ts +++ b/packages-content-model/roosterjs-content-model-types/lib/context/ContentModelHandler.ts @@ -1,8 +1,8 @@ -import { ContentModelBlock } from '../block/ContentModelBlock'; -import { ContentModelBlockGroup } from '../group/ContentModelBlockGroup'; -import { ContentModelDecorator } from '../decorator/ContentModelDecorator'; -import { ContentModelSegment } from '../segment/ContentModelSegment'; -import { ModelToDomContext } from './ModelToDomContext'; +import type { ContentModelBlock } from '../block/ContentModelBlock'; +import type { ContentModelBlockGroup } from '../group/ContentModelBlockGroup'; +import type { ContentModelDecorator } from '../decorator/ContentModelDecorator'; +import type { ContentModelSegment } from '../segment/ContentModelSegment'; +import type { ModelToDomContext } from './ModelToDomContext'; /** * Type of Content Model to DOM handler diff --git a/packages-content-model/roosterjs-content-model-types/lib/context/DomToModelContext.ts b/packages-content-model/roosterjs-content-model-types/lib/context/DomToModelContext.ts index 487a8fb6800..2d8c6f8cb84 100644 --- a/packages-content-model/roosterjs-content-model-types/lib/context/DomToModelContext.ts +++ b/packages-content-model/roosterjs-content-model-types/lib/context/DomToModelContext.ts @@ -1,7 +1,10 @@ -import { DomToModelSelectionContext } from './DomToModelSelectionContext'; -import { DomToModelSettings } from './DomToModelSettings'; -import { EditorContext } from './EditorContext'; -import { DomToModelFormatContext, DomToModelDecoratorContext } from './DomToModelFormatContext'; +import type { DomToModelSelectionContext } from './DomToModelSelectionContext'; +import type { DomToModelSettings } from './DomToModelSettings'; +import type { EditorContext } from './EditorContext'; +import type { + DomToModelFormatContext, + DomToModelDecoratorContext, +} from './DomToModelFormatContext'; /** * Context of DOM to Model conversion, used for parse HTML element according to current context diff --git a/packages-content-model/roosterjs-content-model-types/lib/context/DomToModelFormatContext.ts b/packages-content-model/roosterjs-content-model-types/lib/context/DomToModelFormatContext.ts index 303965d7a31..34e233e2d4e 100644 --- a/packages-content-model/roosterjs-content-model-types/lib/context/DomToModelFormatContext.ts +++ b/packages-content-model/roosterjs-content-model-types/lib/context/DomToModelFormatContext.ts @@ -1,10 +1,10 @@ -import { ContentModelBlockFormat } from '../format/ContentModelBlockFormat'; -import { ContentModelBlockGroup } from '../group/ContentModelBlockGroup'; -import { ContentModelCode } from '../decorator/ContentModelCode'; -import { ContentModelLink } from '../decorator/ContentModelLink'; -import { ContentModelListLevel } from '../decorator/ContentModelListLevel'; -import { ContentModelParagraphDecorator } from '../decorator/ContentModelParagraphDecorator'; -import { ContentModelSegmentFormat } from '../format/ContentModelSegmentFormat'; +import type { ContentModelBlockFormat } from '../format/ContentModelBlockFormat'; +import type { ContentModelBlockGroup } from '../group/ContentModelBlockGroup'; +import type { ContentModelCode } from '../decorator/ContentModelCode'; +import type { ContentModelLink } from '../decorator/ContentModelLink'; +import type { ContentModelListLevel } from '../decorator/ContentModelListLevel'; +import type { ContentModelParagraphDecorator } from '../decorator/ContentModelParagraphDecorator'; +import type { ContentModelSegmentFormat } from '../format/ContentModelSegmentFormat'; /** * Represents the context object used when do DOM to Content Model conversion and processing a List diff --git a/packages-content-model/roosterjs-content-model-types/lib/context/DomToModelOption.ts b/packages-content-model/roosterjs-content-model-types/lib/context/DomToModelOption.ts index 9a358ca0b9f..e381cc5b596 100644 --- a/packages-content-model/roosterjs-content-model-types/lib/context/DomToModelOption.ts +++ b/packages-content-model/roosterjs-content-model-types/lib/context/DomToModelOption.ts @@ -1,4 +1,8 @@ -import { ElementProcessorMap, FormatParsers, FormatParsersPerCategory } from './DomToModelSettings'; +import type { + ElementProcessorMap, + FormatParsers, + FormatParsersPerCategory, +} from './DomToModelSettings'; /** * Options for creating DomToModelContext diff --git a/packages-content-model/roosterjs-content-model-types/lib/context/DomToModelSelectionContext.ts b/packages-content-model/roosterjs-content-model-types/lib/context/DomToModelSelectionContext.ts index dd30c8991ff..e0193371f8c 100644 --- a/packages-content-model/roosterjs-content-model-types/lib/context/DomToModelSelectionContext.ts +++ b/packages-content-model/roosterjs-content-model-types/lib/context/DomToModelSelectionContext.ts @@ -1,4 +1,4 @@ -import { SelectionRangeEx } from 'roosterjs-editor-types'; +import type { DOMSelection } from '../selection/DOMSelection'; /** * Represents the selection information of content used by DOM to Content Model conversion @@ -12,5 +12,5 @@ export interface DomToModelSelectionContext { /** * Current selection range */ - rangeEx?: SelectionRangeEx; + selection?: DOMSelection; } diff --git a/packages-content-model/roosterjs-content-model-types/lib/context/DomToModelSettings.ts b/packages-content-model/roosterjs-content-model-types/lib/context/DomToModelSettings.ts index d89fb59478e..8161c8833de 100644 --- a/packages-content-model/roosterjs-content-model-types/lib/context/DomToModelSettings.ts +++ b/packages-content-model/roosterjs-content-model-types/lib/context/DomToModelSettings.ts @@ -1,8 +1,8 @@ -import { ContentModelFormatBase } from '../format/ContentModelFormatBase'; -import { ContentModelFormatMap } from '../format/ContentModelFormatMap'; -import { DomToModelContext } from './DomToModelContext'; -import { ElementProcessor } from './ElementProcessor'; -import { FormatHandlerTypeMap, FormatKey } from '../format/FormatHandlerTypeMap'; +import type { ContentModelFormatBase } from '../format/ContentModelFormatBase'; +import type { ContentModelFormatMap } from '../format/ContentModelFormatMap'; +import type { DomToModelContext } from './DomToModelContext'; +import type { ElementProcessor } from './ElementProcessor'; +import type { FormatHandlerTypeMap, FormatKey } from '../format/FormatHandlerTypeMap'; /** * A type of Default style map, from tag name string (in upper case) to a static style object diff --git a/packages-content-model/roosterjs-content-model-types/lib/context/EditorContext.ts b/packages-content-model/roosterjs-content-model-types/lib/context/EditorContext.ts index 09999c9b121..eb84ec5e4aa 100644 --- a/packages-content-model/roosterjs-content-model-types/lib/context/EditorContext.ts +++ b/packages-content-model/roosterjs-content-model-types/lib/context/EditorContext.ts @@ -1,5 +1,6 @@ -import { ContentModelSegmentFormat } from '../format/ContentModelSegmentFormat'; -import { DarkColorHandler } from 'roosterjs-editor-types'; +import type { ContentModelDomIndexer } from './ContentModelDomIndexer'; +import type { ContentModelSegmentFormat } from '../format/ContentModelSegmentFormat'; +import type { DarkColorHandler } from 'roosterjs-editor-types'; /** * An editor context interface used by ContentModel PAI @@ -40,4 +41,9 @@ export interface EditorContext { * When pass true, this cached element will be used to create DOM tree back when convert Content Model to DOM */ allowCacheElement?: boolean; + + /** + * @optional Indexer for content model, to help build backward relationship from DOM node to Content Model + */ + domIndexer?: ContentModelDomIndexer; } diff --git a/packages-content-model/roosterjs-content-model-types/lib/context/ElementProcessor.ts b/packages-content-model/roosterjs-content-model-types/lib/context/ElementProcessor.ts index b82f477a704..e3e009d58a6 100644 --- a/packages-content-model/roosterjs-content-model-types/lib/context/ElementProcessor.ts +++ b/packages-content-model/roosterjs-content-model-types/lib/context/ElementProcessor.ts @@ -1,5 +1,5 @@ -import { ContentModelBlockGroup } from '../group/ContentModelBlockGroup'; -import { DomToModelContext } from './DomToModelContext'; +import type { ContentModelBlockGroup } from '../group/ContentModelBlockGroup'; +import type { DomToModelContext } from './DomToModelContext'; /** * A function type to process HTML element when do DOM to Content Model conversion diff --git a/packages-content-model/roosterjs-content-model-types/lib/context/ModelToDomContext.ts b/packages-content-model/roosterjs-content-model-types/lib/context/ModelToDomContext.ts index 04d44dda87d..93293d56b97 100644 --- a/packages-content-model/roosterjs-content-model-types/lib/context/ModelToDomContext.ts +++ b/packages-content-model/roosterjs-content-model-types/lib/context/ModelToDomContext.ts @@ -1,7 +1,7 @@ -import { EditorContext } from './EditorContext'; -import { ModelToDomFormatContext } from './ModelToDomFormatContext'; -import { ModelToDomSelectionContext } from './ModelToDomSelectionContext'; -import { ModelToDomSettings } from './ModelToDomSettings'; +import type { EditorContext } from './EditorContext'; +import type { ModelToDomFormatContext } from './ModelToDomFormatContext'; +import type { ModelToDomSelectionContext } from './ModelToDomSelectionContext'; +import type { ModelToDomSettings } from './ModelToDomSettings'; /** * Context of Model to DOM conversion, used for generate HTML DOM tree according to current context diff --git a/packages-content-model/roosterjs-content-model-types/lib/context/ModelToDomFormatContext.ts b/packages-content-model/roosterjs-content-model-types/lib/context/ModelToDomFormatContext.ts index d66f7de3590..5a279de2684 100644 --- a/packages-content-model/roosterjs-content-model-types/lib/context/ModelToDomFormatContext.ts +++ b/packages-content-model/roosterjs-content-model-types/lib/context/ModelToDomFormatContext.ts @@ -1,6 +1,6 @@ -import { ContentModelBlockFormat } from '../format/ContentModelBlockFormat'; -import { ContentModelListLevel } from '../decorator/ContentModelListLevel'; -import { ContentModelSegmentFormat } from '../format/ContentModelSegmentFormat'; +import type { ContentModelBlockFormat } from '../format/ContentModelBlockFormat'; +import type { ContentModelListLevel } from '../decorator/ContentModelListLevel'; +import type { ContentModelSegmentFormat } from '../format/ContentModelSegmentFormat'; /** * Represents a list stack item used by Content Model to DOM conversion diff --git a/packages-content-model/roosterjs-content-model-types/lib/context/ModelToDomOption.ts b/packages-content-model/roosterjs-content-model-types/lib/context/ModelToDomOption.ts index 953ee74ea47..81ff7532f15 100644 --- a/packages-content-model/roosterjs-content-model-types/lib/context/ModelToDomOption.ts +++ b/packages-content-model/roosterjs-content-model-types/lib/context/ModelToDomOption.ts @@ -1,4 +1,4 @@ -import { +import type { ContentModelHandlerMap, FormatAppliers, FormatAppliersPerCategory, diff --git a/packages-content-model/roosterjs-content-model-types/lib/context/ModelToDomSelectionContext.ts b/packages-content-model/roosterjs-content-model-types/lib/context/ModelToDomSelectionContext.ts index 53590ea5b32..d1af5f375c3 100644 --- a/packages-content-model/roosterjs-content-model-types/lib/context/ModelToDomSelectionContext.ts +++ b/packages-content-model/roosterjs-content-model-types/lib/context/ModelToDomSelectionContext.ts @@ -1,4 +1,4 @@ -import { Coordinates } from 'roosterjs-editor-types'; +import type { ImageSelection, TableSelection } from '../selection/DOMSelection'; /** * Represents internal data structure for a selection position, combined by block and segment node @@ -36,36 +36,6 @@ export interface ModelToDomRegularSelection { current: ModelToDomBlockAndSegmentNode; } -/** - * Represents internal data structure for table selection - */ -export interface ModelToDomTableSelection { - /** - * Table where selection is located - */ - table: HTMLTableElement; - - /** - * Coordinate of first selected cell - */ - firstCell: Coordinates; - - /** - * Coordinate of last selected cell - */ - lastCell: Coordinates; -} - -/** - * Represents an image selection for Content Model to DOM conversion - */ -export interface ModelToDomImageSelection { - /** - * Selected image - */ - image: HTMLImageElement; -} - /** * Represents selection info used by Content Model to DOM conversion */ @@ -78,10 +48,10 @@ export interface ModelToDomSelectionContext { /** * Table selection info */ - tableSelection?: ModelToDomTableSelection; + tableSelection?: TableSelection; /** * Image selection info */ - imageSelection?: ModelToDomImageSelection; + imageSelection?: ImageSelection; } diff --git a/packages-content-model/roosterjs-content-model-types/lib/context/ModelToDomSettings.ts b/packages-content-model/roosterjs-content-model-types/lib/context/ModelToDomSettings.ts index 4802f068898..9603a0192aa 100644 --- a/packages-content-model/roosterjs-content-model-types/lib/context/ModelToDomSettings.ts +++ b/packages-content-model/roosterjs-content-model-types/lib/context/ModelToDomSettings.ts @@ -1,26 +1,26 @@ -import { ContentModelBlock } from '../block/ContentModelBlock'; -import { ContentModelBlockFormat } from '../format/ContentModelBlockFormat'; -import { ContentModelBlockGroup } from '../group/ContentModelBlockGroup'; -import { ContentModelBr } from '../segment/ContentModelBr'; -import { ContentModelDecorator } from '../decorator/ContentModelDecorator'; -import { ContentModelDivider } from '../block/ContentModelDivider'; -import { ContentModelEntity } from '../entity/ContentModelEntity'; -import { ContentModelFormatBase } from '../format/ContentModelFormatBase'; -import { ContentModelFormatContainer } from '../group/ContentModelFormatContainer'; -import { ContentModelFormatMap } from '../format/ContentModelFormatMap'; -import { ContentModelGeneralBlock } from '../group/ContentModelGeneralBlock'; -import { ContentModelGeneralSegment } from '../segment/ContentModelGeneralSegment'; -import { ContentModelImage } from '../segment/ContentModelImage'; -import { ContentModelListItem } from '../group/ContentModelListItem'; -import { ContentModelParagraph } from '../block/ContentModelParagraph'; -import { ContentModelSegment } from '../segment/ContentModelSegment'; -import { ContentModelSegmentFormat } from '../format/ContentModelSegmentFormat'; -import { ContentModelTable } from '../block/ContentModelTable'; -import { ContentModelTableRow } from '../block/ContentModelTableRow'; -import { ContentModelText } from '../segment/ContentModelText'; -import { FormatHandlerTypeMap, FormatKey } from '../format/FormatHandlerTypeMap'; -import { ModelToDomContext } from './ModelToDomContext'; -import { +import type { ContentModelBlock } from '../block/ContentModelBlock'; +import type { ContentModelBlockFormat } from '../format/ContentModelBlockFormat'; +import type { ContentModelBlockGroup } from '../group/ContentModelBlockGroup'; +import type { ContentModelBr } from '../segment/ContentModelBr'; +import type { ContentModelDecorator } from '../decorator/ContentModelDecorator'; +import type { ContentModelDivider } from '../block/ContentModelDivider'; +import type { ContentModelEntity } from '../entity/ContentModelEntity'; +import type { ContentModelFormatBase } from '../format/ContentModelFormatBase'; +import type { ContentModelFormatContainer } from '../group/ContentModelFormatContainer'; +import type { ContentModelFormatMap } from '../format/ContentModelFormatMap'; +import type { ContentModelGeneralBlock } from '../group/ContentModelGeneralBlock'; +import type { ContentModelGeneralSegment } from '../segment/ContentModelGeneralSegment'; +import type { ContentModelImage } from '../segment/ContentModelImage'; +import type { ContentModelListItem } from '../group/ContentModelListItem'; +import type { ContentModelParagraph } from '../block/ContentModelParagraph'; +import type { ContentModelSegment } from '../segment/ContentModelSegment'; +import type { ContentModelSegmentFormat } from '../format/ContentModelSegmentFormat'; +import type { ContentModelTable } from '../block/ContentModelTable'; +import type { ContentModelTableRow } from '../block/ContentModelTableRow'; +import type { ContentModelText } from '../segment/ContentModelText'; +import type { FormatHandlerTypeMap, FormatKey } from '../format/FormatHandlerTypeMap'; +import type { ModelToDomContext } from './ModelToDomContext'; +import type { ContentModelHandler, ContentModelBlockHandler, ContentModelSegmentHandler, diff --git a/packages-content-model/roosterjs-content-model-types/lib/decorator/ContentModelCode.ts b/packages-content-model/roosterjs-content-model-types/lib/decorator/ContentModelCode.ts index a95441abbae..b124d0e61b5 100644 --- a/packages-content-model/roosterjs-content-model-types/lib/decorator/ContentModelCode.ts +++ b/packages-content-model/roosterjs-content-model-types/lib/decorator/ContentModelCode.ts @@ -1,5 +1,5 @@ -import { ContentModelCodeFormat } from '../format/ContentModelCodeFormat'; -import { ContentModelWithFormat } from '../format/ContentModelWithFormat'; +import type { ContentModelCodeFormat } from '../format/ContentModelCodeFormat'; +import type { ContentModelWithFormat } from '../format/ContentModelWithFormat'; /** * Represent code info of Content Model. diff --git a/packages-content-model/roosterjs-content-model-types/lib/decorator/ContentModelDecorator.ts b/packages-content-model/roosterjs-content-model-types/lib/decorator/ContentModelDecorator.ts index 53886389b82..b8fe53b3f2d 100644 --- a/packages-content-model/roosterjs-content-model-types/lib/decorator/ContentModelDecorator.ts +++ b/packages-content-model/roosterjs-content-model-types/lib/decorator/ContentModelDecorator.ts @@ -1,6 +1,6 @@ -import { ContentModelCode } from './ContentModelCode'; -import { ContentModelLink } from './ContentModelLink'; -import { ContentModelListLevel } from './ContentModelListLevel'; +import type { ContentModelCode } from './ContentModelCode'; +import type { ContentModelLink } from './ContentModelLink'; +import type { ContentModelListLevel } from './ContentModelListLevel'; /** * Union type for segment decorators diff --git a/packages-content-model/roosterjs-content-model-types/lib/decorator/ContentModelLink.ts b/packages-content-model/roosterjs-content-model-types/lib/decorator/ContentModelLink.ts index ec6909e54a8..c0622fba7ef 100644 --- a/packages-content-model/roosterjs-content-model-types/lib/decorator/ContentModelLink.ts +++ b/packages-content-model/roosterjs-content-model-types/lib/decorator/ContentModelLink.ts @@ -1,6 +1,6 @@ -import { ContentModelHyperLinkFormat } from '../format/ContentModelHyperLinkFormat'; -import { ContentModelWithDataset } from '../format/ContentModelWithDataset'; -import { ContentModelWithFormat } from '../format/ContentModelWithFormat'; +import type { ContentModelHyperLinkFormat } from '../format/ContentModelHyperLinkFormat'; +import type { ContentModelWithDataset } from '../format/ContentModelWithDataset'; +import type { ContentModelWithFormat } from '../format/ContentModelWithFormat'; /** * Represent link info of Content Model. diff --git a/packages-content-model/roosterjs-content-model-types/lib/decorator/ContentModelListLevel.ts b/packages-content-model/roosterjs-content-model-types/lib/decorator/ContentModelListLevel.ts index ab68214da52..206d541701e 100644 --- a/packages-content-model/roosterjs-content-model-types/lib/decorator/ContentModelListLevel.ts +++ b/packages-content-model/roosterjs-content-model-types/lib/decorator/ContentModelListLevel.ts @@ -1,7 +1,7 @@ -import { ContentModelListItemLevelFormat } from '../format/ContentModelListItemLevelFormat'; -import { ContentModelWithDataset } from '../format/ContentModelWithDataset'; -import { ContentModelWithFormat } from '../format/ContentModelWithFormat'; -import { ListMetadataFormat } from '../format/metadata/ListMetadataFormat'; +import type { ContentModelListItemLevelFormat } from '../format/ContentModelListItemLevelFormat'; +import type { ContentModelWithDataset } from '../format/ContentModelWithDataset'; +import type { ContentModelWithFormat } from '../format/ContentModelWithFormat'; +import type { ListMetadataFormat } from '../format/metadata/ListMetadataFormat'; /** * Content Model of List Level diff --git a/packages-content-model/roosterjs-content-model-types/lib/decorator/ContentModelParagraphDecorator.ts b/packages-content-model/roosterjs-content-model-types/lib/decorator/ContentModelParagraphDecorator.ts index 1f58635f03c..b3c154b6ff1 100644 --- a/packages-content-model/roosterjs-content-model-types/lib/decorator/ContentModelParagraphDecorator.ts +++ b/packages-content-model/roosterjs-content-model-types/lib/decorator/ContentModelParagraphDecorator.ts @@ -1,5 +1,5 @@ -import { ContentModelSegmentFormat } from '../format/ContentModelSegmentFormat'; -import { ContentModelWithFormat } from '../format/ContentModelWithFormat'; +import type { ContentModelSegmentFormat } from '../format/ContentModelSegmentFormat'; +import type { ContentModelWithFormat } from '../format/ContentModelWithFormat'; /** * Represent decorator for a paragraph in Content Model diff --git a/packages-content-model/roosterjs-content-model-types/lib/entity/ContentModelEntity.ts b/packages-content-model/roosterjs-content-model-types/lib/entity/ContentModelEntity.ts index 5b6d899c8d9..897a572920e 100644 --- a/packages-content-model/roosterjs-content-model-types/lib/entity/ContentModelEntity.ts +++ b/packages-content-model/roosterjs-content-model-types/lib/entity/ContentModelEntity.ts @@ -1,7 +1,8 @@ -import { ContentModelBlockBase } from '../block/ContentModelBlockBase'; -import { ContentModelBlockFormat } from '../format/ContentModelBlockFormat'; -import { ContentModelSegmentBase } from '../segment/ContentModelSegmentBase'; -import { ContentModelSegmentFormat } from '../format/ContentModelSegmentFormat'; +import type { ContentModelBlockBase } from '../block/ContentModelBlockBase'; +import type { ContentModelBlockFormat } from '../format/ContentModelBlockFormat'; +import type { ContentModelEntityFormat } from '../format/ContentModelEntityFormat'; +import type { ContentModelSegmentBase } from '../segment/ContentModelSegmentBase'; +import type { ContentModelSegmentFormat } from '../format/ContentModelSegmentFormat'; /** * Content Model of Entity @@ -15,17 +16,7 @@ export interface ContentModelEntity wrapper: HTMLElement; /** - * Whether this is a readonly entity + * Format of this entity */ - isReadonly: boolean; - - /** - * Type of this entity. Specified when insert an entity, can be an valid CSS class-like string. - */ - type?: string; - - /** - * Id of this entity, generated by editor code and will be unique within an editor - */ - id?: string; + entityFormat: ContentModelEntityFormat; } diff --git a/packages-content-model/roosterjs-content-model-types/lib/format/ContentModelBlockFormat.ts b/packages-content-model/roosterjs-content-model-types/lib/format/ContentModelBlockFormat.ts index d10404cc222..ce92eb989e7 100644 --- a/packages-content-model/roosterjs-content-model-types/lib/format/ContentModelBlockFormat.ts +++ b/packages-content-model/roosterjs-content-model-types/lib/format/ContentModelBlockFormat.ts @@ -1,12 +1,12 @@ -import { BackgroundColorFormat } from './formatParts/BackgroundColorFormat'; -import { BorderFormat } from './formatParts/BorderFormat'; -import { DirectionFormat } from './formatParts/DirectionFormat'; -import { HtmlAlignFormat } from './formatParts/HtmlAlignFormat'; -import { LineHeightFormat } from './formatParts/LineHeightFormat'; -import { MarginFormat } from './formatParts/MarginFormat'; -import { PaddingFormat } from './formatParts/PaddingFormat'; -import { TextAlignFormat } from './formatParts/TextAlignFormat'; -import { WhiteSpaceFormat } from './formatParts/WhiteSpaceFormat'; +import type { BackgroundColorFormat } from './formatParts/BackgroundColorFormat'; +import type { BorderFormat } from './formatParts/BorderFormat'; +import type { DirectionFormat } from './formatParts/DirectionFormat'; +import type { HtmlAlignFormat } from './formatParts/HtmlAlignFormat'; +import type { LineHeightFormat } from './formatParts/LineHeightFormat'; +import type { MarginFormat } from './formatParts/MarginFormat'; +import type { PaddingFormat } from './formatParts/PaddingFormat'; +import type { TextAlignFormat } from './formatParts/TextAlignFormat'; +import type { WhiteSpaceFormat } from './formatParts/WhiteSpaceFormat'; /** * The format object for a paragraph in Content Model diff --git a/packages-content-model/roosterjs-content-model-types/lib/format/ContentModelCodeFormat.ts b/packages-content-model/roosterjs-content-model-types/lib/format/ContentModelCodeFormat.ts index e73ecb16854..e674a4f7552 100644 --- a/packages-content-model/roosterjs-content-model-types/lib/format/ContentModelCodeFormat.ts +++ b/packages-content-model/roosterjs-content-model-types/lib/format/ContentModelCodeFormat.ts @@ -1,5 +1,5 @@ -import { DisplayFormat } from './formatParts/DisplayFormat'; -import { FontFamilyFormat } from './formatParts/FontFamilyFormat'; +import type { DisplayFormat } from './formatParts/DisplayFormat'; +import type { FontFamilyFormat } from './formatParts/FontFamilyFormat'; /** * The format object for a code element in Content Model diff --git a/packages-content-model/roosterjs-content-model-types/lib/format/ContentModelDividerFormat.ts b/packages-content-model/roosterjs-content-model-types/lib/format/ContentModelDividerFormat.ts index 584d2e0df5a..bec64102174 100644 --- a/packages-content-model/roosterjs-content-model-types/lib/format/ContentModelDividerFormat.ts +++ b/packages-content-model/roosterjs-content-model-types/lib/format/ContentModelDividerFormat.ts @@ -1,6 +1,6 @@ -import { ContentModelBlockFormat } from './ContentModelBlockFormat'; -import { DisplayFormat } from './formatParts/DisplayFormat'; -import { SizeFormat } from './formatParts/SizeFormat'; +import type { ContentModelBlockFormat } from './ContentModelBlockFormat'; +import type { DisplayFormat } from './formatParts/DisplayFormat'; +import type { SizeFormat } from './formatParts/SizeFormat'; /** * The format object for a divider in Content Model diff --git a/packages-content-model/roosterjs-content-model-types/lib/format/ContentModelEntityFormat.ts b/packages-content-model/roosterjs-content-model-types/lib/format/ContentModelEntityFormat.ts new file mode 100644 index 00000000000..0570d49228d --- /dev/null +++ b/packages-content-model/roosterjs-content-model-types/lib/format/ContentModelEntityFormat.ts @@ -0,0 +1,7 @@ +import type { IdFormat } from './formatParts/IdFormat'; +import type { EntityInfoFormat } from './formatParts/EntityInfoFormat'; + +/** + * The format object for an entity in Content Model + */ +export type ContentModelEntityFormat = EntityInfoFormat & IdFormat; diff --git a/packages-content-model/roosterjs-content-model-types/lib/format/ContentModelFormatContainerFormat.ts b/packages-content-model/roosterjs-content-model-types/lib/format/ContentModelFormatContainerFormat.ts index be48e65cf0e..e5dfb905fc5 100644 --- a/packages-content-model/roosterjs-content-model-types/lib/format/ContentModelFormatContainerFormat.ts +++ b/packages-content-model/roosterjs-content-model-types/lib/format/ContentModelFormatContainerFormat.ts @@ -1,7 +1,7 @@ -import { ContentModelBlockFormat } from './ContentModelBlockFormat'; -import { ContentModelSegmentFormat } from './ContentModelSegmentFormat'; -import { DisplayFormat } from './formatParts/DisplayFormat'; -import { SizeFormat } from './formatParts/SizeFormat'; +import type { ContentModelBlockFormat } from './ContentModelBlockFormat'; +import type { ContentModelSegmentFormat } from './ContentModelSegmentFormat'; +import type { DisplayFormat } from './formatParts/DisplayFormat'; +import type { SizeFormat } from './formatParts/SizeFormat'; /** * Type for FormatContainer diff --git a/packages-content-model/roosterjs-content-model-types/lib/format/ContentModelFormatMap.ts b/packages-content-model/roosterjs-content-model-types/lib/format/ContentModelFormatMap.ts index 558b3deee89..7c2efc571d6 100644 --- a/packages-content-model/roosterjs-content-model-types/lib/format/ContentModelFormatMap.ts +++ b/packages-content-model/roosterjs-content-model-types/lib/format/ContentModelFormatMap.ts @@ -1,15 +1,16 @@ -import { ContentModelBlockFormat } from './ContentModelBlockFormat'; -import { ContentModelDividerFormat } from './ContentModelDividerFormat'; -import { ContentModelFormatContainerFormat } from './ContentModelFormatContainerFormat'; -import { ContentModelHyperLinkFormat } from './ContentModelHyperLinkFormat'; -import { ContentModelImageFormat } from './ContentModelImageFormat'; -import { ContentModelListItemFormat } from './ContentModelListItemFormat'; -import { ContentModelListItemLevelFormat } from './ContentModelListItemLevelFormat'; -import { ContentModelSegmentFormat } from './ContentModelSegmentFormat'; -import { ContentModelTableCellFormat } from './ContentModelTableCellFormat'; -import { ContentModelTableFormat } from './ContentModelTableFormat'; -import { DatasetFormat } from './metadata/DatasetFormat'; -import { FontFamilyFormat } from './formatParts/FontFamilyFormat'; +import type { ContentModelBlockFormat } from './ContentModelBlockFormat'; +import type { ContentModelDividerFormat } from './ContentModelDividerFormat'; +import type { ContentModelEntityFormat } from './ContentModelEntityFormat'; +import type { ContentModelFormatContainerFormat } from './ContentModelFormatContainerFormat'; +import type { ContentModelHyperLinkFormat } from './ContentModelHyperLinkFormat'; +import type { ContentModelImageFormat } from './ContentModelImageFormat'; +import type { ContentModelListItemFormat } from './ContentModelListItemFormat'; +import type { ContentModelListItemLevelFormat } from './ContentModelListItemLevelFormat'; +import type { ContentModelSegmentFormat } from './ContentModelSegmentFormat'; +import type { ContentModelTableCellFormat } from './ContentModelTableCellFormat'; +import type { ContentModelTableFormat } from './ContentModelTableFormat'; +import type { DatasetFormat } from './metadata/DatasetFormat'; +import type { FontFamilyFormat } from './formatParts/FontFamilyFormat'; /** * A map from Content Model format name to its combined format type @@ -124,4 +125,9 @@ export interface ContentModelFormatMap { * Format type for format container */ container: ContentModelFormatContainerFormat; + + /** + * Format type for entity + */ + entity: ContentModelEntityFormat; } diff --git a/packages-content-model/roosterjs-content-model-types/lib/format/ContentModelHyperLinkFormat.ts b/packages-content-model/roosterjs-content-model-types/lib/format/ContentModelHyperLinkFormat.ts index 20dfab68155..129cc55d84f 100644 --- a/packages-content-model/roosterjs-content-model-types/lib/format/ContentModelHyperLinkFormat.ts +++ b/packages-content-model/roosterjs-content-model-types/lib/format/ContentModelHyperLinkFormat.ts @@ -1,13 +1,13 @@ -import { BackgroundColorFormat } from './formatParts/BackgroundColorFormat'; -import { BorderFormat } from './formatParts/BorderFormat'; -import { DisplayFormat } from './formatParts/DisplayFormat'; -import { LinkFormat } from './formatParts/LinkFormat'; -import { MarginFormat } from './formatParts/MarginFormat'; -import { PaddingFormat } from './formatParts/PaddingFormat'; -import { SizeFormat } from './formatParts/SizeFormat'; -import { TextAlignFormat } from './formatParts/TextAlignFormat'; -import { TextColorFormat } from './formatParts/TextColorFormat'; -import { UnderlineFormat } from './formatParts/UnderlineFormat'; +import type { BackgroundColorFormat } from './formatParts/BackgroundColorFormat'; +import type { BorderFormat } from './formatParts/BorderFormat'; +import type { DisplayFormat } from './formatParts/DisplayFormat'; +import type { LinkFormat } from './formatParts/LinkFormat'; +import type { MarginFormat } from './formatParts/MarginFormat'; +import type { PaddingFormat } from './formatParts/PaddingFormat'; +import type { SizeFormat } from './formatParts/SizeFormat'; +import type { TextAlignFormat } from './formatParts/TextAlignFormat'; +import type { TextColorFormat } from './formatParts/TextColorFormat'; +import type { UnderlineFormat } from './formatParts/UnderlineFormat'; /** * The format object for a hyperlink in Content Model diff --git a/packages-content-model/roosterjs-content-model-types/lib/format/ContentModelImageFormat.ts b/packages-content-model/roosterjs-content-model-types/lib/format/ContentModelImageFormat.ts index f344c54931b..6f9ba413a85 100644 --- a/packages-content-model/roosterjs-content-model-types/lib/format/ContentModelImageFormat.ts +++ b/packages-content-model/roosterjs-content-model-types/lib/format/ContentModelImageFormat.ts @@ -1,13 +1,13 @@ -import { BorderFormat } from './formatParts/BorderFormat'; -import { BoxShadowFormat } from './formatParts/BoxShadowFormat'; -import { ContentModelSegmentFormat } from './ContentModelSegmentFormat'; -import { DisplayFormat } from './formatParts/DisplayFormat'; -import { FloatFormat } from './formatParts/FloatFormat'; -import { IdFormat } from './formatParts/IdFormat'; -import { MarginFormat } from './formatParts/MarginFormat'; -import { PaddingFormat } from './formatParts/PaddingFormat'; -import { SizeFormat } from './formatParts/SizeFormat'; -import { VerticalAlignFormat } from './formatParts/VerticalAlignFormat'; +import type { BorderFormat } from './formatParts/BorderFormat'; +import type { BoxShadowFormat } from './formatParts/BoxShadowFormat'; +import type { ContentModelSegmentFormat } from './ContentModelSegmentFormat'; +import type { DisplayFormat } from './formatParts/DisplayFormat'; +import type { FloatFormat } from './formatParts/FloatFormat'; +import type { IdFormat } from './formatParts/IdFormat'; +import type { MarginFormat } from './formatParts/MarginFormat'; +import type { PaddingFormat } from './formatParts/PaddingFormat'; +import type { SizeFormat } from './formatParts/SizeFormat'; +import type { VerticalAlignFormat } from './formatParts/VerticalAlignFormat'; /** * The format object for an image in Content Model diff --git a/packages-content-model/roosterjs-content-model-types/lib/format/ContentModelListItemFormat.ts b/packages-content-model/roosterjs-content-model-types/lib/format/ContentModelListItemFormat.ts index 1b948ccc3c5..340322ccb4c 100644 --- a/packages-content-model/roosterjs-content-model-types/lib/format/ContentModelListItemFormat.ts +++ b/packages-content-model/roosterjs-content-model-types/lib/format/ContentModelListItemFormat.ts @@ -1,8 +1,8 @@ -import { DirectionFormat } from './formatParts/DirectionFormat'; -import { LineHeightFormat } from './formatParts/LineHeightFormat'; -import { MarginFormat } from './formatParts/MarginFormat'; -import { PaddingFormat } from './formatParts/PaddingFormat'; -import { TextAlignFormat } from './formatParts/TextAlignFormat'; +import type { DirectionFormat } from './formatParts/DirectionFormat'; +import type { LineHeightFormat } from './formatParts/LineHeightFormat'; +import type { MarginFormat } from './formatParts/MarginFormat'; +import type { PaddingFormat } from './formatParts/PaddingFormat'; +import type { TextAlignFormat } from './formatParts/TextAlignFormat'; /** * The format object for a list item in Content Model diff --git a/packages-content-model/roosterjs-content-model-types/lib/format/ContentModelListItemLevelFormat.ts b/packages-content-model/roosterjs-content-model-types/lib/format/ContentModelListItemLevelFormat.ts index 9ee4c43cd93..f400fc4ca39 100644 --- a/packages-content-model/roosterjs-content-model-types/lib/format/ContentModelListItemLevelFormat.ts +++ b/packages-content-model/roosterjs-content-model-types/lib/format/ContentModelListItemLevelFormat.ts @@ -1,9 +1,9 @@ -import { DirectionFormat } from './formatParts/DirectionFormat'; -import { ListStylePositionFormat } from './formatParts/ListStylePositionFormat'; -import { ListThreadFormat } from './formatParts/ListThreadFormat'; -import { MarginFormat } from './formatParts/MarginFormat'; -import { PaddingFormat } from './formatParts/PaddingFormat'; -import { TextAlignFormat } from './formatParts/TextAlignFormat'; +import type { DirectionFormat } from './formatParts/DirectionFormat'; +import type { ListStylePositionFormat } from './formatParts/ListStylePositionFormat'; +import type { ListThreadFormat } from './formatParts/ListThreadFormat'; +import type { MarginFormat } from './formatParts/MarginFormat'; +import type { PaddingFormat } from './formatParts/PaddingFormat'; +import type { TextAlignFormat } from './formatParts/TextAlignFormat'; /** * The format object for a list level in Content Model diff --git a/packages-content-model/roosterjs-content-model-types/lib/format/ContentModelSegmentFormat.ts b/packages-content-model/roosterjs-content-model-types/lib/format/ContentModelSegmentFormat.ts index 5ca83f6a498..d8552973cef 100644 --- a/packages-content-model/roosterjs-content-model-types/lib/format/ContentModelSegmentFormat.ts +++ b/packages-content-model/roosterjs-content-model-types/lib/format/ContentModelSegmentFormat.ts @@ -1,14 +1,14 @@ -import { BackgroundColorFormat } from './formatParts/BackgroundColorFormat'; -import { BoldFormat } from './formatParts/BoldFormat'; -import { FontFamilyFormat } from './formatParts/FontFamilyFormat'; -import { FontSizeFormat } from './formatParts/FontSizeFormat'; -import { ItalicFormat } from './formatParts/ItalicFormat'; -import { LetterSpacingFormat } from './formatParts/LetterSpacingFormat'; -import { LineHeightFormat } from './formatParts/LineHeightFormat'; -import { StrikeFormat } from './formatParts/StrikeFormat'; -import { SuperOrSubScriptFormat } from './formatParts/SuperOrSubScriptFormat'; -import { TextColorFormat } from './formatParts/TextColorFormat'; -import { UnderlineFormat } from './formatParts/UnderlineFormat'; +import type { BackgroundColorFormat } from './formatParts/BackgroundColorFormat'; +import type { BoldFormat } from './formatParts/BoldFormat'; +import type { FontFamilyFormat } from './formatParts/FontFamilyFormat'; +import type { FontSizeFormat } from './formatParts/FontSizeFormat'; +import type { ItalicFormat } from './formatParts/ItalicFormat'; +import type { LetterSpacingFormat } from './formatParts/LetterSpacingFormat'; +import type { LineHeightFormat } from './formatParts/LineHeightFormat'; +import type { StrikeFormat } from './formatParts/StrikeFormat'; +import type { SuperOrSubScriptFormat } from './formatParts/SuperOrSubScriptFormat'; +import type { TextColorFormat } from './formatParts/TextColorFormat'; +import type { UnderlineFormat } from './formatParts/UnderlineFormat'; /** * The format object for a segment in Content Model diff --git a/packages-content-model/roosterjs-content-model-types/lib/format/ContentModelTableCellFormat.ts b/packages-content-model/roosterjs-content-model-types/lib/format/ContentModelTableCellFormat.ts index 58d00cd73bb..651535c95f0 100644 --- a/packages-content-model/roosterjs-content-model-types/lib/format/ContentModelTableCellFormat.ts +++ b/packages-content-model/roosterjs-content-model-types/lib/format/ContentModelTableCellFormat.ts @@ -1,9 +1,9 @@ -import { BorderBoxFormat } from './formatParts/BorderBoxFormat'; -import { ContentModelBlockFormat } from './ContentModelBlockFormat'; -import { SizeFormat } from './formatParts/SizeFormat'; -import { TextColorFormat } from './formatParts/TextColorFormat'; -import { VerticalAlignFormat } from './formatParts/VerticalAlignFormat'; -import { WordBreakFormat } from '../format/formatParts/WordBreakFormat'; +import type { BorderBoxFormat } from './formatParts/BorderBoxFormat'; +import type { ContentModelBlockFormat } from './ContentModelBlockFormat'; +import type { SizeFormat } from './formatParts/SizeFormat'; +import type { TextColorFormat } from './formatParts/TextColorFormat'; +import type { VerticalAlignFormat } from './formatParts/VerticalAlignFormat'; +import type { WordBreakFormat } from '../format/formatParts/WordBreakFormat'; /** * Format of table cell diff --git a/packages-content-model/roosterjs-content-model-types/lib/format/ContentModelTableFormat.ts b/packages-content-model/roosterjs-content-model-types/lib/format/ContentModelTableFormat.ts index cb653038b16..0754cb5c93b 100644 --- a/packages-content-model/roosterjs-content-model-types/lib/format/ContentModelTableFormat.ts +++ b/packages-content-model/roosterjs-content-model-types/lib/format/ContentModelTableFormat.ts @@ -1,11 +1,11 @@ -import { BorderBoxFormat } from './formatParts/BorderBoxFormat'; -import { BorderFormat } from './formatParts/BorderFormat'; -import { ContentModelBlockFormat } from './ContentModelBlockFormat'; -import { DisplayFormat } from './formatParts/DisplayFormat'; -import { IdFormat } from './formatParts/IdFormat'; -import { MarginFormat } from './formatParts/MarginFormat'; -import { SpacingFormat } from './formatParts/SpacingFormat'; -import { TableLayoutFormat } from './formatParts/TableLayoutFormat'; +import type { BorderBoxFormat } from './formatParts/BorderBoxFormat'; +import type { BorderFormat } from './formatParts/BorderFormat'; +import type { ContentModelBlockFormat } from './ContentModelBlockFormat'; +import type { DisplayFormat } from './formatParts/DisplayFormat'; +import type { IdFormat } from './formatParts/IdFormat'; +import type { MarginFormat } from './formatParts/MarginFormat'; +import type { SpacingFormat } from './formatParts/SpacingFormat'; +import type { TableLayoutFormat } from './formatParts/TableLayoutFormat'; /** * Format of Table diff --git a/packages-content-model/roosterjs-content-model-types/lib/format/ContentModelWithDataset.ts b/packages-content-model/roosterjs-content-model-types/lib/format/ContentModelWithDataset.ts index 111f07ff6fc..0fe8f417c78 100644 --- a/packages-content-model/roosterjs-content-model-types/lib/format/ContentModelWithDataset.ts +++ b/packages-content-model/roosterjs-content-model-types/lib/format/ContentModelWithDataset.ts @@ -1,4 +1,4 @@ -import { DatasetFormat } from './metadata/DatasetFormat'; +import type { DatasetFormat } from './metadata/DatasetFormat'; /** * Represents base format of an element that supports dataset and/or metadata diff --git a/packages-content-model/roosterjs-content-model-types/lib/format/ContentModelWithFormat.ts b/packages-content-model/roosterjs-content-model-types/lib/format/ContentModelWithFormat.ts index 95eaaadddac..53f52e67522 100644 --- a/packages-content-model/roosterjs-content-model-types/lib/format/ContentModelWithFormat.ts +++ b/packages-content-model/roosterjs-content-model-types/lib/format/ContentModelWithFormat.ts @@ -1,4 +1,4 @@ -import { ContentModelFormatBase } from './ContentModelFormatBase'; +import type { ContentModelFormatBase } from './ContentModelFormatBase'; /** * Represent a content model with format diff --git a/packages-content-model/roosterjs-content-model-types/lib/format/FormatHandlerTypeMap.ts b/packages-content-model/roosterjs-content-model-types/lib/format/FormatHandlerTypeMap.ts index 8936c886c75..d3d32c43763 100644 --- a/packages-content-model/roosterjs-content-model-types/lib/format/FormatHandlerTypeMap.ts +++ b/packages-content-model/roosterjs-content-model-types/lib/format/FormatHandlerTypeMap.ts @@ -1,35 +1,36 @@ -import { BackgroundColorFormat } from './formatParts/BackgroundColorFormat'; -import { BoldFormat } from './formatParts/BoldFormat'; -import { BorderBoxFormat } from './formatParts/BorderBoxFormat'; -import { BorderFormat } from './formatParts/BorderFormat'; -import { BoxShadowFormat } from './formatParts/BoxShadowFormat'; -import { DatasetFormat } from './metadata/DatasetFormat'; -import { DirectionFormat } from './formatParts/DirectionFormat'; -import { DisplayFormat } from './formatParts/DisplayFormat'; -import { FloatFormat } from './formatParts/FloatFormat'; -import { FontFamilyFormat } from './formatParts/FontFamilyFormat'; -import { FontSizeFormat } from './formatParts/FontSizeFormat'; -import { HtmlAlignFormat } from './formatParts/HtmlAlignFormat'; -import { IdFormat } from './formatParts/IdFormat'; -import { ItalicFormat } from './formatParts/ItalicFormat'; -import { LetterSpacingFormat } from './formatParts/LetterSpacingFormat'; -import { LineHeightFormat } from './formatParts/LineHeightFormat'; -import { LinkFormat } from './formatParts/LinkFormat'; -import { ListStylePositionFormat } from './formatParts/ListStylePositionFormat'; -import { ListThreadFormat } from './formatParts/ListThreadFormat'; -import { MarginFormat } from './formatParts/MarginFormat'; -import { PaddingFormat } from './formatParts/PaddingFormat'; -import { SizeFormat } from './formatParts/SizeFormat'; -import { SpacingFormat } from './formatParts/SpacingFormat'; -import { StrikeFormat } from './formatParts/StrikeFormat'; -import { SuperOrSubScriptFormat } from './formatParts/SuperOrSubScriptFormat'; -import { TableLayoutFormat } from './formatParts/TableLayoutFormat'; -import { TextAlignFormat } from './formatParts/TextAlignFormat'; -import { TextColorFormat } from './formatParts/TextColorFormat'; -import { UnderlineFormat } from './formatParts/UnderlineFormat'; -import { VerticalAlignFormat } from './formatParts/VerticalAlignFormat'; -import { WhiteSpaceFormat } from './formatParts/WhiteSpaceFormat'; -import { WordBreakFormat } from './formatParts/WordBreakFormat'; +import type { BackgroundColorFormat } from './formatParts/BackgroundColorFormat'; +import type { BoldFormat } from './formatParts/BoldFormat'; +import type { BorderBoxFormat } from './formatParts/BorderBoxFormat'; +import type { BorderFormat } from './formatParts/BorderFormat'; +import type { BoxShadowFormat } from './formatParts/BoxShadowFormat'; +import type { DatasetFormat } from './metadata/DatasetFormat'; +import type { DirectionFormat } from './formatParts/DirectionFormat'; +import type { DisplayFormat } from './formatParts/DisplayFormat'; +import type { EntityInfoFormat } from './formatParts/EntityInfoFormat'; +import type { FloatFormat } from './formatParts/FloatFormat'; +import type { FontFamilyFormat } from './formatParts/FontFamilyFormat'; +import type { FontSizeFormat } from './formatParts/FontSizeFormat'; +import type { HtmlAlignFormat } from './formatParts/HtmlAlignFormat'; +import type { IdFormat } from './formatParts/IdFormat'; +import type { ItalicFormat } from './formatParts/ItalicFormat'; +import type { LetterSpacingFormat } from './formatParts/LetterSpacingFormat'; +import type { LineHeightFormat } from './formatParts/LineHeightFormat'; +import type { LinkFormat } from './formatParts/LinkFormat'; +import type { ListStylePositionFormat } from './formatParts/ListStylePositionFormat'; +import type { ListThreadFormat } from './formatParts/ListThreadFormat'; +import type { MarginFormat } from './formatParts/MarginFormat'; +import type { PaddingFormat } from './formatParts/PaddingFormat'; +import type { SizeFormat } from './formatParts/SizeFormat'; +import type { SpacingFormat } from './formatParts/SpacingFormat'; +import type { StrikeFormat } from './formatParts/StrikeFormat'; +import type { SuperOrSubScriptFormat } from './formatParts/SuperOrSubScriptFormat'; +import type { TableLayoutFormat } from './formatParts/TableLayoutFormat'; +import type { TextAlignFormat } from './formatParts/TextAlignFormat'; +import type { TextColorFormat } from './formatParts/TextColorFormat'; +import type { UnderlineFormat } from './formatParts/UnderlineFormat'; +import type { VerticalAlignFormat } from './formatParts/VerticalAlignFormat'; +import type { WhiteSpaceFormat } from './formatParts/WhiteSpaceFormat'; +import type { WordBreakFormat } from './formatParts/WordBreakFormat'; /** * Represents a record of all format handlers @@ -75,6 +76,11 @@ export interface FormatHandlerTypeMap { */ display: DisplayFormat; + /** + * Format for EntityInfoFormat and IdFormat + */ + entity: EntityInfoFormat & IdFormat; + /** * Format for FloatFormat */ diff --git a/packages-content-model/roosterjs-content-model-types/lib/format/formatParts/EntityInfoFormat.ts b/packages-content-model/roosterjs-content-model-types/lib/format/formatParts/EntityInfoFormat.ts new file mode 100644 index 00000000000..1476ef1b32f --- /dev/null +++ b/packages-content-model/roosterjs-content-model-types/lib/format/formatParts/EntityInfoFormat.ts @@ -0,0 +1,19 @@ +/** + * Format of entity type + */ +export type EntityInfoFormat = { + /** + * For a readonly DOM element, we also treat it as entity, with isFakeEntity set to true + */ + isFakeEntity?: boolean; + + /** + * Whether the entity is readonly + */ + isReadonly?: boolean; + + /** + * Type of this entity + */ + entityType?: string; +}; diff --git a/packages-content-model/roosterjs-content-model-types/lib/format/metadata/ListMetadataFormat.ts b/packages-content-model/roosterjs-content-model-types/lib/format/metadata/ListMetadataFormat.ts index a7becb5d993..fb8ea110f94 100644 --- a/packages-content-model/roosterjs-content-model-types/lib/format/metadata/ListMetadataFormat.ts +++ b/packages-content-model/roosterjs-content-model-types/lib/format/metadata/ListMetadataFormat.ts @@ -1,4 +1,4 @@ -import { BulletListType, NumberingListType } from 'roosterjs-editor-types'; +import type { BulletListType, NumberingListType } from 'roosterjs-editor-types'; /** * Format of list / list item that stored as metadata diff --git a/packages-content-model/roosterjs-content-model-types/lib/format/metadata/TableMetadataFormat.ts b/packages-content-model/roosterjs-content-model-types/lib/format/metadata/TableMetadataFormat.ts index 8a73070b9f4..9118d22e1e0 100644 --- a/packages-content-model/roosterjs-content-model-types/lib/format/metadata/TableMetadataFormat.ts +++ b/packages-content-model/roosterjs-content-model-types/lib/format/metadata/TableMetadataFormat.ts @@ -1,4 +1,4 @@ -import { TableBorderFormat } from 'roosterjs-editor-types'; +import type { TableBorderFormat } from 'roosterjs-editor-types'; import type { CompatibleTableBorderFormat } from 'roosterjs-editor-types/lib/compatibleTypes'; /** diff --git a/packages-content-model/roosterjs-content-model-types/lib/group/ContentModelBlockGroup.ts b/packages-content-model/roosterjs-content-model-types/lib/group/ContentModelBlockGroup.ts index 855ab6b5ac2..9462d998e10 100644 --- a/packages-content-model/roosterjs-content-model-types/lib/group/ContentModelBlockGroup.ts +++ b/packages-content-model/roosterjs-content-model-types/lib/group/ContentModelBlockGroup.ts @@ -1,8 +1,8 @@ -import { ContentModelDocument } from './ContentModelDocument'; -import { ContentModelFormatContainer } from './ContentModelFormatContainer'; -import { ContentModelGeneralBlock } from './ContentModelGeneralBlock'; -import { ContentModelListItem } from './ContentModelListItem'; -import { ContentModelTableCell } from './ContentModelTableCell'; +import type { ContentModelDocument } from './ContentModelDocument'; +import type { ContentModelFormatContainer } from './ContentModelFormatContainer'; +import type { ContentModelGeneralBlock } from './ContentModelGeneralBlock'; +import type { ContentModelListItem } from './ContentModelListItem'; +import type { ContentModelTableCell } from './ContentModelTableCell'; /** * The union type of Content Model Block Group diff --git a/packages-content-model/roosterjs-content-model-types/lib/group/ContentModelBlockGroupBase.ts b/packages-content-model/roosterjs-content-model-types/lib/group/ContentModelBlockGroupBase.ts index bcfcbd0e3b8..afb1a07f30e 100644 --- a/packages-content-model/roosterjs-content-model-types/lib/group/ContentModelBlockGroupBase.ts +++ b/packages-content-model/roosterjs-content-model-types/lib/group/ContentModelBlockGroupBase.ts @@ -1,5 +1,5 @@ -import { ContentModelBlock } from '../block/ContentModelBlock'; -import { ContentModelBlockGroupType } from '../enum/BlockGroupType'; +import type { ContentModelBlock } from '../block/ContentModelBlock'; +import type { ContentModelBlockGroupType } from '../enum/BlockGroupType'; /** * Base type of Content Model Block Group diff --git a/packages-content-model/roosterjs-content-model-types/lib/group/ContentModelDocument.ts b/packages-content-model/roosterjs-content-model-types/lib/group/ContentModelDocument.ts index 8b0fd81e4ba..0d9126a3ae0 100644 --- a/packages-content-model/roosterjs-content-model-types/lib/group/ContentModelDocument.ts +++ b/packages-content-model/roosterjs-content-model-types/lib/group/ContentModelDocument.ts @@ -1,6 +1,6 @@ -import { ContentModelBlockGroupBase } from './ContentModelBlockGroupBase'; -import { ContentModelSegmentFormat } from '../format/ContentModelSegmentFormat'; -import { ContentModelWithFormat } from '../format/ContentModelWithFormat'; +import type { ContentModelBlockGroupBase } from './ContentModelBlockGroupBase'; +import type { ContentModelSegmentFormat } from '../format/ContentModelSegmentFormat'; +import type { ContentModelWithFormat } from '../format/ContentModelWithFormat'; /** * Content Model document entry point diff --git a/packages-content-model/roosterjs-content-model-types/lib/group/ContentModelFormatContainer.ts b/packages-content-model/roosterjs-content-model-types/lib/group/ContentModelFormatContainer.ts index 2bc21852f56..2f0f987bb40 100644 --- a/packages-content-model/roosterjs-content-model-types/lib/group/ContentModelFormatContainer.ts +++ b/packages-content-model/roosterjs-content-model-types/lib/group/ContentModelFormatContainer.ts @@ -1,7 +1,7 @@ -import { ContentModelBlockBase } from '../block/ContentModelBlockBase'; -import { ContentModelBlockGroupBase } from './ContentModelBlockGroupBase'; -import { ContentModelBlockWithCache } from '../block/ContentModelBlockWithCache'; -import { ContentModelFormatContainerFormat } from '../format/ContentModelFormatContainerFormat'; +import type { ContentModelBlockBase } from '../block/ContentModelBlockBase'; +import type { ContentModelBlockGroupBase } from './ContentModelBlockGroupBase'; +import type { ContentModelBlockWithCache } from '../block/ContentModelBlockWithCache'; +import type { ContentModelFormatContainerFormat } from '../format/ContentModelFormatContainerFormat'; /** * Content Model of Format Container diff --git a/packages-content-model/roosterjs-content-model-types/lib/group/ContentModelGeneralBlock.ts b/packages-content-model/roosterjs-content-model-types/lib/group/ContentModelGeneralBlock.ts index 3f2448482b3..800cce2481f 100644 --- a/packages-content-model/roosterjs-content-model-types/lib/group/ContentModelGeneralBlock.ts +++ b/packages-content-model/roosterjs-content-model-types/lib/group/ContentModelGeneralBlock.ts @@ -1,8 +1,8 @@ -import { ContentModelBlockBase } from '../block/ContentModelBlockBase'; -import { ContentModelBlockFormat } from '../format/ContentModelBlockFormat'; -import { ContentModelBlockGroupBase } from './ContentModelBlockGroupBase'; -import { ContentModelSegmentFormat } from '../format/ContentModelSegmentFormat'; -import { Selectable } from '../selection/Selectable'; +import type { ContentModelBlockBase } from '../block/ContentModelBlockBase'; +import type { ContentModelBlockFormat } from '../format/ContentModelBlockFormat'; +import type { ContentModelBlockGroupBase } from './ContentModelBlockGroupBase'; +import type { ContentModelSegmentFormat } from '../format/ContentModelSegmentFormat'; +import type { Selectable } from '../selection/Selectable'; /** * Content Model for general Block element diff --git a/packages-content-model/roosterjs-content-model-types/lib/group/ContentModelListItem.ts b/packages-content-model/roosterjs-content-model-types/lib/group/ContentModelListItem.ts index 4d196d19223..a7b7eabd5ff 100644 --- a/packages-content-model/roosterjs-content-model-types/lib/group/ContentModelListItem.ts +++ b/packages-content-model/roosterjs-content-model-types/lib/group/ContentModelListItem.ts @@ -1,8 +1,8 @@ -import { ContentModelBlockBase } from '../block/ContentModelBlockBase'; -import { ContentModelBlockGroupBase } from './ContentModelBlockGroupBase'; -import { ContentModelListItemFormat } from '../format/ContentModelListItemFormat'; -import { ContentModelListLevel } from '../decorator/ContentModelListLevel'; -import { ContentModelSelectionMarker } from '../segment/ContentModelSelectionMarker'; +import type { ContentModelBlockBase } from '../block/ContentModelBlockBase'; +import type { ContentModelBlockGroupBase } from './ContentModelBlockGroupBase'; +import type { ContentModelListItemFormat } from '../format/ContentModelListItemFormat'; +import type { ContentModelListLevel } from '../decorator/ContentModelListLevel'; +import type { ContentModelSelectionMarker } from '../segment/ContentModelSelectionMarker'; /** * Content Model of List Item diff --git a/packages-content-model/roosterjs-content-model-types/lib/group/ContentModelTableCell.ts b/packages-content-model/roosterjs-content-model-types/lib/group/ContentModelTableCell.ts index d9c425da485..b46081c4276 100644 --- a/packages-content-model/roosterjs-content-model-types/lib/group/ContentModelTableCell.ts +++ b/packages-content-model/roosterjs-content-model-types/lib/group/ContentModelTableCell.ts @@ -1,10 +1,10 @@ -import { ContentModelBlockGroupBase } from './ContentModelBlockGroupBase'; -import { ContentModelBlockWithCache } from '../block/ContentModelBlockWithCache'; -import { ContentModelTableCellFormat } from '../format/ContentModelTableCellFormat'; -import { ContentModelWithDataset } from '../format/ContentModelWithDataset'; -import { ContentModelWithFormat } from '../format/ContentModelWithFormat'; -import { Selectable } from '../selection/Selectable'; -import { TableCellMetadataFormat } from 'roosterjs-editor-types'; +import type { ContentModelBlockGroupBase } from './ContentModelBlockGroupBase'; +import type { ContentModelBlockWithCache } from '../block/ContentModelBlockWithCache'; +import type { ContentModelTableCellFormat } from '../format/ContentModelTableCellFormat'; +import type { ContentModelWithDataset } from '../format/ContentModelWithDataset'; +import type { ContentModelWithFormat } from '../format/ContentModelWithFormat'; +import type { Selectable } from '../selection/Selectable'; +import type { TableCellMetadataFormat } from 'roosterjs-editor-types'; /** * Content Model of Table Cell diff --git a/packages-content-model/roosterjs-content-model-types/lib/index.ts b/packages-content-model/roosterjs-content-model-types/lib/index.ts index e2a4ad5d7c6..5a6114299e6 100644 --- a/packages-content-model/roosterjs-content-model-types/lib/index.ts +++ b/packages-content-model/roosterjs-content-model-types/lib/index.ts @@ -13,6 +13,7 @@ export { ContentModelDividerFormat } from './format/ContentModelDividerFormat'; export { ContentModelFormatBase } from './format/ContentModelFormatBase'; export { ContentModelFormatMap } from './format/ContentModelFormatMap'; export { ContentModelImageFormat } from './format/ContentModelImageFormat'; +export { ContentModelEntityFormat } from './format/ContentModelEntityFormat'; export { FormatHandlerTypeMap, FormatKey } from './format/FormatHandlerTypeMap'; export { BackgroundColorFormat } from './format/formatParts/BackgroundColorFormat'; @@ -46,6 +47,7 @@ export { BoxShadowFormat } from './format/formatParts/BoxShadowFormat'; export { ListThreadFormat } from './format/formatParts/ListThreadFormat'; export { ListStylePositionFormat } from './format/formatParts/ListStylePositionFormat'; export { FloatFormat } from './format/formatParts/FloatFormat'; +export { EntityInfoFormat } from './format/formatParts/EntityInfoFormat'; export { DatasetFormat } from './format/metadata/DatasetFormat'; export { TableMetadataFormat } from './format/metadata/TableMetadataFormat'; @@ -94,6 +96,14 @@ export { ContentModelDecorator } from './decorator/ContentModelDecorator'; export { ContentModelListLevel } from './decorator/ContentModelListLevel'; export { Selectable } from './selection/Selectable'; +export { + DOMSelection, + SelectionType, + SelectionBase, + ImageSelection, + RangeSelection, + TableSelection, +} from './selection/DOMSelection'; export { ContentModelHandlerMap, @@ -125,8 +135,6 @@ export { ModelToDomContext } from './context/ModelToDomContext'; export { ModelToDomBlockAndSegmentNode, ModelToDomRegularSelection, - ModelToDomTableSelection, - ModelToDomImageSelection, ModelToDomSelectionContext, } from './context/ModelToDomSelectionContext'; export { @@ -141,3 +149,4 @@ export { } from './context/ContentModelHandler'; export { DomToModelOption } from './context/DomToModelOption'; export { ModelToDomOption } from './context/ModelToDomOption'; +export { ContentModelDomIndexer } from './context/ContentModelDomIndexer'; diff --git a/packages-content-model/roosterjs-content-model-types/lib/segment/ContentModelBr.ts b/packages-content-model/roosterjs-content-model-types/lib/segment/ContentModelBr.ts index e141daf9efc..d8bf1b04871 100644 --- a/packages-content-model/roosterjs-content-model-types/lib/segment/ContentModelBr.ts +++ b/packages-content-model/roosterjs-content-model-types/lib/segment/ContentModelBr.ts @@ -1,4 +1,4 @@ -import { ContentModelSegmentBase } from './ContentModelSegmentBase'; +import type { ContentModelSegmentBase } from './ContentModelSegmentBase'; /** * Content Model of BR diff --git a/packages-content-model/roosterjs-content-model-types/lib/segment/ContentModelGeneralSegment.ts b/packages-content-model/roosterjs-content-model-types/lib/segment/ContentModelGeneralSegment.ts index dd69aa260c0..a0efb7ef80a 100644 --- a/packages-content-model/roosterjs-content-model-types/lib/segment/ContentModelGeneralSegment.ts +++ b/packages-content-model/roosterjs-content-model-types/lib/segment/ContentModelGeneralSegment.ts @@ -1,7 +1,7 @@ -import { ContentModelBlockFormat } from '../format/ContentModelBlockFormat'; -import { ContentModelGeneralBlock } from '../group/ContentModelGeneralBlock'; -import { ContentModelSegmentBase } from './ContentModelSegmentBase'; -import { ContentModelSegmentFormat } from '../format/ContentModelSegmentFormat'; +import type { ContentModelBlockFormat } from '../format/ContentModelBlockFormat'; +import type { ContentModelGeneralBlock } from '../group/ContentModelGeneralBlock'; +import type { ContentModelSegmentBase } from './ContentModelSegmentBase'; +import type { ContentModelSegmentFormat } from '../format/ContentModelSegmentFormat'; /** * Content Model of general Segment diff --git a/packages-content-model/roosterjs-content-model-types/lib/segment/ContentModelImage.ts b/packages-content-model/roosterjs-content-model-types/lib/segment/ContentModelImage.ts index 5a4c284707a..8b457ce4f0c 100644 --- a/packages-content-model/roosterjs-content-model-types/lib/segment/ContentModelImage.ts +++ b/packages-content-model/roosterjs-content-model-types/lib/segment/ContentModelImage.ts @@ -1,7 +1,7 @@ -import { ContentModelImageFormat } from '../format/ContentModelImageFormat'; -import { ContentModelSegmentBase } from './ContentModelSegmentBase'; -import { ContentModelWithDataset } from '../format/ContentModelWithDataset'; -import { ImageMetadataFormat } from '../format/metadata/ImageMetadataFormat'; +import type { ContentModelImageFormat } from '../format/ContentModelImageFormat'; +import type { ContentModelSegmentBase } from './ContentModelSegmentBase'; +import type { ContentModelWithDataset } from '../format/ContentModelWithDataset'; +import type { ImageMetadataFormat } from '../format/metadata/ImageMetadataFormat'; /** * Content Model of IMG diff --git a/packages-content-model/roosterjs-content-model-types/lib/segment/ContentModelSegment.ts b/packages-content-model/roosterjs-content-model-types/lib/segment/ContentModelSegment.ts index b797c080b66..77fd2c68cad 100644 --- a/packages-content-model/roosterjs-content-model-types/lib/segment/ContentModelSegment.ts +++ b/packages-content-model/roosterjs-content-model-types/lib/segment/ContentModelSegment.ts @@ -1,9 +1,9 @@ -import { ContentModelBr } from './ContentModelBr'; -import { ContentModelEntity } from '../entity/ContentModelEntity'; -import { ContentModelGeneralSegment } from './ContentModelGeneralSegment'; -import { ContentModelImage } from './ContentModelImage'; -import { ContentModelSelectionMarker } from './ContentModelSelectionMarker'; -import { ContentModelText } from './ContentModelText'; +import type { ContentModelBr } from './ContentModelBr'; +import type { ContentModelEntity } from '../entity/ContentModelEntity'; +import type { ContentModelGeneralSegment } from './ContentModelGeneralSegment'; +import type { ContentModelImage } from './ContentModelImage'; +import type { ContentModelSelectionMarker } from './ContentModelSelectionMarker'; +import type { ContentModelText } from './ContentModelText'; /** * Union type of Content Model Segment diff --git a/packages-content-model/roosterjs-content-model-types/lib/segment/ContentModelSegmentBase.ts b/packages-content-model/roosterjs-content-model-types/lib/segment/ContentModelSegmentBase.ts index e411b2f93bd..2e06a7d46ae 100644 --- a/packages-content-model/roosterjs-content-model-types/lib/segment/ContentModelSegmentBase.ts +++ b/packages-content-model/roosterjs-content-model-types/lib/segment/ContentModelSegmentBase.ts @@ -1,9 +1,9 @@ -import { ContentModelCode } from '../decorator/ContentModelCode'; -import { ContentModelLink } from '../decorator/ContentModelLink'; -import { ContentModelSegmentFormat } from '../format/ContentModelSegmentFormat'; -import { ContentModelSegmentType } from '../enum/SegmentType'; -import { ContentModelWithFormat } from '../format/ContentModelWithFormat'; -import { Selectable } from '../selection/Selectable'; +import type { ContentModelCode } from '../decorator/ContentModelCode'; +import type { ContentModelLink } from '../decorator/ContentModelLink'; +import type { ContentModelSegmentFormat } from '../format/ContentModelSegmentFormat'; +import type { ContentModelSegmentType } from '../enum/SegmentType'; +import type { ContentModelWithFormat } from '../format/ContentModelWithFormat'; +import type { Selectable } from '../selection/Selectable'; /** * Base type of Content Model Segment diff --git a/packages-content-model/roosterjs-content-model-types/lib/segment/ContentModelSelectionMarker.ts b/packages-content-model/roosterjs-content-model-types/lib/segment/ContentModelSelectionMarker.ts index c097ad3bd38..af6cf9fae6d 100644 --- a/packages-content-model/roosterjs-content-model-types/lib/segment/ContentModelSelectionMarker.ts +++ b/packages-content-model/roosterjs-content-model-types/lib/segment/ContentModelSelectionMarker.ts @@ -1,4 +1,4 @@ -import { ContentModelSegmentBase } from './ContentModelSegmentBase'; +import type { ContentModelSegmentBase } from './ContentModelSegmentBase'; /** * Content Model of Selection Marker diff --git a/packages-content-model/roosterjs-content-model-types/lib/segment/ContentModelText.ts b/packages-content-model/roosterjs-content-model-types/lib/segment/ContentModelText.ts index 2d06d97ed76..5cd7c79f19b 100644 --- a/packages-content-model/roosterjs-content-model-types/lib/segment/ContentModelText.ts +++ b/packages-content-model/roosterjs-content-model-types/lib/segment/ContentModelText.ts @@ -1,4 +1,4 @@ -import { ContentModelSegmentBase } from './ContentModelSegmentBase'; +import type { ContentModelSegmentBase } from './ContentModelSegmentBase'; /** * Content Model for Text diff --git a/packages-content-model/roosterjs-content-model-types/lib/selection/DOMSelection.ts b/packages-content-model/roosterjs-content-model-types/lib/selection/DOMSelection.ts new file mode 100644 index 00000000000..c5401fd11c2 --- /dev/null +++ b/packages-content-model/roosterjs-content-model-types/lib/selection/DOMSelection.ts @@ -0,0 +1,76 @@ +/** + * Type of DOM selection, it can be one of the 3 below: + * range: A regular selection that can be represented by a DOM Range object with start and end container and offset + * table: A table selection that can be defined using the Table element and first/last row and column number. Table selection can + * cover multiple table cells, it does not need to be continuous, but it should be a rectangle + * image: A image selection that can be defined with an image element. Not like a regular range selection with an image, image selection + * is created when user single click the image, then we will show a selection border rather the blue background to show the selection + */ +export type SelectionType = 'range' | 'table' | 'image'; + +/** + * Base type of Selection + */ +export interface SelectionBase { + /** + * Type of this selection + */ + type: T; +} + +/** + * A regular selection that can be represented by a DOM Range object with start and end container and offset + */ +export interface RangeSelection extends SelectionBase<'range'> { + /** + * The DOM Range of this selection + */ + range: Range; +} + +/** + * image: A image selection that can be defined with an image element. Not like a regular range selection with an image, image selection + * is created when user single click the image, then we will show a selection border rather the blue background to show the selection + */ +export interface ImageSelection extends SelectionBase<'image'> { + /** + * The image that this selection is representing + */ + image: HTMLImageElement; +} + +/** + * A table selection that can be defined using the Table element and first/last row and column number. Table selection can + * cover multiple table cells, it does not need to be continuous, but it should be a rectangle + */ +export interface TableSelection extends SelectionBase<'table'> { + /** + * The table that this selection is representing + */ + table: HTMLTableElement; + + /** + * Number of first selected row + */ + firstRow: number; + + /** + * Number of last selected row + */ + lastRow: number; + + /** + * Number of first selected column + */ + firstColumn: number; + + /** + * Number of last selected column + */ + lastColumn: number; +} + +/** + * The union type of 3 selection types + */ +export type DOMSelection = RangeSelection | ImageSelection | TableSelection; diff --git a/packages-content-model/roosterjs-content-model/lib/createContentModelEditor.ts b/packages-content-model/roosterjs-content-model/lib/createContentModelEditor.ts index 31251777052..9a14653ff5c 100644 --- a/packages-content-model/roosterjs-content-model/lib/createContentModelEditor.ts +++ b/packages-content-model/roosterjs-content-model/lib/createContentModelEditor.ts @@ -1,9 +1,8 @@ -import { EditorPlugin } from 'roosterjs-editor-types'; +import { ContentModelEditor, ContentModelPastePlugin } from 'roosterjs-content-model-editor'; import { getDarkColor } from 'roosterjs-color-utils'; -import { - ContentModelEditor, +import type { EditorPlugin } from 'roosterjs-editor-types'; +import type { ContentModelEditorOptions, - ContentModelPastePlugin, IContentModelEditor, } from 'roosterjs-content-model-editor'; diff --git a/packages-ui/roosterjs-react/lib/colorPicker/component/renderColorPicker.tsx b/packages-ui/roosterjs-react/lib/colorPicker/component/renderColorPicker.tsx index 0f62f8bf995..301f29ef687 100644 --- a/packages-ui/roosterjs-react/lib/colorPicker/component/renderColorPicker.tsx +++ b/packages-ui/roosterjs-react/lib/colorPicker/component/renderColorPicker.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; -import { IContextualMenuItem } from '@fluentui/react/lib/ContextualMenu'; import { mergeStyleSets } from '@fluentui/react/lib/Styling'; -import { ModeIndependentColor } from 'roosterjs-editor-types'; +import type { IContextualMenuItem } from '@fluentui/react/lib/ContextualMenu'; +import type { ModeIndependentColor } from 'roosterjs-editor-types'; const classNames = mergeStyleSets({ colorSquare: { diff --git a/packages-ui/roosterjs-react/lib/colorPicker/utils/backgroundColors.ts b/packages-ui/roosterjs-react/lib/colorPicker/utils/backgroundColors.ts index 025bbd147de..29b3f0cb381 100644 --- a/packages-ui/roosterjs-react/lib/colorPicker/utils/backgroundColors.ts +++ b/packages-ui/roosterjs-react/lib/colorPicker/utils/backgroundColors.ts @@ -1,5 +1,5 @@ -import { BackgroundColorKeys } from '../types/stringKeys'; -import { ModeIndependentColor } from 'roosterjs-editor-types'; +import type { BackgroundColorKeys } from '../types/stringKeys'; +import type { ModeIndependentColor } from 'roosterjs-editor-types'; /** * @internal diff --git a/packages-ui/roosterjs-react/lib/colorPicker/utils/textColors.ts b/packages-ui/roosterjs-react/lib/colorPicker/utils/textColors.ts index c2e61dac616..83d43daf175 100644 --- a/packages-ui/roosterjs-react/lib/colorPicker/utils/textColors.ts +++ b/packages-ui/roosterjs-react/lib/colorPicker/utils/textColors.ts @@ -1,5 +1,5 @@ -import { ModeIndependentColor } from 'roosterjs-editor-types'; -import { TextColorKeys } from '../types/stringKeys'; +import type { ModeIndependentColor } from 'roosterjs-editor-types'; +import type { TextColorKeys } from '../types/stringKeys'; /** * @internal diff --git a/packages-ui/roosterjs-react/lib/common/type/ReactEditorPlugin.ts b/packages-ui/roosterjs-react/lib/common/type/ReactEditorPlugin.ts index 65701d05f22..d5146ace3c0 100644 --- a/packages-ui/roosterjs-react/lib/common/type/ReactEditorPlugin.ts +++ b/packages-ui/roosterjs-react/lib/common/type/ReactEditorPlugin.ts @@ -1,5 +1,5 @@ -import UIUtilities from './UIUtilities'; -import { EditorPlugin } from 'roosterjs-editor-types'; +import type UIUtilities from './UIUtilities'; +import type { EditorPlugin } from 'roosterjs-editor-types'; /** * A sub interface of EditorPlugin to provide additional functionalities for rendering react component from the plugin diff --git a/packages-ui/roosterjs-react/lib/common/utils/createUIUtilities.tsx b/packages-ui/roosterjs-react/lib/common/utils/createUIUtilities.tsx index 5350bf6e40d..7c170b7036a 100644 --- a/packages-ui/roosterjs-react/lib/common/utils/createUIUtilities.tsx +++ b/packages-ui/roosterjs-react/lib/common/utils/createUIUtilities.tsx @@ -1,9 +1,10 @@ import * as React from 'react'; import * as ReactDOM from 'react-dom'; -import UIUtilities from '../type/UIUtilities'; import { getComputedStyles } from 'roosterjs-editor-dom'; -import { PartialTheme, ThemeProvider } from '@fluentui/react/lib/Theme'; +import { ThemeProvider } from '@fluentui/react/lib/Theme'; import { WindowProvider } from '@fluentui/react/lib/WindowProvider'; +import type UIUtilities from '../type/UIUtilities'; +import type { PartialTheme } from '@fluentui/react/lib/Theme'; /** * Create the UI Utilities object for plugins to render additional react components diff --git a/packages-ui/roosterjs-react/lib/common/utils/getLocalizedString.ts b/packages-ui/roosterjs-react/lib/common/utils/getLocalizedString.ts index 9763426bd0a..8cf13cc09e1 100644 --- a/packages-ui/roosterjs-react/lib/common/utils/getLocalizedString.ts +++ b/packages-ui/roosterjs-react/lib/common/utils/getLocalizedString.ts @@ -1,4 +1,4 @@ -import { LocalizedStrings } from '../type/LocalizedStrings'; +import type { LocalizedStrings } from '../type/LocalizedStrings'; /** * Get a localized string diff --git a/packages-ui/roosterjs-react/lib/common/utils/renderReactComponent.ts b/packages-ui/roosterjs-react/lib/common/utils/renderReactComponent.ts index d9bffdc3bfb..850190798f7 100644 --- a/packages-ui/roosterjs-react/lib/common/utils/renderReactComponent.ts +++ b/packages-ui/roosterjs-react/lib/common/utils/renderReactComponent.ts @@ -1,4 +1,4 @@ -import UIUtilities from '../type/UIUtilities'; +import type UIUtilities from '../type/UIUtilities'; /** * @internal diff --git a/packages-ui/roosterjs-react/lib/contextMenu/menus/createImageEditMenuProvider.tsx b/packages-ui/roosterjs-react/lib/contextMenu/menus/createImageEditMenuProvider.tsx index 63c493f4929..ba6ee5eb0ec 100644 --- a/packages-ui/roosterjs-react/lib/contextMenu/menus/createImageEditMenuProvider.tsx +++ b/packages-ui/roosterjs-react/lib/contextMenu/menus/createImageEditMenuProvider.tsx @@ -1,17 +1,14 @@ -import ContextMenuItem from '../types/ContextMenuItem'; import createContextMenuProvider from '../utils/createContextMenuProvider'; import showInputDialog from '../../inputDialog/utils/showInputDialog'; -import { DocumentCommand, EditorPlugin, IEditor, ImageEditOperation } from 'roosterjs-editor-types'; -import { ImageEditMenuItemStringKey } from '../types/ContextMenuItemStringKeys'; -import { LocalizedStrings } from '../../common/type/LocalizedStrings'; +import { canRegenerateImage, resetImage, resizeByPercentage } from 'roosterjs-editor-plugins'; +import { DocumentCommand, ImageEditOperation } from 'roosterjs-editor-types'; import { safeInstanceOf } from 'roosterjs-editor-dom'; import { setImageAltText } from 'roosterjs-editor-api'; -import { - canRegenerateImage, - ImageEdit, - resetImage, - resizeByPercentage, -} from 'roosterjs-editor-plugins'; +import type ContextMenuItem from '../types/ContextMenuItem'; +import type { EditorPlugin, IEditor } from 'roosterjs-editor-types'; +import type { ImageEditMenuItemStringKey } from '../types/ContextMenuItemStringKeys'; +import type { LocalizedStrings } from '../../common/type/LocalizedStrings'; +import type { ImageEdit } from 'roosterjs-editor-plugins'; const ImageAltTextMenuItem: ContextMenuItem = { key: 'menuNameImageAltText', diff --git a/packages-ui/roosterjs-react/lib/contextMenu/menus/createListEditMenuProvider.ts b/packages-ui/roosterjs-react/lib/contextMenu/menus/createListEditMenuProvider.ts index e29a0a43152..89e9c2ec562 100644 --- a/packages-ui/roosterjs-react/lib/contextMenu/menus/createListEditMenuProvider.ts +++ b/packages-ui/roosterjs-react/lib/contextMenu/menus/createListEditMenuProvider.ts @@ -1,11 +1,11 @@ -import ContextMenuItem from '../types/ContextMenuItem'; import createContextMenuProvider from '../utils/createContextMenuProvider'; import showInputDialog from '../../inputDialog/utils/showInputDialog'; -import { EditorPlugin, IEditor } from 'roosterjs-editor-types'; -import { ListNumberMenuItemStringKey } from '../types/ContextMenuItemStringKeys'; -import { LocalizedStrings } from '../../common/type/LocalizedStrings'; import { safeInstanceOf } from 'roosterjs-editor-dom'; import { setOrderedListNumbering } from 'roosterjs-editor-api'; +import type ContextMenuItem from '../types/ContextMenuItem'; +import type { EditorPlugin, IEditor } from 'roosterjs-editor-types'; +import type { ListNumberMenuItemStringKey } from '../types/ContextMenuItemStringKeys'; +import type { LocalizedStrings } from '../../common/type/LocalizedStrings'; const ListNumberResetMenuItem: ContextMenuItem = { key: 'menuNameListNumberReset', diff --git a/packages-ui/roosterjs-react/lib/contextMenu/menus/createTableEditMenuProvider.ts b/packages-ui/roosterjs-react/lib/contextMenu/menus/createTableEditMenuProvider.ts index ceae17a4e5c..20e0b639399 100644 --- a/packages-ui/roosterjs-react/lib/contextMenu/menus/createTableEditMenuProvider.ts +++ b/packages-ui/roosterjs-react/lib/contextMenu/menus/createTableEditMenuProvider.ts @@ -1,15 +1,15 @@ -import ContextMenuItem from '../types/ContextMenuItem'; import createContextMenuProvider from '../utils/createContextMenuProvider'; import { applyCellShading, editTable } from 'roosterjs-editor-api'; -import { EditorPlugin, IEditor, TableOperation } from 'roosterjs-editor-types'; -import { LocalizedStrings } from '../../common/type/LocalizedStrings'; -import { ModeIndependentColor } from 'roosterjs-editor-types'; import { renderColorPicker } from '../../colorPicker/component/renderColorPicker'; +import { TableOperation } from 'roosterjs-editor-types'; +import type ContextMenuItem from '../types/ContextMenuItem'; +import type { EditorPlugin, IEditor, ModeIndependentColor } from 'roosterjs-editor-types'; +import type { LocalizedStrings } from '../../common/type/LocalizedStrings'; import { getColorPickerContainerClassName, getColorPickerItemClassName, } from '../../colorPicker/utils/getClassNamesForColorPicker'; -import { +import type { TableEditMenuItemStringKey, TableEditInsertMenuItemStringKey, TableEditDeleteMenuItemStringKey, diff --git a/packages-ui/roosterjs-react/lib/contextMenu/plugin/createContextMenuPlugin.tsx b/packages-ui/roosterjs-react/lib/contextMenu/plugin/createContextMenuPlugin.tsx index dbfec75be62..0bfb267cce9 100644 --- a/packages-ui/roosterjs-react/lib/contextMenu/plugin/createContextMenuPlugin.tsx +++ b/packages-ui/roosterjs-react/lib/contextMenu/plugin/createContextMenuPlugin.tsx @@ -1,8 +1,9 @@ import * as React from 'react'; import { ContextMenu } from 'roosterjs-editor-plugins'; -import { ContextualMenu, IContextualMenuItem } from '@fluentui/react/lib/ContextualMenu'; -import { ReactEditorPlugin, UIUtilities } from '../../common/index'; +import { ContextualMenu } from '@fluentui/react/lib/ContextualMenu'; import { renderReactComponent } from '../../common/utils/renderReactComponent'; +import type { IContextualMenuItem } from '@fluentui/react/lib/ContextualMenu'; +import type { ReactEditorPlugin, UIUtilities } from '../../common/index'; function normalizeItems(items: (IContextualMenuItem | null)[]) { let dividerKey = 0; diff --git a/packages-ui/roosterjs-react/lib/contextMenu/types/ContextMenuItem.ts b/packages-ui/roosterjs-react/lib/contextMenu/types/ContextMenuItem.ts index 9d3de3be3b0..b1728ac2bda 100644 --- a/packages-ui/roosterjs-react/lib/contextMenu/types/ContextMenuItem.ts +++ b/packages-ui/roosterjs-react/lib/contextMenu/types/ContextMenuItem.ts @@ -1,6 +1,6 @@ -import { IContextualMenuItem, IContextualMenuProps } from '@fluentui/react/lib/ContextualMenu'; -import { IEditor } from 'roosterjs-editor-types'; -import { LocalizedStrings, UIUtilities } from '../../common/index'; +import type { IContextualMenuItem, IContextualMenuProps } from '@fluentui/react/lib/ContextualMenu'; +import type { IEditor } from 'roosterjs-editor-types'; +import type { LocalizedStrings, UIUtilities } from '../../common/index'; /** * Represent a context menu item diff --git a/packages-ui/roosterjs-react/lib/contextMenu/types/ContextMenuItemStringKeys.ts b/packages-ui/roosterjs-react/lib/contextMenu/types/ContextMenuItemStringKeys.ts index e8f4d582714..bd0f74fdf80 100644 --- a/packages-ui/roosterjs-react/lib/contextMenu/types/ContextMenuItemStringKeys.ts +++ b/packages-ui/roosterjs-react/lib/contextMenu/types/ContextMenuItemStringKeys.ts @@ -1,5 +1,5 @@ -import { BackgroundColorKeys } from '../../colorPicker/index'; -import { +import type { BackgroundColorKeys } from '../../colorPicker/index'; +import type { CancelButtonStringKey, MenuItemSplitterKey0, OkButtonStringKey, diff --git a/packages-ui/roosterjs-react/lib/contextMenu/utils/createContextMenuProvider.ts b/packages-ui/roosterjs-react/lib/contextMenu/utils/createContextMenuProvider.ts index f541be1c2f9..375c8e92f0e 100644 --- a/packages-ui/roosterjs-react/lib/contextMenu/utils/createContextMenuProvider.ts +++ b/packages-ui/roosterjs-react/lib/contextMenu/utils/createContextMenuProvider.ts @@ -1,9 +1,9 @@ -import ContextMenuItem from '../types/ContextMenuItem'; import getLocalizedString from '../../common/utils/getLocalizedString'; -import { ContextMenuProvider, EditorPlugin, IEditor } from 'roosterjs-editor-types'; import { getObjectKeys } from 'roosterjs-editor-dom'; -import { IContextualMenuItem } from '@fluentui/react/lib/ContextualMenu'; -import { LocalizedStrings, ReactEditorPlugin, UIUtilities } from '../../common/index'; +import type ContextMenuItem from '../types/ContextMenuItem'; +import type { ContextMenuProvider, EditorPlugin, IEditor } from 'roosterjs-editor-types'; +import type { IContextualMenuItem } from '@fluentui/react/lib/ContextualMenu'; +import type { LocalizedStrings, ReactEditorPlugin, UIUtilities } from '../../common/index'; /** * A plugin of editor to provide context menu items diff --git a/packages-ui/roosterjs-react/lib/emoji/components/EmojiIcon.tsx b/packages-ui/roosterjs-react/lib/emoji/components/EmojiIcon.tsx index 0d8cd114d4a..20f56060a01 100644 --- a/packages-ui/roosterjs-react/lib/emoji/components/EmojiIcon.tsx +++ b/packages-ui/roosterjs-react/lib/emoji/components/EmojiIcon.tsx @@ -1,8 +1,8 @@ import * as React from 'react'; import { css } from '@fluentui/react/lib/Utilities'; -import { Emoji } from '../type/Emoji'; -import { EmojiPaneStyle } from '../type/EmojiPaneStyles'; -import { IProcessedStyleSet, IStyleSet } from '@fluentui/react/lib/Styling'; +import type { Emoji } from '../type/Emoji'; +import type { EmojiPaneStyle } from '../type/EmojiPaneStyles'; +import type { IProcessedStyleSet, IStyleSet } from '@fluentui/react/lib/Styling'; /** * @internal * Emoji icon data diff --git a/packages-ui/roosterjs-react/lib/emoji/components/EmojiNavBar.tsx b/packages-ui/roosterjs-react/lib/emoji/components/EmojiNavBar.tsx index 2c88e1c1286..4a4d4dc787c 100644 --- a/packages-ui/roosterjs-react/lib/emoji/components/EmojiNavBar.tsx +++ b/packages-ui/roosterjs-react/lib/emoji/components/EmojiNavBar.tsx @@ -1,12 +1,13 @@ import * as React from 'react'; import { css } from '@fluentui/react/lib/Utilities'; -import { EmojiFabricIconCharacterMap, EmojiFamilyKeys, EmojiList } from '../utils/emojiList'; -import { EmojiPaneStyle } from '../type/EmojiPaneStyles'; +import { EmojiFabricIconCharacterMap, EmojiList } from '../utils/emojiList'; import { FocusZone, FocusZoneDirection } from '@fluentui/react/lib/FocusZone'; import { getObjectKeys } from 'roosterjs-editor-dom'; import { Icon } from '@fluentui/react/lib/Icon'; -import { IProcessedStyleSet, IStyleSet } from '@fluentui/react/lib/Styling'; import { TooltipHost } from '@fluentui/react/lib/Tooltip'; +import type { EmojiFamilyKeys } from '../utils/emojiList'; +import type { EmojiPaneStyle } from '../type/EmojiPaneStyles'; +import type { IProcessedStyleSet, IStyleSet } from '@fluentui/react/lib/Styling'; /** * @internal diff --git a/packages-ui/roosterjs-react/lib/emoji/components/EmojiPane.tsx b/packages-ui/roosterjs-react/lib/emoji/components/EmojiPane.tsx index aee2375e147..abaa816b38d 100644 --- a/packages-ui/roosterjs-react/lib/emoji/components/EmojiPane.tsx +++ b/packages-ui/roosterjs-react/lib/emoji/components/EmojiPane.tsx @@ -1,19 +1,25 @@ import * as React from 'react'; -import EmojiIcon, { EmojiIconProps } from './EmojiIcon'; -import EmojiNavBar, { EmojiNavBarProps } from './EmojiNavBar'; -import EmojiStatusBar, { EmojiStatusBarProps } from './EmojiStatusBar'; -import { Callout, DirectionalHint, ICalloutProps } from '@fluentui/react/lib/Callout'; +import EmojiIcon from './EmojiIcon'; +import EmojiNavBar from './EmojiNavBar'; +import EmojiStatusBar from './EmojiStatusBar'; +import { Callout, DirectionalHint } from '@fluentui/react/lib/Callout'; import { CommonEmojis, EmojiFamilyKeys, EmojiList, MoreEmoji } from '../utils/emojiList'; -import { css, KeyCodes } from '@fluentui/react/lib/Utilities'; -import { Emoji } from '../type/Emoji'; -import { EmojiStringKeys } from '../type/EmojiStringKeys'; import { FocusZone } from '@fluentui/react/lib/FocusZone'; -import { getLocalizedString, LocalizedStrings } from '../../common/index'; -import { ITextField, TextField } from '@fluentui/react/lib/TextField'; -import { memoizeFunction } from '@fluentui/react/lib/Utilities'; +import { getLocalizedString } from '../../common/index'; import { mergeStyleSets } from '@fluentui/react/lib/Styling'; import { searchEmojis } from '../utils/searchEmojis'; -import { Theme, useTheme } from '@fluentui/react/lib/Theme'; +import { TextField } from '@fluentui/react/lib/TextField'; +import { useTheme } from '@fluentui/react/lib/Theme'; +import { css, KeyCodes, memoizeFunction } from '@fluentui/react/lib/Utilities'; +import type { EmojiIconProps } from './EmojiIcon'; +import type { EmojiNavBarProps } from './EmojiNavBar'; +import type { EmojiStatusBarProps } from './EmojiStatusBar'; +import type { ICalloutProps } from '@fluentui/react/lib/Callout'; +import type { Emoji } from '../type/Emoji'; +import type { EmojiStringKeys } from '../type/EmojiStringKeys'; +import type { LocalizedStrings } from '../../common/index'; +import type { ITextField } from '@fluentui/react/lib/TextField'; +import type { Theme } from '@fluentui/react/lib/Theme'; // "When a div contains an element that is bigger (either taller or wider) than the parent and has the property // overflow-x or overflow-y set to any value, then it can receive the focus." diff --git a/packages-ui/roosterjs-react/lib/emoji/components/EmojiStatusBar.tsx b/packages-ui/roosterjs-react/lib/emoji/components/EmojiStatusBar.tsx index 1a5b99d56e1..36092f62025 100644 --- a/packages-ui/roosterjs-react/lib/emoji/components/EmojiStatusBar.tsx +++ b/packages-ui/roosterjs-react/lib/emoji/components/EmojiStatusBar.tsx @@ -1,8 +1,8 @@ import * as React from 'react'; -import { Emoji } from '../type/Emoji'; -import { EmojiPaneStyle } from '../type/EmojiPaneStyles'; -import { IProcessedStyleSet, IStyleSet } from '@fluentui/react/lib/Styling'; import { TooltipHost, TooltipOverflowMode } from '@fluentui/react/lib/Tooltip'; +import type { Emoji } from '../type/Emoji'; +import type { EmojiPaneStyle } from '../type/EmojiPaneStyles'; +import type { IProcessedStyleSet, IStyleSet } from '@fluentui/react/lib/Styling'; /** * @internal * Emoji Status Bar data diff --git a/packages-ui/roosterjs-react/lib/emoji/components/showEmojiCallout.tsx b/packages-ui/roosterjs-react/lib/emoji/components/showEmojiCallout.tsx index cdfa1ab0365..329e8df55bf 100644 --- a/packages-ui/roosterjs-react/lib/emoji/components/showEmojiCallout.tsx +++ b/packages-ui/roosterjs-react/lib/emoji/components/showEmojiCallout.tsx @@ -1,10 +1,11 @@ import * as React from 'react'; import { Callout, DirectionalHint } from '@fluentui/react/lib/Callout'; -import { Emoji } from '../type/Emoji'; -import { EmojiPane, showEmojiPane } from './EmojiPane'; -import { EmojiStringKeys } from '../type/EmojiStringKeys'; -import { LocalizedStrings, UIUtilities } from '../../common/index'; import { renderReactComponent } from '../../common/utils/renderReactComponent'; +import { showEmojiPane } from './EmojiPane'; +import type { Emoji } from '../type/Emoji'; +import type { EmojiPane } from './EmojiPane'; +import type { EmojiStringKeys } from '../type/EmojiStringKeys'; +import type { LocalizedStrings, UIUtilities } from '../../common/index'; /** * @internal diff --git a/packages-ui/roosterjs-react/lib/emoji/plugin/createEmojiPlugin.ts b/packages-ui/roosterjs-react/lib/emoji/plugin/createEmojiPlugin.ts index d45fe115c92..8baca6c4770 100644 --- a/packages-ui/roosterjs-react/lib/emoji/plugin/createEmojiPlugin.ts +++ b/packages-ui/roosterjs-react/lib/emoji/plugin/createEmojiPlugin.ts @@ -1,27 +1,27 @@ -import showEmojiCallout, { EmojiICallout } from '../components/showEmojiCallout'; -import { Emoji } from '../type/Emoji'; -import { EmojiPane } from '../components/EmojiPane'; -import { EmojiStringKeys } from '../type/EmojiStringKeys'; +import * as React from 'react'; +import showEmojiCallout from '../components/showEmojiCallout'; import { isModifierKey } from 'roosterjs-editor-dom'; import { KeyCodes } from '@fluentui/react/lib/Utilities'; -import { LocalizedStrings, ReactEditorPlugin, UIUtilities } from '../../common/index'; import { MoreEmoji } from '../utils/emojiList'; import { replaceWithNode } from 'roosterjs-editor-api'; -import { +import type { EmojiICallout } from '../components/showEmojiCallout'; +import type { Emoji } from '../type/Emoji'; +import type { EmojiPane } from '../components/EmojiPane'; +import type { EmojiStringKeys } from '../type/EmojiStringKeys'; +import type { LocalizedStrings, ReactEditorPlugin, UIUtilities } from '../../common/index'; +import type { IEditor, PluginDomEvent, PluginEvent, - PluginEventType, PluginKeyboardEvent, PluginKeyDownEvent, - PositionType, } from 'roosterjs-editor-types'; +import { PluginEventType, PositionType } from 'roosterjs-editor-types'; import { EmojiDescriptionStrings, EmojiFamilyStrings, EmojiKeywordStrings, } from '../type/EmojiStrings'; -import * as React from 'react'; const KEYCODE_COLON = 186; const KEYCODE_COLON_FIREFOX = 59; diff --git a/packages-ui/roosterjs-react/lib/emoji/type/EmojiPaneStyles.ts b/packages-ui/roosterjs-react/lib/emoji/type/EmojiPaneStyles.ts index 62d84021d47..0b4c32f9606 100644 --- a/packages-ui/roosterjs-react/lib/emoji/type/EmojiPaneStyles.ts +++ b/packages-ui/roosterjs-react/lib/emoji/type/EmojiPaneStyles.ts @@ -1,4 +1,4 @@ -import { IStyleSet } from '@fluentui/react/lib/Styling'; +import type { IStyleSet } from '@fluentui/react/lib/Styling'; /** * @internal * EmojiPane Style classes diff --git a/packages-ui/roosterjs-react/lib/emoji/utils/emojiList.ts b/packages-ui/roosterjs-react/lib/emoji/utils/emojiList.ts index 1032980c877..151c38ef66c 100644 --- a/packages-ui/roosterjs-react/lib/emoji/utils/emojiList.ts +++ b/packages-ui/roosterjs-react/lib/emoji/utils/emojiList.ts @@ -1,5 +1,5 @@ -import { Emoji } from '../type/Emoji'; import { getObjectKeys } from 'roosterjs-editor-dom'; +import type { Emoji } from '../type/Emoji'; const Common1 = createEmoji('1f60a', ':) :-)'); const common2 = createEmoji('1f609', ';) ;-)'); diff --git a/packages-ui/roosterjs-react/lib/emoji/utils/searchEmojis.ts b/packages-ui/roosterjs-react/lib/emoji/utils/searchEmojis.ts index f4657402cfa..804bcb12032 100644 --- a/packages-ui/roosterjs-react/lib/emoji/utils/searchEmojis.ts +++ b/packages-ui/roosterjs-react/lib/emoji/utils/searchEmojis.ts @@ -1,5 +1,5 @@ -import { Emoji } from '../type/Emoji'; import { forEachEmoji } from './emojiList'; +import type { Emoji } from '../type/Emoji'; /** * @internal diff --git a/packages-ui/roosterjs-react/lib/inputDialog/component/InputDialog.tsx b/packages-ui/roosterjs-react/lib/inputDialog/component/InputDialog.tsx index daf7408f15a..816c8add846 100644 --- a/packages-ui/roosterjs-react/lib/inputDialog/component/InputDialog.tsx +++ b/packages-ui/roosterjs-react/lib/inputDialog/component/InputDialog.tsx @@ -1,15 +1,15 @@ import * as React from 'react'; -import DialogItem from '../type/DialogItem'; import InputDialogItem from './InputDialogItem'; -import { +import { DefaultButton, PrimaryButton } from '@fluentui/react/lib/Button'; +import { Dialog, DialogFooter, DialogType } from '@fluentui/react/lib/Dialog'; +import { getLocalizedString } from '../../common/index'; +import { getObjectKeys } from 'roosterjs-editor-dom'; +import type DialogItem from '../type/DialogItem'; +import type { CancelButtonStringKey, - getLocalizedString, LocalizedStrings, OkButtonStringKey, } from '../../common/index'; -import { DefaultButton, PrimaryButton } from '@fluentui/react/lib/Button'; -import { Dialog, DialogFooter, DialogType } from '@fluentui/react/lib/Dialog'; -import { getObjectKeys } from 'roosterjs-editor-dom'; /** * @internal diff --git a/packages-ui/roosterjs-react/lib/inputDialog/component/InputDialogItem.tsx b/packages-ui/roosterjs-react/lib/inputDialog/component/InputDialogItem.tsx index 37ae8528491..fd6fefae160 100644 --- a/packages-ui/roosterjs-react/lib/inputDialog/component/InputDialogItem.tsx +++ b/packages-ui/roosterjs-react/lib/inputDialog/component/InputDialogItem.tsx @@ -1,9 +1,10 @@ import * as React from 'react'; -import DialogItem from '../type/DialogItem'; -import { getLocalizedString, LocalizedStrings } from '../../common/index'; +import { getLocalizedString } from '../../common/index'; import { Keys } from 'roosterjs-editor-types'; import { mergeStyleSets } from '@fluentui/react/lib/Styling'; import { TextField } from '@fluentui/react/lib/TextField'; +import type DialogItem from '../type/DialogItem'; +import type { LocalizedStrings } from '../../common/index'; /** * @internal diff --git a/packages-ui/roosterjs-react/lib/inputDialog/utils/showInputDialog.tsx b/packages-ui/roosterjs-react/lib/inputDialog/utils/showInputDialog.tsx index 6a7ea3cee78..ecfe9b080fd 100644 --- a/packages-ui/roosterjs-react/lib/inputDialog/utils/showInputDialog.tsx +++ b/packages-ui/roosterjs-react/lib/inputDialog/utils/showInputDialog.tsx @@ -1,8 +1,8 @@ import * as React from 'react'; -import DialogItem from '../type/DialogItem'; import InputDialog from '../component/InputDialog'; import { renderReactComponent } from '../../common/utils/renderReactComponent'; -import { +import type DialogItem from '../type/DialogItem'; +import type { CancelButtonStringKey, LocalizedStrings, OkButtonStringKey, diff --git a/packages-ui/roosterjs-react/lib/pasteOptions/component/showPasteOptionPane.tsx b/packages-ui/roosterjs-react/lib/pasteOptions/component/showPasteOptionPane.tsx index 7a705867c7d..0e3c35a37e1 100644 --- a/packages-ui/roosterjs-react/lib/pasteOptions/component/showPasteOptionPane.tsx +++ b/packages-ui/roosterjs-react/lib/pasteOptions/component/showPasteOptionPane.tsx @@ -1,14 +1,16 @@ import * as React from 'react'; import { ButtonKeys, Buttons } from '../utils/buttons'; import { Callout, DirectionalHint } from '@fluentui/react/lib/Callout'; -import { getLocalizedString, LocalizedStrings, UIUtilities } from '../../common/index'; +import { getLocalizedString } from '../../common/index'; import { getObjectKeys, getPositionRect } from 'roosterjs-editor-dom'; import { Icon } from '@fluentui/react/lib/Icon'; import { IconButton } from '@fluentui/react/lib/Button'; import { memoizeFunction } from '@fluentui/react/lib/Utilities'; import { mergeStyleSets } from '@fluentui/react/lib/Styling'; import { renderReactComponent } from '../../common/utils/renderReactComponent'; -import { Theme, useTheme } from '@fluentui/react/lib/Theme'; +import { useTheme } from '@fluentui/react/lib/Theme'; +import type { LocalizedStrings, UIUtilities } from '../../common/index'; +import type { Theme } from '@fluentui/react/lib/Theme'; import type { PasteOptionButtonKeys, PasteOptionStringKeys } from '../type/PasteOptionStringKeys'; import type { NodePosition } from 'roosterjs-editor-types'; diff --git a/packages-ui/roosterjs-react/lib/pasteOptions/plugin/createPasteOptionPlugin.ts b/packages-ui/roosterjs-react/lib/pasteOptions/plugin/createPasteOptionPlugin.ts index de27caa574e..b2bb9e32d7e 100644 --- a/packages-ui/roosterjs-react/lib/pasteOptions/plugin/createPasteOptionPlugin.ts +++ b/packages-ui/roosterjs-react/lib/pasteOptions/plugin/createPasteOptionPlugin.ts @@ -1,9 +1,11 @@ import * as React from 'react'; -import showPasteOptionPane, { PasteOptionPane } from '../component/showPasteOptionPane'; +import showPasteOptionPane from '../component/showPasteOptionPane'; import { ButtonKeys, Buttons } from '../utils/buttons'; -import { ClipboardData, IEditor, Keys, PluginEvent, PluginEventType } from 'roosterjs-editor-types'; -import { LocalizedStrings, ReactEditorPlugin, UIUtilities } from '../../common/index'; -import { PasteOptionButtonKeys, PasteOptionStringKeys } from '../type/PasteOptionStringKeys'; +import { Keys, PluginEventType } from 'roosterjs-editor-types'; +import type { PasteOptionPane } from '../component/showPasteOptionPane'; +import type { ClipboardData, IEditor, PluginEvent } from 'roosterjs-editor-types'; +import type { LocalizedStrings, ReactEditorPlugin, UIUtilities } from '../../common/index'; +import type { PasteOptionButtonKeys, PasteOptionStringKeys } from '../type/PasteOptionStringKeys'; class PasteOptionPlugin implements ReactEditorPlugin { private clipboardData: ClipboardData | null = null; diff --git a/packages-ui/roosterjs-react/lib/pasteOptions/utils/buttons.ts b/packages-ui/roosterjs-react/lib/pasteOptions/utils/buttons.ts index 951b9c17884..8549bdd6d7d 100644 --- a/packages-ui/roosterjs-react/lib/pasteOptions/utils/buttons.ts +++ b/packages-ui/roosterjs-react/lib/pasteOptions/utils/buttons.ts @@ -1,4 +1,4 @@ -import { PasteOptionButtonKeys } from '../type/PasteOptionStringKeys'; +import type { PasteOptionButtonKeys } from '../type/PasteOptionStringKeys'; /** * @internal diff --git a/packages-ui/roosterjs-react/lib/ribbon/component/Ribbon.tsx b/packages-ui/roosterjs-react/lib/ribbon/component/Ribbon.tsx index 024fd1fa9e3..ac6d9ff14b1 100644 --- a/packages-ui/roosterjs-react/lib/ribbon/component/Ribbon.tsx +++ b/packages-ui/roosterjs-react/lib/ribbon/component/Ribbon.tsx @@ -1,14 +1,18 @@ import * as React from 'react'; import getLocalizedString from '../../common/utils/getLocalizedString'; -import RibbonButton from '../type/RibbonButton'; -import RibbonProps from '../type/RibbonProps'; -import { CommandBar, ICommandBarItemProps } from '@fluentui/react/lib/CommandBar'; +import { CommandBar } from '@fluentui/react/lib/CommandBar'; import { FocusZoneDirection } from '@fluentui/react/lib/FocusZone'; -import { FormatState } from 'roosterjs-editor-types'; import { getObjectKeys } from 'roosterjs-editor-dom'; -import { IContextualMenuItem, IContextualMenuItemProps } from '@fluentui/react/lib/ContextualMenu'; import { mergeStyles } from '@fluentui/react/lib/Styling'; import { moreCommands } from './buttons/moreCommands'; +import type RibbonButton from '../type/RibbonButton'; +import type RibbonProps from '../type/RibbonProps'; +import type { ICommandBarItemProps } from '@fluentui/react/lib/CommandBar'; +import type { FormatState } from 'roosterjs-editor-types'; +import type { + IContextualMenuItem, + IContextualMenuItemProps, +} from '@fluentui/react/lib/ContextualMenu'; import type { IRenderFunction } from '@fluentui/react/lib/Utilities'; const ribbonClassName = mergeStyles({ diff --git a/packages-ui/roosterjs-react/lib/ribbon/component/buttons/alignCenter.ts b/packages-ui/roosterjs-react/lib/ribbon/component/buttons/alignCenter.ts index 07335264194..ba4a3bdba86 100644 --- a/packages-ui/roosterjs-react/lib/ribbon/component/buttons/alignCenter.ts +++ b/packages-ui/roosterjs-react/lib/ribbon/component/buttons/alignCenter.ts @@ -1,7 +1,7 @@ -import RibbonButton from '../../type/RibbonButton'; -import { AlignCenterButtonStringKey } from '../../type/RibbonButtonStringKeys'; import { Alignment } from 'roosterjs-editor-types'; import { setAlignment } from 'roosterjs-editor-api'; +import type RibbonButton from '../../type/RibbonButton'; +import type { AlignCenterButtonStringKey } from '../../type/RibbonButtonStringKeys'; /** * @internal diff --git a/packages-ui/roosterjs-react/lib/ribbon/component/buttons/alignLeft.ts b/packages-ui/roosterjs-react/lib/ribbon/component/buttons/alignLeft.ts index 267240632eb..0795e7c963a 100644 --- a/packages-ui/roosterjs-react/lib/ribbon/component/buttons/alignLeft.ts +++ b/packages-ui/roosterjs-react/lib/ribbon/component/buttons/alignLeft.ts @@ -1,7 +1,7 @@ -import RibbonButton from '../../type/RibbonButton'; -import { AlignLeftButtonStringKey } from '../../type/RibbonButtonStringKeys'; import { Alignment } from 'roosterjs-editor-types'; import { setAlignment } from 'roosterjs-editor-api'; +import type RibbonButton from '../../type/RibbonButton'; +import type { AlignLeftButtonStringKey } from '../../type/RibbonButtonStringKeys'; /** * @internal diff --git a/packages-ui/roosterjs-react/lib/ribbon/component/buttons/alignRight.ts b/packages-ui/roosterjs-react/lib/ribbon/component/buttons/alignRight.ts index 22afbc732b6..ca91ca320e6 100644 --- a/packages-ui/roosterjs-react/lib/ribbon/component/buttons/alignRight.ts +++ b/packages-ui/roosterjs-react/lib/ribbon/component/buttons/alignRight.ts @@ -1,7 +1,7 @@ -import RibbonButton from '../../type/RibbonButton'; import { Alignment } from 'roosterjs-editor-types'; -import { AlignRightButtonStringKey } from '../../type/RibbonButtonStringKeys'; import { setAlignment } from 'roosterjs-editor-api'; +import type RibbonButton from '../../type/RibbonButton'; +import type { AlignRightButtonStringKey } from '../../type/RibbonButtonStringKeys'; /** * @internal diff --git a/packages-ui/roosterjs-react/lib/ribbon/component/buttons/backgroundColor.ts b/packages-ui/roosterjs-react/lib/ribbon/component/buttons/backgroundColor.ts index a3920dfe4bb..aa64f22fb3b 100644 --- a/packages-ui/roosterjs-react/lib/ribbon/component/buttons/backgroundColor.ts +++ b/packages-ui/roosterjs-react/lib/ribbon/component/buttons/backgroundColor.ts @@ -1,7 +1,7 @@ -import RibbonButton from '../../type/RibbonButton'; -import { BackgroundColorButtonStringKey } from '../../type/RibbonButtonStringKeys'; import { renderColorPicker } from '../../../colorPicker/component/renderColorPicker'; import { setBackgroundColor } from 'roosterjs-editor-api'; +import type RibbonButton from '../../type/RibbonButton'; +import type { BackgroundColorButtonStringKey } from '../../type/RibbonButtonStringKeys'; import { getColorPickerContainerClassName, getColorPickerItemClassName, diff --git a/packages-ui/roosterjs-react/lib/ribbon/component/buttons/bold.ts b/packages-ui/roosterjs-react/lib/ribbon/component/buttons/bold.ts index ae5c5a994a3..98b940d56df 100644 --- a/packages-ui/roosterjs-react/lib/ribbon/component/buttons/bold.ts +++ b/packages-ui/roosterjs-react/lib/ribbon/component/buttons/bold.ts @@ -1,6 +1,6 @@ -import RibbonButton from '../../type/RibbonButton'; -import { BoldButtonStringKey } from '../../type/RibbonButtonStringKeys'; import { toggleBold } from 'roosterjs-editor-api'; +import type RibbonButton from '../../type/RibbonButton'; +import type { BoldButtonStringKey } from '../../type/RibbonButtonStringKeys'; /** * @internal diff --git a/packages-ui/roosterjs-react/lib/ribbon/component/buttons/bulletedList.ts b/packages-ui/roosterjs-react/lib/ribbon/component/buttons/bulletedList.ts index b46621fd2e3..f654427d281 100644 --- a/packages-ui/roosterjs-react/lib/ribbon/component/buttons/bulletedList.ts +++ b/packages-ui/roosterjs-react/lib/ribbon/component/buttons/bulletedList.ts @@ -1,6 +1,6 @@ -import RibbonButton from '../../type/RibbonButton'; -import { BulletedListButtonStringKey } from '../../type/RibbonButtonStringKeys'; import { toggleBullet } from 'roosterjs-editor-api'; +import type RibbonButton from '../../type/RibbonButton'; +import type { BulletedListButtonStringKey } from '../../type/RibbonButtonStringKeys'; /** * @internal diff --git a/packages-ui/roosterjs-react/lib/ribbon/component/buttons/clearFormat.ts b/packages-ui/roosterjs-react/lib/ribbon/component/buttons/clearFormat.ts index 3355d5e7095..a27bffdcfd4 100644 --- a/packages-ui/roosterjs-react/lib/ribbon/component/buttons/clearFormat.ts +++ b/packages-ui/roosterjs-react/lib/ribbon/component/buttons/clearFormat.ts @@ -1,7 +1,7 @@ -import RibbonButton from '../../type/RibbonButton'; import { clearFormat as clearFormatApi } from 'roosterjs-editor-api'; -import { ClearFormatButtonStringKey } from '../../type/RibbonButtonStringKeys'; import { ClearFormatMode } from 'roosterjs-editor-types'; +import type RibbonButton from '../../type/RibbonButton'; +import type { ClearFormatButtonStringKey } from '../../type/RibbonButtonStringKeys'; /** * @internal diff --git a/packages-ui/roosterjs-react/lib/ribbon/component/buttons/code.ts b/packages-ui/roosterjs-react/lib/ribbon/component/buttons/code.ts index 5dadb5ad747..557315641d9 100644 --- a/packages-ui/roosterjs-react/lib/ribbon/component/buttons/code.ts +++ b/packages-ui/roosterjs-react/lib/ribbon/component/buttons/code.ts @@ -1,6 +1,6 @@ -import RibbonButton from '../../type/RibbonButton'; -import { CodeButtonStringKey } from '../../type/RibbonButtonStringKeys'; import { toggleCodeBlock } from 'roosterjs-editor-api'; +import type RibbonButton from '../../type/RibbonButton'; +import type { CodeButtonStringKey } from '../../type/RibbonButtonStringKeys'; /** * @internal diff --git a/packages-ui/roosterjs-react/lib/ribbon/component/buttons/decreaseFontSize.ts b/packages-ui/roosterjs-react/lib/ribbon/component/buttons/decreaseFontSize.ts index 11bb2589cb7..6c67997ac99 100644 --- a/packages-ui/roosterjs-react/lib/ribbon/component/buttons/decreaseFontSize.ts +++ b/packages-ui/roosterjs-react/lib/ribbon/component/buttons/decreaseFontSize.ts @@ -1,7 +1,7 @@ -import RibbonButton from '../../type/RibbonButton'; import { changeFontSize } from 'roosterjs-editor-api'; -import { DecreaseFontSizeButtonStringKey } from '../../type/RibbonButtonStringKeys'; import { FontSizeChange } from 'roosterjs-editor-types'; +import type RibbonButton from '../../type/RibbonButton'; +import type { DecreaseFontSizeButtonStringKey } from '../../type/RibbonButtonStringKeys'; /** * @internal diff --git a/packages-ui/roosterjs-react/lib/ribbon/component/buttons/decreaseIndent.ts b/packages-ui/roosterjs-react/lib/ribbon/component/buttons/decreaseIndent.ts index d87a5fdb373..2debec2b05c 100644 --- a/packages-ui/roosterjs-react/lib/ribbon/component/buttons/decreaseIndent.ts +++ b/packages-ui/roosterjs-react/lib/ribbon/component/buttons/decreaseIndent.ts @@ -1,7 +1,7 @@ -import RibbonButton from '../../type/RibbonButton'; -import { DecreaseIndentButtonStringKey } from '../../type/RibbonButtonStringKeys'; import { Indentation } from 'roosterjs-editor-types'; import { setIndentation } from 'roosterjs-editor-api'; +import type RibbonButton from '../../type/RibbonButton'; +import type { DecreaseIndentButtonStringKey } from '../../type/RibbonButtonStringKeys'; /** * @internal diff --git a/packages-ui/roosterjs-react/lib/ribbon/component/buttons/font.ts b/packages-ui/roosterjs-react/lib/ribbon/component/buttons/font.ts index 929735e30cd..f2dd45e9f27 100644 --- a/packages-ui/roosterjs-react/lib/ribbon/component/buttons/font.ts +++ b/packages-ui/roosterjs-react/lib/ribbon/component/buttons/font.ts @@ -1,6 +1,6 @@ -import RibbonButton from '../../type/RibbonButton'; -import { FontButtonStringKey } from '../../type/RibbonButtonStringKeys'; import { setFontName } from 'roosterjs-editor-api'; +import type RibbonButton from '../../type/RibbonButton'; +import type { FontButtonStringKey } from '../../type/RibbonButtonStringKeys'; interface FontName { name: string; diff --git a/packages-ui/roosterjs-react/lib/ribbon/component/buttons/fontSize.ts b/packages-ui/roosterjs-react/lib/ribbon/component/buttons/fontSize.ts index 916a068bec2..8c42d79eaa3 100644 --- a/packages-ui/roosterjs-react/lib/ribbon/component/buttons/fontSize.ts +++ b/packages-ui/roosterjs-react/lib/ribbon/component/buttons/fontSize.ts @@ -1,6 +1,6 @@ -import RibbonButton from '../../type/RibbonButton'; import { FONT_SIZES, setFontSize } from 'roosterjs-editor-api'; -import { FontSizeButtonStringKey } from '../../type/RibbonButtonStringKeys'; +import type RibbonButton from '../../type/RibbonButton'; +import type { FontSizeButtonStringKey } from '../../type/RibbonButtonStringKeys'; /** * @internal diff --git a/packages-ui/roosterjs-react/lib/ribbon/component/buttons/heading.ts b/packages-ui/roosterjs-react/lib/ribbon/component/buttons/heading.ts index e9b51df3b61..f283f8965f0 100644 --- a/packages-ui/roosterjs-react/lib/ribbon/component/buttons/heading.ts +++ b/packages-ui/roosterjs-react/lib/ribbon/component/buttons/heading.ts @@ -1,7 +1,7 @@ -import RibbonButton from '../../type/RibbonButton'; import { getObjectKeys } from 'roosterjs-editor-dom'; -import { HeadingButtonStringKey } from '../../type/RibbonButtonStringKeys'; import { setHeadingLevel } from 'roosterjs-editor-api'; +import type RibbonButton from '../../type/RibbonButton'; +import type { HeadingButtonStringKey } from '../../type/RibbonButtonStringKeys'; const headings: Partial> = { buttonNameHeading1: 'Heading 1', diff --git a/packages-ui/roosterjs-react/lib/ribbon/component/buttons/increaseFontSize.ts b/packages-ui/roosterjs-react/lib/ribbon/component/buttons/increaseFontSize.ts index 8c65e2f32a5..de4c46813d1 100644 --- a/packages-ui/roosterjs-react/lib/ribbon/component/buttons/increaseFontSize.ts +++ b/packages-ui/roosterjs-react/lib/ribbon/component/buttons/increaseFontSize.ts @@ -1,7 +1,7 @@ -import RibbonButton from '../../type/RibbonButton'; import { changeFontSize } from 'roosterjs-editor-api'; import { FontSizeChange } from 'roosterjs-editor-types'; -import { IncreaseFontSizeButtonStringKey } from '../../type/RibbonButtonStringKeys'; +import type RibbonButton from '../../type/RibbonButton'; +import type { IncreaseFontSizeButtonStringKey } from '../../type/RibbonButtonStringKeys'; /** * @internal diff --git a/packages-ui/roosterjs-react/lib/ribbon/component/buttons/increaseIndent.ts b/packages-ui/roosterjs-react/lib/ribbon/component/buttons/increaseIndent.ts index 1149978d44e..2e788eb0426 100644 --- a/packages-ui/roosterjs-react/lib/ribbon/component/buttons/increaseIndent.ts +++ b/packages-ui/roosterjs-react/lib/ribbon/component/buttons/increaseIndent.ts @@ -1,7 +1,7 @@ -import RibbonButton from '../../type/RibbonButton'; -import { IncreaseIndentButtonStringKey } from '../../type/RibbonButtonStringKeys'; import { Indentation } from 'roosterjs-editor-types'; import { setIndentation } from 'roosterjs-editor-api'; +import type RibbonButton from '../../type/RibbonButton'; +import type { IncreaseIndentButtonStringKey } from '../../type/RibbonButtonStringKeys'; /** * @internal diff --git a/packages-ui/roosterjs-react/lib/ribbon/component/buttons/insertImage.ts b/packages-ui/roosterjs-react/lib/ribbon/component/buttons/insertImage.ts index 950f6b82256..81d65eb8f63 100644 --- a/packages-ui/roosterjs-react/lib/ribbon/component/buttons/insertImage.ts +++ b/packages-ui/roosterjs-react/lib/ribbon/component/buttons/insertImage.ts @@ -1,8 +1,8 @@ -import RibbonButton from '../../type/RibbonButton'; import { createElement } from 'roosterjs-editor-dom'; -import { CreateElementData } from 'roosterjs-editor-types'; import { insertImage as insertImageApi } from 'roosterjs-editor-api'; -import { InsertImageButtonStringKey } from '../../type/RibbonButtonStringKeys'; +import type RibbonButton from '../../type/RibbonButton'; +import type { CreateElementData } from 'roosterjs-editor-types'; +import type { InsertImageButtonStringKey } from '../../type/RibbonButtonStringKeys'; const FileInput: CreateElementData = { tag: 'input', diff --git a/packages-ui/roosterjs-react/lib/ribbon/component/buttons/insertLink.ts b/packages-ui/roosterjs-react/lib/ribbon/component/buttons/insertLink.ts index 885e811736c..1ea35d94d44 100644 --- a/packages-ui/roosterjs-react/lib/ribbon/component/buttons/insertLink.ts +++ b/packages-ui/roosterjs-react/lib/ribbon/component/buttons/insertLink.ts @@ -1,8 +1,8 @@ -import RibbonButton from '../../type/RibbonButton'; import showInputDialog from '../../../inputDialog/utils/showInputDialog'; import { createLink } from 'roosterjs-editor-api'; -import { InsertLinkButtonStringKey } from '../../type/RibbonButtonStringKeys'; import { QueryScope } from 'roosterjs-editor-types'; +import type RibbonButton from '../../type/RibbonButton'; +import type { InsertLinkButtonStringKey } from '../../type/RibbonButtonStringKeys'; /** * @internal diff --git a/packages-ui/roosterjs-react/lib/ribbon/component/buttons/insertTable.tsx b/packages-ui/roosterjs-react/lib/ribbon/component/buttons/insertTable.tsx index ba16ca9311c..e631eec81f5 100644 --- a/packages-ui/roosterjs-react/lib/ribbon/component/buttons/insertTable.tsx +++ b/packages-ui/roosterjs-react/lib/ribbon/component/buttons/insertTable.tsx @@ -1,11 +1,11 @@ import * as React from 'react'; -import RibbonButton from '../../type/RibbonButton'; import { FocusZone, FocusZoneDirection } from '@fluentui/react/lib/FocusZone'; -import { IContextualMenuItem } from '@fluentui/react/lib/ContextualMenu'; import { insertTable as insertTableApi } from 'roosterjs-editor-api'; -import { InsertTableButtonStringKey } from '../../type/RibbonButtonStringKeys'; import { mergeStyleSets } from '@fluentui/react/lib/Styling'; import { safeInstanceOf } from 'roosterjs-editor-dom'; +import type RibbonButton from '../../type/RibbonButton'; +import type { IContextualMenuItem } from '@fluentui/react/lib/ContextualMenu'; +import type { InsertTableButtonStringKey } from '../../type/RibbonButtonStringKeys'; const MaxRows = 10; const MaxCols = 10; diff --git a/packages-ui/roosterjs-react/lib/ribbon/component/buttons/italic.ts b/packages-ui/roosterjs-react/lib/ribbon/component/buttons/italic.ts index 6cc719e6b34..2dae7709c14 100644 --- a/packages-ui/roosterjs-react/lib/ribbon/component/buttons/italic.ts +++ b/packages-ui/roosterjs-react/lib/ribbon/component/buttons/italic.ts @@ -1,6 +1,6 @@ -import RibbonButton from '../../type/RibbonButton'; -import { ItalicButtonStringKey } from '../../type/RibbonButtonStringKeys'; import { toggleItalic } from 'roosterjs-editor-api'; +import type RibbonButton from '../../type/RibbonButton'; +import type { ItalicButtonStringKey } from '../../type/RibbonButtonStringKeys'; /** * @internal diff --git a/packages-ui/roosterjs-react/lib/ribbon/component/buttons/ltr.ts b/packages-ui/roosterjs-react/lib/ribbon/component/buttons/ltr.ts index a6714b68086..4114c4fc6eb 100644 --- a/packages-ui/roosterjs-react/lib/ribbon/component/buttons/ltr.ts +++ b/packages-ui/roosterjs-react/lib/ribbon/component/buttons/ltr.ts @@ -1,7 +1,7 @@ -import RibbonButton from '../../type/RibbonButton'; import { Direction } from 'roosterjs-editor-types'; -import { LtrButtonStringKey } from '../../type/RibbonButtonStringKeys'; import { setDirection } from 'roosterjs-editor-api'; +import type RibbonButton from '../../type/RibbonButton'; +import type { LtrButtonStringKey } from '../../type/RibbonButtonStringKeys'; /** * @internal diff --git a/packages-ui/roosterjs-react/lib/ribbon/component/buttons/moreCommands.ts b/packages-ui/roosterjs-react/lib/ribbon/component/buttons/moreCommands.ts index 831a9d0083f..327607e9d9e 100644 --- a/packages-ui/roosterjs-react/lib/ribbon/component/buttons/moreCommands.ts +++ b/packages-ui/roosterjs-react/lib/ribbon/component/buttons/moreCommands.ts @@ -1,5 +1,5 @@ -import RibbonButton from '../../type/RibbonButton'; -import { MoreCommandsButtonStringKey } from '../../type/RibbonButtonStringKeys'; +import type RibbonButton from '../../type/RibbonButton'; +import type { MoreCommandsButtonStringKey } from '../../type/RibbonButtonStringKeys'; /** * @internal diff --git a/packages-ui/roosterjs-react/lib/ribbon/component/buttons/numberedList.ts b/packages-ui/roosterjs-react/lib/ribbon/component/buttons/numberedList.ts index 0acad1e19e3..91f7d7762a4 100644 --- a/packages-ui/roosterjs-react/lib/ribbon/component/buttons/numberedList.ts +++ b/packages-ui/roosterjs-react/lib/ribbon/component/buttons/numberedList.ts @@ -1,6 +1,6 @@ -import RibbonButton from '../../type/RibbonButton'; -import { NumberedListButtonStringKey } from '../../type/RibbonButtonStringKeys'; import { toggleNumbering } from 'roosterjs-editor-api'; +import type RibbonButton from '../../type/RibbonButton'; +import type { NumberedListButtonStringKey } from '../../type/RibbonButtonStringKeys'; /** * @internal diff --git a/packages-ui/roosterjs-react/lib/ribbon/component/buttons/quote.ts b/packages-ui/roosterjs-react/lib/ribbon/component/buttons/quote.ts index a031a7b36a7..fe4a34d2a7e 100644 --- a/packages-ui/roosterjs-react/lib/ribbon/component/buttons/quote.ts +++ b/packages-ui/roosterjs-react/lib/ribbon/component/buttons/quote.ts @@ -1,6 +1,6 @@ -import RibbonButton from '../../type/RibbonButton'; -import { QuoteButtonStringKey } from '../../type/RibbonButtonStringKeys'; import { toggleBlockQuote } from 'roosterjs-editor-api'; +import type RibbonButton from '../../type/RibbonButton'; +import type { QuoteButtonStringKey } from '../../type/RibbonButtonStringKeys'; /** * @internal diff --git a/packages-ui/roosterjs-react/lib/ribbon/component/buttons/redo.ts b/packages-ui/roosterjs-react/lib/ribbon/component/buttons/redo.ts index 56c1a24ec94..f6a9d2abd44 100644 --- a/packages-ui/roosterjs-react/lib/ribbon/component/buttons/redo.ts +++ b/packages-ui/roosterjs-react/lib/ribbon/component/buttons/redo.ts @@ -1,5 +1,5 @@ -import RibbonButton from '../../type/RibbonButton'; -import { RedoButtonStringKey } from '../../type/RibbonButtonStringKeys'; +import type RibbonButton from '../../type/RibbonButton'; +import type { RedoButtonStringKey } from '../../type/RibbonButtonStringKeys'; /** * @internal diff --git a/packages-ui/roosterjs-react/lib/ribbon/component/buttons/removeLink.ts b/packages-ui/roosterjs-react/lib/ribbon/component/buttons/removeLink.ts index c87a28db8c2..e88fdb5abcb 100644 --- a/packages-ui/roosterjs-react/lib/ribbon/component/buttons/removeLink.ts +++ b/packages-ui/roosterjs-react/lib/ribbon/component/buttons/removeLink.ts @@ -1,6 +1,6 @@ -import RibbonButton from '../../type/RibbonButton'; import { removeLink as removeLinkApi } from 'roosterjs-editor-api'; -import { RemoveLinkButtonStringKey } from '../../type/RibbonButtonStringKeys'; +import type RibbonButton from '../../type/RibbonButton'; +import type { RemoveLinkButtonStringKey } from '../../type/RibbonButtonStringKeys'; /** * @internal diff --git a/packages-ui/roosterjs-react/lib/ribbon/component/buttons/rtl.ts b/packages-ui/roosterjs-react/lib/ribbon/component/buttons/rtl.ts index 683228cf2c7..d01c36ee5e7 100644 --- a/packages-ui/roosterjs-react/lib/ribbon/component/buttons/rtl.ts +++ b/packages-ui/roosterjs-react/lib/ribbon/component/buttons/rtl.ts @@ -1,7 +1,7 @@ -import RibbonButton from '../../type/RibbonButton'; import { Direction } from 'roosterjs-editor-types'; -import { RtlButtonStringKey } from '../../type/RibbonButtonStringKeys'; import { setDirection } from 'roosterjs-editor-api'; +import type RibbonButton from '../../type/RibbonButton'; +import type { RtlButtonStringKey } from '../../type/RibbonButtonStringKeys'; /** * @internal diff --git a/packages-ui/roosterjs-react/lib/ribbon/component/buttons/strikethrough.ts b/packages-ui/roosterjs-react/lib/ribbon/component/buttons/strikethrough.ts index 4dc3e4179fe..f4362bc75a5 100644 --- a/packages-ui/roosterjs-react/lib/ribbon/component/buttons/strikethrough.ts +++ b/packages-ui/roosterjs-react/lib/ribbon/component/buttons/strikethrough.ts @@ -1,6 +1,6 @@ -import RibbonButton from '../../type/RibbonButton'; -import { StrikethroughButtonStringKey } from '../../type/RibbonButtonStringKeys'; import { toggleStrikethrough } from 'roosterjs-editor-api'; +import type RibbonButton from '../../type/RibbonButton'; +import type { StrikethroughButtonStringKey } from '../../type/RibbonButtonStringKeys'; /** * @internal diff --git a/packages-ui/roosterjs-react/lib/ribbon/component/buttons/subscript.ts b/packages-ui/roosterjs-react/lib/ribbon/component/buttons/subscript.ts index 2ebb2e1960d..c3863e87db6 100644 --- a/packages-ui/roosterjs-react/lib/ribbon/component/buttons/subscript.ts +++ b/packages-ui/roosterjs-react/lib/ribbon/component/buttons/subscript.ts @@ -1,6 +1,6 @@ -import RibbonButton from '../../type/RibbonButton'; -import { SubscriptButtonStringKey } from '../../type/RibbonButtonStringKeys'; import { toggleSubscript } from 'roosterjs-editor-api'; +import type RibbonButton from '../../type/RibbonButton'; +import type { SubscriptButtonStringKey } from '../../type/RibbonButtonStringKeys'; /** * @internal diff --git a/packages-ui/roosterjs-react/lib/ribbon/component/buttons/superscript.ts b/packages-ui/roosterjs-react/lib/ribbon/component/buttons/superscript.ts index d3a17a88368..379e4328f26 100644 --- a/packages-ui/roosterjs-react/lib/ribbon/component/buttons/superscript.ts +++ b/packages-ui/roosterjs-react/lib/ribbon/component/buttons/superscript.ts @@ -1,6 +1,6 @@ -import RibbonButton from '../../type/RibbonButton'; -import { SuperscriptButtonStringKey } from '../../type/RibbonButtonStringKeys'; import { toggleSuperscript } from 'roosterjs-editor-api'; +import type RibbonButton from '../../type/RibbonButton'; +import type { SuperscriptButtonStringKey } from '../../type/RibbonButtonStringKeys'; /** * @internal diff --git a/packages-ui/roosterjs-react/lib/ribbon/component/buttons/textColor.ts b/packages-ui/roosterjs-react/lib/ribbon/component/buttons/textColor.ts index 679e80ca775..738692330a9 100644 --- a/packages-ui/roosterjs-react/lib/ribbon/component/buttons/textColor.ts +++ b/packages-ui/roosterjs-react/lib/ribbon/component/buttons/textColor.ts @@ -1,7 +1,7 @@ -import RibbonButton from '../../type/RibbonButton'; import { renderColorPicker } from '../../../colorPicker/component/renderColorPicker'; import { setTextColor } from 'roosterjs-editor-api'; -import { TextColorButtonStringKey } from '../../type/RibbonButtonStringKeys'; +import type RibbonButton from '../../type/RibbonButton'; +import type { TextColorButtonStringKey } from '../../type/RibbonButtonStringKeys'; import { getTextColorValue, TextColorDropDownItems, diff --git a/packages-ui/roosterjs-react/lib/ribbon/component/buttons/underline.ts b/packages-ui/roosterjs-react/lib/ribbon/component/buttons/underline.ts index 8185f4d7539..d508bdadaa0 100644 --- a/packages-ui/roosterjs-react/lib/ribbon/component/buttons/underline.ts +++ b/packages-ui/roosterjs-react/lib/ribbon/component/buttons/underline.ts @@ -1,6 +1,6 @@ -import RibbonButton from '../../type/RibbonButton'; import { toggleUnderline } from 'roosterjs-editor-api'; -import { UnderlineButtonStringKey } from '../../type/RibbonButtonStringKeys'; +import type RibbonButton from '../../type/RibbonButton'; +import type { UnderlineButtonStringKey } from '../../type/RibbonButtonStringKeys'; /** * @internal diff --git a/packages-ui/roosterjs-react/lib/ribbon/component/buttons/undo.ts b/packages-ui/roosterjs-react/lib/ribbon/component/buttons/undo.ts index 12045215b5b..36f1d108e71 100644 --- a/packages-ui/roosterjs-react/lib/ribbon/component/buttons/undo.ts +++ b/packages-ui/roosterjs-react/lib/ribbon/component/buttons/undo.ts @@ -1,5 +1,5 @@ -import RibbonButton from '../../type/RibbonButton'; -import { UndoButtonStringKey } from '../../type/RibbonButtonStringKeys'; +import type RibbonButton from '../../type/RibbonButton'; +import type { UndoButtonStringKey } from '../../type/RibbonButtonStringKeys'; /** * @internal diff --git a/packages-ui/roosterjs-react/lib/ribbon/component/getButtons.ts b/packages-ui/roosterjs-react/lib/ribbon/component/getButtons.ts index ecf922228a4..aa25dd8188a 100644 --- a/packages-ui/roosterjs-react/lib/ribbon/component/getButtons.ts +++ b/packages-ui/roosterjs-react/lib/ribbon/component/getButtons.ts @@ -1,8 +1,6 @@ -import RibbonButton from '../type/RibbonButton'; import { alignCenter } from './buttons/alignCenter'; import { alignLeft } from './buttons/alignLeft'; import { alignRight } from './buttons/alignRight'; -import { AllButtonStringKeys } from '../type/RibbonButtonStringKeys'; import { backgroundColor } from './buttons/backgroundColor'; import { bold } from './buttons/bold'; import { bulletedList } from './buttons/bulletedList'; @@ -32,6 +30,8 @@ import { superscript } from './buttons/superscript'; import { textColor } from './buttons/textColor'; import { underline } from './buttons/underline'; import { undo } from './buttons/undo'; +import type RibbonButton from '../type/RibbonButton'; +import type { AllButtonStringKeys } from '../type/RibbonButtonStringKeys'; const KnownRibbonButtons = >>{ [KnownRibbonButtonKey.Bold]: bold, diff --git a/packages-ui/roosterjs-react/lib/ribbon/plugin/createRibbonPlugin.ts b/packages-ui/roosterjs-react/lib/ribbon/plugin/createRibbonPlugin.ts index 477185d1233..b1835f59305 100644 --- a/packages-ui/roosterjs-react/lib/ribbon/plugin/createRibbonPlugin.ts +++ b/packages-ui/roosterjs-react/lib/ribbon/plugin/createRibbonPlugin.ts @@ -1,9 +1,10 @@ -import RibbonButton from '../type/RibbonButton'; -import RibbonPlugin from '../type/RibbonPlugin'; -import { FormatState, IEditor, PluginEvent, PluginEventType } from 'roosterjs-editor-types'; import { getFormatState } from 'roosterjs-editor-api'; import { getObjectKeys } from 'roosterjs-editor-dom'; -import { LocalizedStrings, UIUtilities } from '../../common/index'; +import { PluginEventType } from 'roosterjs-editor-types'; +import type RibbonButton from '../type/RibbonButton'; +import type RibbonPlugin from '../type/RibbonPlugin'; +import type { FormatState, IEditor, PluginEvent } from 'roosterjs-editor-types'; +import type { LocalizedStrings, UIUtilities } from '../../common/index'; /** * A plugin to connect format ribbon component and the editor diff --git a/packages-ui/roosterjs-react/lib/ribbon/type/RibbonButton.ts b/packages-ui/roosterjs-react/lib/ribbon/type/RibbonButton.ts index aba42b7333b..339d1ad198a 100644 --- a/packages-ui/roosterjs-react/lib/ribbon/type/RibbonButton.ts +++ b/packages-ui/roosterjs-react/lib/ribbon/type/RibbonButton.ts @@ -1,7 +1,7 @@ -import RibbonButtonDropDown from './RibbonButtonDropDown'; -import { FormatState, IEditor } from 'roosterjs-editor-types'; -import { ICommandBarItemProps } from '@fluentui/react/lib/CommandBar'; -import { LocalizedStrings, UIUtilities } from '../../common/index'; +import type RibbonButtonDropDown from './RibbonButtonDropDown'; +import type { FormatState, IEditor } from 'roosterjs-editor-types'; +import type { ICommandBarItemProps } from '@fluentui/react/lib/CommandBar'; +import type { LocalizedStrings, UIUtilities } from '../../common/index'; /** * Represents a button on format ribbon diff --git a/packages-ui/roosterjs-react/lib/ribbon/type/RibbonButtonDropDown.ts b/packages-ui/roosterjs-react/lib/ribbon/type/RibbonButtonDropDown.ts index ec383e8b992..93c038f5142 100644 --- a/packages-ui/roosterjs-react/lib/ribbon/type/RibbonButtonDropDown.ts +++ b/packages-ui/roosterjs-react/lib/ribbon/type/RibbonButtonDropDown.ts @@ -1,5 +1,5 @@ -import { FormatState } from 'roosterjs-editor-types'; -import { IContextualMenuItem, IContextualMenuProps } from '@fluentui/react/lib/ContextualMenu'; +import type { FormatState } from 'roosterjs-editor-types'; +import type { IContextualMenuItem, IContextualMenuProps } from '@fluentui/react/lib/ContextualMenu'; /** * Represent a drop down menu of a ribbon button diff --git a/packages-ui/roosterjs-react/lib/ribbon/type/RibbonButtonStringKeys.ts b/packages-ui/roosterjs-react/lib/ribbon/type/RibbonButtonStringKeys.ts index caa309dee3f..651c09c4077 100644 --- a/packages-ui/roosterjs-react/lib/ribbon/type/RibbonButtonStringKeys.ts +++ b/packages-ui/roosterjs-react/lib/ribbon/type/RibbonButtonStringKeys.ts @@ -1,5 +1,5 @@ -import { BackgroundColorKeys, TextColorKeys } from '../../colorPicker/index'; -import { +import type { BackgroundColorKeys, TextColorKeys } from '../../colorPicker/index'; +import type { CancelButtonStringKey, MenuItemSplitterKey0, OkButtonStringKey, diff --git a/packages-ui/roosterjs-react/lib/ribbon/type/RibbonPlugin.ts b/packages-ui/roosterjs-react/lib/ribbon/type/RibbonPlugin.ts index b1f4f1c2ad1..38c5d64f147 100644 --- a/packages-ui/roosterjs-react/lib/ribbon/type/RibbonPlugin.ts +++ b/packages-ui/roosterjs-react/lib/ribbon/type/RibbonPlugin.ts @@ -1,6 +1,6 @@ -import RibbonButton from './RibbonButton'; -import { FormatState } from 'roosterjs-editor-types'; -import { LocalizedStrings, ReactEditorPlugin } from '../../common/index'; +import type RibbonButton from './RibbonButton'; +import type { FormatState } from 'roosterjs-editor-types'; +import type { LocalizedStrings, ReactEditorPlugin } from '../../common/index'; /** * Represents a plugin to connect format ribbon component and the editor diff --git a/packages-ui/roosterjs-react/lib/ribbon/type/RibbonProps.ts b/packages-ui/roosterjs-react/lib/ribbon/type/RibbonProps.ts index 8bd3e9ecff2..a58645c5516 100644 --- a/packages-ui/roosterjs-react/lib/ribbon/type/RibbonProps.ts +++ b/packages-ui/roosterjs-react/lib/ribbon/type/RibbonProps.ts @@ -1,7 +1,7 @@ -import RibbonButton from './RibbonButton'; -import RibbonPlugin from './RibbonPlugin'; -import { ICommandBarProps } from '@fluentui/react/lib/CommandBar'; -import { LocalizedStrings } from '../../common/type/LocalizedStrings'; +import type RibbonButton from './RibbonButton'; +import type RibbonPlugin from './RibbonPlugin'; +import type { ICommandBarProps } from '@fluentui/react/lib/CommandBar'; +import type { LocalizedStrings } from '../../common/type/LocalizedStrings'; /** * Properties of Ribbon component diff --git a/packages-ui/roosterjs-react/lib/rooster/component/Rooster.tsx b/packages-ui/roosterjs-react/lib/rooster/component/Rooster.tsx index 976c68a6658..b675fcd5334 100644 --- a/packages-ui/roosterjs-react/lib/rooster/component/Rooster.tsx +++ b/packages-ui/roosterjs-react/lib/rooster/component/Rooster.tsx @@ -1,10 +1,11 @@ import * as React from 'react'; -import RoosterProps from '../type/RoosterProps'; -import { createUIUtilities, ReactEditorPlugin } from '../../common/index'; +import { createUIUtilities } from '../../common/index'; import { divProperties, getNativeProps } from '@fluentui/react/lib/Utilities'; import { Editor } from 'roosterjs-editor-core'; -import { EditorOptions, EditorPlugin, IEditor } from 'roosterjs-editor-types'; import { useTheme } from '@fluentui/react/lib/Theme'; +import type RoosterProps from '../type/RoosterProps'; +import type { ReactEditorPlugin } from '../../common/index'; +import type { EditorOptions, EditorPlugin, IEditor } from 'roosterjs-editor-types'; /** * Main component of react wrapper for roosterjs diff --git a/packages-ui/roosterjs-react/lib/rooster/plugin/createUpdateContentPlugin.ts b/packages-ui/roosterjs-react/lib/rooster/plugin/createUpdateContentPlugin.ts index 2ebe25de9e5..14ae6c384ce 100644 --- a/packages-ui/roosterjs-react/lib/rooster/plugin/createUpdateContentPlugin.ts +++ b/packages-ui/roosterjs-react/lib/rooster/plugin/createUpdateContentPlugin.ts @@ -1,6 +1,7 @@ -import UpdateContentPlugin from '../type/UpdateContentPlugin'; -import { IEditor, PluginEvent, PluginEventType } from 'roosterjs-editor-types'; +import { PluginEventType } from 'roosterjs-editor-types'; import { UpdateMode } from '../type/UpdateMode'; +import type UpdateContentPlugin from '../type/UpdateContentPlugin'; +import type { IEditor, PluginEvent } from 'roosterjs-editor-types'; /** * A plugin to help get HTML content from editor diff --git a/packages-ui/roosterjs-react/lib/rooster/type/RoosterProps.ts b/packages-ui/roosterjs-react/lib/rooster/type/RoosterProps.ts index fc524e64e00..90c96c5cee9 100644 --- a/packages-ui/roosterjs-react/lib/rooster/type/RoosterProps.ts +++ b/packages-ui/roosterjs-react/lib/rooster/type/RoosterProps.ts @@ -1,4 +1,4 @@ -import { EditorOptions, IEditor } from 'roosterjs-editor-types'; +import type { EditorOptions, IEditor } from 'roosterjs-editor-types'; /** * Properties for Rooster react component diff --git a/packages-ui/roosterjs-react/lib/rooster/type/UpdateContentPlugin.ts b/packages-ui/roosterjs-react/lib/rooster/type/UpdateContentPlugin.ts index 3f211ee3975..8b809497fb3 100644 --- a/packages-ui/roosterjs-react/lib/rooster/type/UpdateContentPlugin.ts +++ b/packages-ui/roosterjs-react/lib/rooster/type/UpdateContentPlugin.ts @@ -1,4 +1,4 @@ -import { EditorPlugin } from 'roosterjs-editor-types'; +import type { EditorPlugin } from 'roosterjs-editor-types'; /** * Represents a plugin to help get HTML content from editor diff --git a/packages/roosterjs-editor-api/lib/format/changeCapitalization.ts b/packages/roosterjs-editor-api/lib/format/changeCapitalization.ts index 977cbe0feec..f9bceeab2e4 100644 --- a/packages/roosterjs-editor-api/lib/format/changeCapitalization.ts +++ b/packages/roosterjs-editor-api/lib/format/changeCapitalization.ts @@ -1,6 +1,7 @@ import applyInlineStyle from '../utils/applyInlineStyle'; -import { Capitalization, IEditor, NodeType } from 'roosterjs-editor-types'; +import { Capitalization, NodeType } from 'roosterjs-editor-types'; import { getFirstLeafNode, getNextLeafSibling } from 'roosterjs-editor-dom'; +import type { IEditor } from 'roosterjs-editor-types'; import type { CompatibleCapitalization } from 'roosterjs-editor-types/lib/compatibleTypes'; /** diff --git a/packages/roosterjs-editor-api/lib/format/changeFontSize.ts b/packages/roosterjs-editor-api/lib/format/changeFontSize.ts index ff4e3d551df..47d5b37716b 100644 --- a/packages/roosterjs-editor-api/lib/format/changeFontSize.ts +++ b/packages/roosterjs-editor-api/lib/format/changeFontSize.ts @@ -1,6 +1,7 @@ import applyInlineStyle from '../utils/applyInlineStyle'; -import { FontSizeChange, IEditor } from 'roosterjs-editor-types'; +import { FontSizeChange } from 'roosterjs-editor-types'; import { getComputedStyle } from 'roosterjs-editor-dom'; +import type { IEditor } from 'roosterjs-editor-types'; import type { CompatibleFontSizeChange } from 'roosterjs-editor-types/lib/compatibleTypes'; /** diff --git a/packages/roosterjs-editor-api/lib/format/clearBlockFormat.ts b/packages/roosterjs-editor-api/lib/format/clearBlockFormat.ts index eaba7b5eb7b..05a61cb426b 100644 --- a/packages/roosterjs-editor-api/lib/format/clearBlockFormat.ts +++ b/packages/roosterjs-editor-api/lib/format/clearBlockFormat.ts @@ -1,5 +1,6 @@ import clearFormat from './clearFormat'; -import { ClearFormatMode, IEditor } from 'roosterjs-editor-types'; +import { ClearFormatMode } from 'roosterjs-editor-types'; +import type { IEditor } from 'roosterjs-editor-types'; /** * @deprecated Use clearFormat instead and pass the ClearFormatMode.Block as parameter diff --git a/packages/roosterjs-editor-api/lib/format/clearFormat.ts b/packages/roosterjs-editor-api/lib/format/clearFormat.ts index e97938fc1e0..2f2aaaac8ff 100644 --- a/packages/roosterjs-editor-api/lib/format/clearFormat.ts +++ b/packages/roosterjs-editor-api/lib/format/clearFormat.ts @@ -9,13 +9,8 @@ import setTextColor from './setTextColor'; import toggleBold from './toggleBold'; import toggleItalic from './toggleItalic'; import toggleUnderline from './toggleUnderline'; -import { - ChangeSource, - ClearFormatMode, - DocumentCommand, - IEditor, - QueryScope, -} from 'roosterjs-editor-types'; +import { ChangeSource, ClearFormatMode, DocumentCommand, QueryScope } from 'roosterjs-editor-types'; +import type { IEditor } from 'roosterjs-editor-types'; import { collapseNodesInRegion, getObjectKeys, diff --git a/packages/roosterjs-editor-api/lib/format/createLink.ts b/packages/roosterjs-editor-api/lib/format/createLink.ts index 7a6f2784e85..af0f99f663b 100644 --- a/packages/roosterjs-editor-api/lib/format/createLink.ts +++ b/packages/roosterjs-editor-api/lib/format/createLink.ts @@ -1,8 +1,8 @@ import { HtmlSanitizer, matchLink, wrap } from 'roosterjs-editor-dom'; +import type { IEditor } from 'roosterjs-editor-types'; import { ChangeSource, DocumentCommand, - IEditor, QueryScope, SelectionRangeTypes, } from 'roosterjs-editor-types'; diff --git a/packages/roosterjs-editor-api/lib/format/getFormatState.ts b/packages/roosterjs-editor-api/lib/format/getFormatState.ts index e7b6e654fcd..b60761dfd97 100644 --- a/packages/roosterjs-editor-api/lib/format/getFormatState.ts +++ b/packages/roosterjs-editor-api/lib/format/getFormatState.ts @@ -1,12 +1,11 @@ import { getTableFormatInfo, getTagOfNode, toArray } from 'roosterjs-editor-dom'; -import { +import type { ElementBasedFormatState, FormatState, IEditor, PluginEvent, - QueryScope, - SelectionRangeTypes, } from 'roosterjs-editor-types'; +import { QueryScope, SelectionRangeTypes } from 'roosterjs-editor-types'; /** * Get element based Format State at cursor diff --git a/packages/roosterjs-editor-api/lib/format/insertEntity.ts b/packages/roosterjs-editor-api/lib/format/insertEntity.ts index a340af53972..e2b4623c3d0 100644 --- a/packages/roosterjs-editor-api/lib/format/insertEntity.ts +++ b/packages/roosterjs-editor-api/lib/format/insertEntity.ts @@ -10,14 +10,11 @@ import { VListChain, wrap, } from 'roosterjs-editor-dom'; +import type { Entity, IEditor, NodePosition } from 'roosterjs-editor-types'; import { ChangeSource, ContentPosition, - Entity, - ExperimentalFeatures, - IEditor, KnownCreateElementDataIndex, - NodePosition, PositionType, } from 'roosterjs-editor-types'; import type { CompatibleContentPosition } from 'roosterjs-editor-types/lib/compatibleTypes'; @@ -149,12 +146,10 @@ export default function insertEntity( editor.select(pos); } } - } else if ( - isReadonly && - editor.isFeatureEnabled(ExperimentalFeatures.InlineEntityReadOnlyDelimiters) - ) { + } else if (isReadonly) { addDelimiters(entity.wrapper); - if (entity.wrapper.nextElementSibling) { + + if (entity.wrapper.nextElementSibling && editor.hasFocus()) { editor.select(new Position(entity.wrapper.nextElementSibling, PositionType.After)); } } diff --git a/packages/roosterjs-editor-api/lib/format/insertImage.ts b/packages/roosterjs-editor-api/lib/format/insertImage.ts index 00832a28b19..4eb620d435c 100644 --- a/packages/roosterjs-editor-api/lib/format/insertImage.ts +++ b/packages/roosterjs-editor-api/lib/format/insertImage.ts @@ -1,6 +1,6 @@ import formatUndoSnapshot from '../utils/formatUndoSnapshot'; import { getObjectKeys, readFile } from 'roosterjs-editor-dom'; -import { IEditor } from 'roosterjs-editor-types'; +import type { IEditor } from 'roosterjs-editor-types'; /** * Insert an image to editor at current selection diff --git a/packages/roosterjs-editor-api/lib/format/removeLink.ts b/packages/roosterjs-editor-api/lib/format/removeLink.ts index f02a5dc2a22..1c17e3ef677 100644 --- a/packages/roosterjs-editor-api/lib/format/removeLink.ts +++ b/packages/roosterjs-editor-api/lib/format/removeLink.ts @@ -1,6 +1,7 @@ import formatUndoSnapshot from '../utils/formatUndoSnapshot'; -import { IEditor, QueryScope } from 'roosterjs-editor-types'; +import { QueryScope } from 'roosterjs-editor-types'; import { unwrap } from 'roosterjs-editor-dom'; +import type { IEditor } from 'roosterjs-editor-types'; /** * Remove link at selection. If no links at selection, do nothing. diff --git a/packages/roosterjs-editor-api/lib/format/replaceWithNode.ts b/packages/roosterjs-editor-api/lib/format/replaceWithNode.ts index 443a6628c58..0648424ae44 100644 --- a/packages/roosterjs-editor-api/lib/format/replaceWithNode.ts +++ b/packages/roosterjs-editor-api/lib/format/replaceWithNode.ts @@ -1,4 +1,5 @@ -import { ContentPosition, IEditor, IPositionContentSearcher } from 'roosterjs-editor-types'; +import { ContentPosition } from 'roosterjs-editor-types'; +import type { IEditor, IPositionContentSearcher } from 'roosterjs-editor-types'; /** * Replace text before current selection with a node, current selection will be kept if possible diff --git a/packages/roosterjs-editor-api/lib/format/rotateElement.ts b/packages/roosterjs-editor-api/lib/format/rotateElement.ts index 55d3dbe32c6..ed5d1e12475 100644 --- a/packages/roosterjs-editor-api/lib/format/rotateElement.ts +++ b/packages/roosterjs-editor-api/lib/format/rotateElement.ts @@ -1,5 +1,5 @@ import formatUndoSnapshot from '../utils/formatUndoSnapshot'; -import { IEditor } from 'roosterjs-editor-types'; +import type { IEditor } from 'roosterjs-editor-types'; /** * Rotate an element visually diff --git a/packages/roosterjs-editor-api/lib/format/setAlignment.ts b/packages/roosterjs-editor-api/lib/format/setAlignment.ts index fdd81a52ce9..da9f1587bbf 100644 --- a/packages/roosterjs-editor-api/lib/format/setAlignment.ts +++ b/packages/roosterjs-editor-api/lib/format/setAlignment.ts @@ -2,6 +2,12 @@ import blockFormat from '../utils/blockFormat'; import execCommand from '../utils/execCommand'; import formatUndoSnapshot from '../utils/formatUndoSnapshot'; import normalizeBlockquote from '../utils/normalizeBlockquote'; +import { + Alignment, + DocumentCommand, + QueryScope, + SelectionRangeTypes, +} from 'roosterjs-editor-types'; import { createVListFromRegion, findClosestElementAncestor, @@ -9,14 +15,7 @@ import { isWholeTableSelected, VTable, } from 'roosterjs-editor-dom'; -import { - Alignment, - DocumentCommand, - IEditor, - QueryScope, - SelectionRangeTypes, - TableSelectionRange, -} from 'roosterjs-editor-types'; +import type { IEditor, TableSelectionRange } from 'roosterjs-editor-types'; import type { CompatibleAlignment } from 'roosterjs-editor-types/lib/compatibleTypes'; /** diff --git a/packages/roosterjs-editor-api/lib/format/setBackgroundColor.ts b/packages/roosterjs-editor-api/lib/format/setBackgroundColor.ts index f0618a74931..60f622af353 100644 --- a/packages/roosterjs-editor-api/lib/format/setBackgroundColor.ts +++ b/packages/roosterjs-editor-api/lib/format/setBackgroundColor.ts @@ -1,6 +1,6 @@ import applyInlineStyle from '../utils/applyInlineStyle'; -import { IEditor, ModeIndependentColor } from 'roosterjs-editor-types'; import { setColor } from 'roosterjs-editor-dom'; +import type { IEditor, ModeIndependentColor } from 'roosterjs-editor-types'; /** * Set background color at current selection diff --git a/packages/roosterjs-editor-api/lib/format/setDirection.ts b/packages/roosterjs-editor-api/lib/format/setDirection.ts index e2e92dd65c4..830e8493b1b 100644 --- a/packages/roosterjs-editor-api/lib/format/setDirection.ts +++ b/packages/roosterjs-editor-api/lib/format/setDirection.ts @@ -1,6 +1,7 @@ import collapseSelectedBlocks from '../utils/collapseSelectedBlocks'; import formatUndoSnapshot from '../utils/formatUndoSnapshot'; -import { Direction, IEditor } from 'roosterjs-editor-types'; +import { Direction } from 'roosterjs-editor-types'; +import type { IEditor } from 'roosterjs-editor-types'; import type { CompatibleDirection } from 'roosterjs-editor-types/lib/compatibleTypes'; /** diff --git a/packages/roosterjs-editor-api/lib/format/setFontName.ts b/packages/roosterjs-editor-api/lib/format/setFontName.ts index 9a4c80ead83..32e2b4a380a 100644 --- a/packages/roosterjs-editor-api/lib/format/setFontName.ts +++ b/packages/roosterjs-editor-api/lib/format/setFontName.ts @@ -1,5 +1,5 @@ import applyListItemStyleWrap from '../utils/applyListItemWrap'; -import { IEditor } from 'roosterjs-editor-types'; +import type { IEditor } from 'roosterjs-editor-types'; /** * Set font name at selection diff --git a/packages/roosterjs-editor-api/lib/format/setFontSize.ts b/packages/roosterjs-editor-api/lib/format/setFontSize.ts index ed1568148a4..96c4e07f3c0 100644 --- a/packages/roosterjs-editor-api/lib/format/setFontSize.ts +++ b/packages/roosterjs-editor-api/lib/format/setFontSize.ts @@ -1,6 +1,6 @@ import applyListItemStyleWrap from '../utils/applyListItemWrap'; import { getComputedStyle } from 'roosterjs-editor-dom'; -import { IEditor } from 'roosterjs-editor-types'; +import type { IEditor } from 'roosterjs-editor-types'; /** * Set font size at selection diff --git a/packages/roosterjs-editor-api/lib/format/setHeadingLevel.ts b/packages/roosterjs-editor-api/lib/format/setHeadingLevel.ts index 174278ee849..da90f9f83f6 100644 --- a/packages/roosterjs-editor-api/lib/format/setHeadingLevel.ts +++ b/packages/roosterjs-editor-api/lib/format/setHeadingLevel.ts @@ -1,6 +1,7 @@ import formatUndoSnapshot from '../utils/formatUndoSnapshot'; -import { DocumentCommand, IEditor, QueryScope } from 'roosterjs-editor-types'; +import { DocumentCommand, QueryScope } from 'roosterjs-editor-types'; import { HtmlSanitizer, moveChildNodes } from 'roosterjs-editor-dom'; +import type { IEditor } from 'roosterjs-editor-types'; /** * Set heading level at selection diff --git a/packages/roosterjs-editor-api/lib/format/setImageAltText.ts b/packages/roosterjs-editor-api/lib/format/setImageAltText.ts index adec527156b..617e1d3f176 100644 --- a/packages/roosterjs-editor-api/lib/format/setImageAltText.ts +++ b/packages/roosterjs-editor-api/lib/format/setImageAltText.ts @@ -1,5 +1,6 @@ import formatUndoSnapshot from '../utils/formatUndoSnapshot'; -import { IEditor, QueryScope } from 'roosterjs-editor-types'; +import { QueryScope } from 'roosterjs-editor-types'; +import type { IEditor } from 'roosterjs-editor-types'; /** * Set image alt text for all selected images at selection. If no images is contained diff --git a/packages/roosterjs-editor-api/lib/format/setIndentation.ts b/packages/roosterjs-editor-api/lib/format/setIndentation.ts index b3b6d41cd69..598dc4579bc 100644 --- a/packages/roosterjs-editor-api/lib/format/setIndentation.ts +++ b/packages/roosterjs-editor-api/lib/format/setIndentation.ts @@ -1,14 +1,13 @@ import blockFormat from '../utils/blockFormat'; import normalizeBlockquote from '../utils/normalizeBlockquote'; +import type { BlockElement, IEditor, RegionBase } from 'roosterjs-editor-types'; import { - BlockElement, ExperimentalFeatures, - IEditor, Indentation, KnownCreateElementDataIndex, - RegionBase, SelectionRangeTypes, } from 'roosterjs-editor-types'; +import type { VList } from 'roosterjs-editor-dom'; import { collapseNodesInRegion, createVListFromRegion, @@ -20,7 +19,6 @@ import { splitBalancedNodeRange, toArray, unwrap, - VList, VTable, wrap, } from 'roosterjs-editor-dom'; diff --git a/packages/roosterjs-editor-api/lib/format/setOrderedListNumbering.ts b/packages/roosterjs-editor-api/lib/format/setOrderedListNumbering.ts index 60c213cbc20..b23f567b209 100644 --- a/packages/roosterjs-editor-api/lib/format/setOrderedListNumbering.ts +++ b/packages/roosterjs-editor-api/lib/format/setOrderedListNumbering.ts @@ -1,6 +1,7 @@ import formatUndoSnapshot from '../utils/formatUndoSnapshot'; import { createVListFromRegion } from 'roosterjs-editor-dom'; -import { ExperimentalFeatures, IEditor } from 'roosterjs-editor-types'; +import { ExperimentalFeatures } from 'roosterjs-editor-types'; +import type { IEditor } from 'roosterjs-editor-types'; /** * Resets Ordered List Numbering back to the value of the parameter startNumber diff --git a/packages/roosterjs-editor-api/lib/format/setTextColor.ts b/packages/roosterjs-editor-api/lib/format/setTextColor.ts index 3b3398bed36..ae5a95263cf 100644 --- a/packages/roosterjs-editor-api/lib/format/setTextColor.ts +++ b/packages/roosterjs-editor-api/lib/format/setTextColor.ts @@ -1,6 +1,6 @@ import applyListItemStyleWrap from '../utils/applyListItemWrap'; -import { IEditor, ModeIndependentColor } from 'roosterjs-editor-types'; import { setColor } from 'roosterjs-editor-dom'; +import type { IEditor, ModeIndependentColor } from 'roosterjs-editor-types'; /** * Set text color at selection diff --git a/packages/roosterjs-editor-api/lib/format/toggleBlockQuote.ts b/packages/roosterjs-editor-api/lib/format/toggleBlockQuote.ts index 1ada8464eff..273da10f5d9 100644 --- a/packages/roosterjs-editor-api/lib/format/toggleBlockQuote.ts +++ b/packages/roosterjs-editor-api/lib/format/toggleBlockQuote.ts @@ -1,6 +1,7 @@ import blockWrap from '../utils/blockWrap'; -import { IEditor, QueryScope } from 'roosterjs-editor-types'; +import { QueryScope } from 'roosterjs-editor-types'; import { unwrap, wrap } from 'roosterjs-editor-dom'; +import type { IEditor } from 'roosterjs-editor-types'; const BLOCKQUOTE_TAG = 'blockquote'; const DEFAULT_STYLER = (element: HTMLElement): void => { diff --git a/packages/roosterjs-editor-api/lib/format/toggleBold.ts b/packages/roosterjs-editor-api/lib/format/toggleBold.ts index c547bd67a63..c402b708155 100644 --- a/packages/roosterjs-editor-api/lib/format/toggleBold.ts +++ b/packages/roosterjs-editor-api/lib/format/toggleBold.ts @@ -1,5 +1,6 @@ import execCommand from '../utils/execCommand'; -import { DocumentCommand, IEditor } from 'roosterjs-editor-types'; +import { DocumentCommand } from 'roosterjs-editor-types'; +import type { IEditor } from 'roosterjs-editor-types'; /** * Toggle bold at selection diff --git a/packages/roosterjs-editor-api/lib/format/toggleBullet.ts b/packages/roosterjs-editor-api/lib/format/toggleBullet.ts index 77a286bf5e3..ba68373a017 100644 --- a/packages/roosterjs-editor-api/lib/format/toggleBullet.ts +++ b/packages/roosterjs-editor-api/lib/format/toggleBullet.ts @@ -1,5 +1,6 @@ import toggleListType from '../utils/toggleListType'; -import { BulletListType, IEditor, ListType } from 'roosterjs-editor-types'; +import { ListType } from 'roosterjs-editor-types'; +import type { BulletListType, IEditor } from 'roosterjs-editor-types'; import type { CompatibleBulletListType } from 'roosterjs-editor-types/lib/compatibleTypes'; /** diff --git a/packages/roosterjs-editor-api/lib/format/toggleCodeBlock.ts b/packages/roosterjs-editor-api/lib/format/toggleCodeBlock.ts index b7eb5d084dc..624cd3ad772 100644 --- a/packages/roosterjs-editor-api/lib/format/toggleCodeBlock.ts +++ b/packages/roosterjs-editor-api/lib/format/toggleCodeBlock.ts @@ -1,6 +1,7 @@ import blockWrap from '../utils/blockWrap'; -import { IEditor, QueryScope } from 'roosterjs-editor-types'; +import { QueryScope } from 'roosterjs-editor-types'; import { unwrap, wrap } from 'roosterjs-editor-dom'; +import type { IEditor } from 'roosterjs-editor-types'; const PRE_TAG = 'pre'; const CODE_TAG = 'code'; diff --git a/packages/roosterjs-editor-api/lib/format/toggleItalic.ts b/packages/roosterjs-editor-api/lib/format/toggleItalic.ts index 4b18c06d29d..fa90dd3ee93 100644 --- a/packages/roosterjs-editor-api/lib/format/toggleItalic.ts +++ b/packages/roosterjs-editor-api/lib/format/toggleItalic.ts @@ -1,5 +1,6 @@ import execCommand from '../utils/execCommand'; -import { DocumentCommand, IEditor } from 'roosterjs-editor-types'; +import { DocumentCommand } from 'roosterjs-editor-types'; +import type { IEditor } from 'roosterjs-editor-types'; /** * Toggle italic at selection diff --git a/packages/roosterjs-editor-api/lib/format/toggleNumbering.ts b/packages/roosterjs-editor-api/lib/format/toggleNumbering.ts index a71d26cf79b..095b23908f4 100644 --- a/packages/roosterjs-editor-api/lib/format/toggleNumbering.ts +++ b/packages/roosterjs-editor-api/lib/format/toggleNumbering.ts @@ -1,5 +1,6 @@ import toggleListType from '../utils/toggleListType'; -import { IEditor, ListType, NumberingListType } from 'roosterjs-editor-types'; +import { ListType } from 'roosterjs-editor-types'; +import type { IEditor, NumberingListType } from 'roosterjs-editor-types'; import type { CompatibleNumberingListType } from 'roosterjs-editor-types/lib/compatibleTypes'; /** diff --git a/packages/roosterjs-editor-api/lib/format/toggleStrikethrough.ts b/packages/roosterjs-editor-api/lib/format/toggleStrikethrough.ts index b9764fd56fa..98367de68cf 100644 --- a/packages/roosterjs-editor-api/lib/format/toggleStrikethrough.ts +++ b/packages/roosterjs-editor-api/lib/format/toggleStrikethrough.ts @@ -1,5 +1,6 @@ import execCommand from '../utils/execCommand'; -import { DocumentCommand, IEditor } from 'roosterjs-editor-types'; +import { DocumentCommand } from 'roosterjs-editor-types'; +import type { IEditor } from 'roosterjs-editor-types'; /** * Toggle strikethrough at selection diff --git a/packages/roosterjs-editor-api/lib/format/toggleSubscript.ts b/packages/roosterjs-editor-api/lib/format/toggleSubscript.ts index eb252c65c45..2252f585c43 100644 --- a/packages/roosterjs-editor-api/lib/format/toggleSubscript.ts +++ b/packages/roosterjs-editor-api/lib/format/toggleSubscript.ts @@ -1,5 +1,6 @@ import execCommand from '../utils/execCommand'; -import { DocumentCommand, IEditor } from 'roosterjs-editor-types'; +import { DocumentCommand } from 'roosterjs-editor-types'; +import type { IEditor } from 'roosterjs-editor-types'; /** * Toggle subscript at selection diff --git a/packages/roosterjs-editor-api/lib/format/toggleSuperscript.ts b/packages/roosterjs-editor-api/lib/format/toggleSuperscript.ts index bcd98e1d10a..942455254b6 100644 --- a/packages/roosterjs-editor-api/lib/format/toggleSuperscript.ts +++ b/packages/roosterjs-editor-api/lib/format/toggleSuperscript.ts @@ -1,5 +1,6 @@ import execCommand from '../utils/execCommand'; -import { DocumentCommand, IEditor } from 'roosterjs-editor-types'; +import { DocumentCommand } from 'roosterjs-editor-types'; +import type { IEditor } from 'roosterjs-editor-types'; /** * Toggle superscript at selection diff --git a/packages/roosterjs-editor-api/lib/format/toggleUnderline.ts b/packages/roosterjs-editor-api/lib/format/toggleUnderline.ts index 3bb73eb5cf4..a9d49e5e0a9 100644 --- a/packages/roosterjs-editor-api/lib/format/toggleUnderline.ts +++ b/packages/roosterjs-editor-api/lib/format/toggleUnderline.ts @@ -1,5 +1,6 @@ import execCommand from '../utils/execCommand'; -import { DocumentCommand, IEditor } from 'roosterjs-editor-types'; +import { DocumentCommand } from 'roosterjs-editor-types'; +import type { IEditor } from 'roosterjs-editor-types'; /** * Toggle underline at selection diff --git a/packages/roosterjs-editor-api/lib/table/applyCellShading.ts b/packages/roosterjs-editor-api/lib/table/applyCellShading.ts index a0499617052..ed416349cc1 100644 --- a/packages/roosterjs-editor-api/lib/table/applyCellShading.ts +++ b/packages/roosterjs-editor-api/lib/table/applyCellShading.ts @@ -1,5 +1,5 @@ import formatUndoSnapshot from '../utils/formatUndoSnapshot'; -import { IEditor, ModeIndependentColor } from 'roosterjs-editor-types'; +import type { IEditor, ModeIndependentColor } from 'roosterjs-editor-types'; import { getTableCellMetadata, safeInstanceOf, diff --git a/packages/roosterjs-editor-api/lib/table/editTable.ts b/packages/roosterjs-editor-api/lib/table/editTable.ts index a6e57608f4d..a6daa5f81c7 100644 --- a/packages/roosterjs-editor-api/lib/table/editTable.ts +++ b/packages/roosterjs-editor-api/lib/table/editTable.ts @@ -1,6 +1,7 @@ import formatUndoSnapshot from '../utils/formatUndoSnapshot'; -import { IEditor, PositionType, SelectionRangeTypes, TableOperation } from 'roosterjs-editor-types'; +import { PositionType, SelectionRangeTypes, TableOperation } from 'roosterjs-editor-types'; import { VTable } from 'roosterjs-editor-dom'; +import type { IEditor } from 'roosterjs-editor-types'; import type { CompatibleTableOperation } from 'roosterjs-editor-types/lib/compatibleTypes'; /** diff --git a/packages/roosterjs-editor-api/lib/table/formatTable.ts b/packages/roosterjs-editor-api/lib/table/formatTable.ts index 6fccfbae959..d5bf17db868 100644 --- a/packages/roosterjs-editor-api/lib/table/formatTable.ts +++ b/packages/roosterjs-editor-api/lib/table/formatTable.ts @@ -1,6 +1,6 @@ import formatUndoSnapshot from '../utils/formatUndoSnapshot'; -import { IEditor, TableFormat } from 'roosterjs-editor-types'; import { VTable } from 'roosterjs-editor-dom'; +import type { IEditor, TableFormat } from 'roosterjs-editor-types'; /** * Format table diff --git a/packages/roosterjs-editor-api/lib/table/insertTable.ts b/packages/roosterjs-editor-api/lib/table/insertTable.ts index 85dcbaeb72d..e134053b29c 100644 --- a/packages/roosterjs-editor-api/lib/table/insertTable.ts +++ b/packages/roosterjs-editor-api/lib/table/insertTable.ts @@ -1,7 +1,8 @@ import formatUndoSnapshot from '../utils/formatUndoSnapshot'; import setBackgroundColor from '../format/setBackgroundColor'; -import { IEditor, PositionType, TableFormat } from 'roosterjs-editor-types'; import { Position, VTable } from 'roosterjs-editor-dom'; +import { PositionType } from 'roosterjs-editor-types'; +import type { IEditor, TableFormat } from 'roosterjs-editor-types'; /** * Insert table into editor at current selection diff --git a/packages/roosterjs-editor-api/lib/utils/applyInlineStyle.ts b/packages/roosterjs-editor-api/lib/utils/applyInlineStyle.ts index b9b5e7f94bb..9f4de1d68ca 100644 --- a/packages/roosterjs-editor-api/lib/utils/applyInlineStyle.ts +++ b/packages/roosterjs-editor-api/lib/utils/applyInlineStyle.ts @@ -1,8 +1,8 @@ import formatUndoSnapshot from './formatUndoSnapshot'; import { getTagOfNode } from 'roosterjs-editor-dom'; +import type { IEditor } from 'roosterjs-editor-types'; import { ChangeSource, - IEditor, PluginEventType, PositionType, SelectionRangeTypes, diff --git a/packages/roosterjs-editor-api/lib/utils/applyListItemWrap.ts b/packages/roosterjs-editor-api/lib/utils/applyListItemWrap.ts index 6554d201158..e3a7022f14d 100644 --- a/packages/roosterjs-editor-api/lib/utils/applyListItemWrap.ts +++ b/packages/roosterjs-editor-api/lib/utils/applyListItemWrap.ts @@ -1,6 +1,6 @@ import applyInlineStyle from '../utils/applyInlineStyle'; -import { IEditor } from 'roosterjs-editor-types'; import { safeInstanceOf, setListItemStyle } from 'roosterjs-editor-dom'; +import type { IEditor } from 'roosterjs-editor-types'; /** * @internal diff --git a/packages/roosterjs-editor-api/lib/utils/blockFormat.ts b/packages/roosterjs-editor-api/lib/utils/blockFormat.ts index e7545e5086a..7b1629a09bc 100644 --- a/packages/roosterjs-editor-api/lib/utils/blockFormat.ts +++ b/packages/roosterjs-editor-api/lib/utils/blockFormat.ts @@ -1,7 +1,8 @@ import commitListChains from '../utils/commitListChains'; import formatUndoSnapshot from './formatUndoSnapshot'; -import { IEditor, NodePosition, Region, SelectionRangeTypes } from 'roosterjs-editor-types'; +import { SelectionRangeTypes } from 'roosterjs-editor-types'; import { VListChain } from 'roosterjs-editor-dom'; +import type { IEditor, NodePosition, Region } from 'roosterjs-editor-types'; /** * Split selection into regions, and perform a block-wise formatting action for each region. diff --git a/packages/roosterjs-editor-api/lib/utils/blockWrap.ts b/packages/roosterjs-editor-api/lib/utils/blockWrap.ts index 53cbc50ee66..0a43d12a076 100644 --- a/packages/roosterjs-editor-api/lib/utils/blockWrap.ts +++ b/packages/roosterjs-editor-api/lib/utils/blockWrap.ts @@ -1,5 +1,5 @@ import blockFormat from './blockFormat'; -import { IEditor } from 'roosterjs-editor-types'; +import type { IEditor } from 'roosterjs-editor-types'; import { collapseNodesInRegion, getSelectedBlockElementsInRegion, diff --git a/packages/roosterjs-editor-api/lib/utils/collapseSelectedBlocks.ts b/packages/roosterjs-editor-api/lib/utils/collapseSelectedBlocks.ts index f3a538ecceb..f8eec70bdb7 100644 --- a/packages/roosterjs-editor-api/lib/utils/collapseSelectedBlocks.ts +++ b/packages/roosterjs-editor-api/lib/utils/collapseSelectedBlocks.ts @@ -1,5 +1,6 @@ -import { BlockElement, IEditor, NodeType } from 'roosterjs-editor-types'; import { getTagOfNode } from 'roosterjs-editor-dom'; +import { NodeType } from 'roosterjs-editor-types'; +import type { BlockElement, IEditor } from 'roosterjs-editor-types'; /** * @internal diff --git a/packages/roosterjs-editor-api/lib/utils/commitListChains.ts b/packages/roosterjs-editor-api/lib/utils/commitListChains.ts index 20b59b188f8..4def021a738 100644 --- a/packages/roosterjs-editor-api/lib/utils/commitListChains.ts +++ b/packages/roosterjs-editor-api/lib/utils/commitListChains.ts @@ -1,5 +1,7 @@ -import { ExperimentalFeatures, IEditor } from 'roosterjs-editor-types'; -import { Position, VListChain } from 'roosterjs-editor-dom'; +import { ExperimentalFeatures } from 'roosterjs-editor-types'; +import { Position } from 'roosterjs-editor-dom'; +import type { IEditor } from 'roosterjs-editor-types'; +import type { VListChain } from 'roosterjs-editor-dom'; /** * Commit changes of all list changes when experiment features are allowed diff --git a/packages/roosterjs-editor-api/lib/utils/execCommand.ts b/packages/roosterjs-editor-api/lib/utils/execCommand.ts index b293e051fa6..01e89bbf678 100644 --- a/packages/roosterjs-editor-api/lib/utils/execCommand.ts +++ b/packages/roosterjs-editor-api/lib/utils/execCommand.ts @@ -1,11 +1,8 @@ import formatUndoSnapshot from './formatUndoSnapshot'; -import { getObjectKeys, PendableFormatCommandMap, PendableFormatNames } from 'roosterjs-editor-dom'; -import { - DocumentCommand, - IEditor, - PluginEventType, - SelectionRangeTypes, -} from 'roosterjs-editor-types'; +import { getObjectKeys, PendableFormatCommandMap } from 'roosterjs-editor-dom'; +import { PluginEventType, SelectionRangeTypes } from 'roosterjs-editor-types'; +import type { PendableFormatNames } from 'roosterjs-editor-dom'; +import type { DocumentCommand, IEditor } from 'roosterjs-editor-types'; import type { CompatibleDocumentCommand } from 'roosterjs-editor-types/lib/compatibleTypes'; /** diff --git a/packages/roosterjs-editor-api/lib/utils/formatUndoSnapshot.ts b/packages/roosterjs-editor-api/lib/utils/formatUndoSnapshot.ts index 8ba3c760d01..a8e70c65a97 100644 --- a/packages/roosterjs-editor-api/lib/utils/formatUndoSnapshot.ts +++ b/packages/roosterjs-editor-api/lib/utils/formatUndoSnapshot.ts @@ -1,4 +1,5 @@ -import { ChangeSource, IEditor, NodePosition } from 'roosterjs-editor-types'; +import { ChangeSource } from 'roosterjs-editor-types'; +import type { IEditor, NodePosition } from 'roosterjs-editor-types'; /** * @internal diff --git a/packages/roosterjs-editor-api/lib/utils/toggleListType.ts b/packages/roosterjs-editor-api/lib/utils/toggleListType.ts index e268bf7deba..a502bc769c8 100644 --- a/packages/roosterjs-editor-api/lib/utils/toggleListType.ts +++ b/packages/roosterjs-editor-api/lib/utils/toggleListType.ts @@ -1,12 +1,7 @@ import blockFormat from '../utils/blockFormat'; import { createVListFromRegion, getBlockElementAtNode } from 'roosterjs-editor-dom'; -import { - BulletListType, - ExperimentalFeatures, - IEditor, - ListType, - NumberingListType, -} from 'roosterjs-editor-types'; +import { ExperimentalFeatures } from 'roosterjs-editor-types'; +import type { BulletListType, IEditor, ListType, NumberingListType } from 'roosterjs-editor-types'; import type { CompatibleBulletListType, CompatibleListType, diff --git a/packages/roosterjs-editor-core/lib/coreApi/addUndoSnapshot.ts b/packages/roosterjs-editor-core/lib/coreApi/addUndoSnapshot.ts index 0079a07606b..ea68bba690e 100644 --- a/packages/roosterjs-editor-core/lib/coreApi/addUndoSnapshot.ts +++ b/packages/roosterjs-editor-core/lib/coreApi/addUndoSnapshot.ts @@ -1,6 +1,7 @@ -import { EntityState } from 'roosterjs-editor-types'; import { getSelectionPath, Position } from 'roosterjs-editor-dom'; -import { +import { PluginEventType, SelectionRangeTypes } from 'roosterjs-editor-types'; +import type { + EntityState, AddUndoSnapshot, ChangeSource, ContentChangedData, @@ -8,9 +9,7 @@ import { ContentMetadata, EditorCore, NodePosition, - PluginEventType, SelectionRangeEx, - SelectionRangeTypes, } from 'roosterjs-editor-types'; import type { CompatibleChangeSource } from 'roosterjs-editor-types/lib/compatibleTypes'; diff --git a/packages/roosterjs-editor-core/lib/coreApi/attachDomEvent.ts b/packages/roosterjs-editor-core/lib/coreApi/attachDomEvent.ts index 0f8a76e0ec0..f65b37082ce 100644 --- a/packages/roosterjs-editor-core/lib/coreApi/attachDomEvent.ts +++ b/packages/roosterjs-editor-core/lib/coreApi/attachDomEvent.ts @@ -1,5 +1,5 @@ import { getObjectKeys } from 'roosterjs-editor-dom'; -import { +import type { AttachDomEvent, DOMEventHandler, DOMEventHandlerObject, diff --git a/packages/roosterjs-editor-core/lib/coreApi/coreApiMap.ts b/packages/roosterjs-editor-core/lib/coreApi/coreApiMap.ts index b3c4e885046..dd12197703f 100644 --- a/packages/roosterjs-editor-core/lib/coreApi/coreApiMap.ts +++ b/packages/roosterjs-editor-core/lib/coreApi/coreApiMap.ts @@ -1,6 +1,5 @@ import { addUndoSnapshot } from './addUndoSnapshot'; import { attachDomEvent } from './attachDomEvent'; -import { CoreApiMap } from 'roosterjs-editor-types'; import { createPasteFragment } from './createPasteFragment'; import { ensureTypeInContainer } from './ensureTypeInContainer'; import { focus } from './focus'; @@ -20,6 +19,7 @@ import { setContent } from './setContent'; import { switchShadowEdit } from './switchShadowEdit'; import { transformColor } from './transformColor'; import { triggerEvent } from './triggerEvent'; +import type { CoreApiMap } from 'roosterjs-editor-types'; /** * @internal diff --git a/packages/roosterjs-editor-core/lib/coreApi/createPasteFragment.ts b/packages/roosterjs-editor-core/lib/coreApi/createPasteFragment.ts index 7a1b98e8bc3..923579d0815 100644 --- a/packages/roosterjs-editor-core/lib/coreApi/createPasteFragment.ts +++ b/packages/roosterjs-editor-core/lib/coreApi/createPasteFragment.ts @@ -1,3 +1,4 @@ +import { PasteType, PluginEventType } from 'roosterjs-editor-types'; import { applyFormat, applyTextStyle, @@ -9,14 +10,12 @@ import { retrieveMetadataFromClipboard, sanitizePasteContent, } from 'roosterjs-editor-dom'; -import { +import type { BeforePasteEvent, ClipboardData, CreatePasteFragment, EditorCore, - PluginEventType, NodePosition, - PasteType, DefaultFormat, } from 'roosterjs-editor-types'; diff --git a/packages/roosterjs-editor-core/lib/coreApi/ensureTypeInContainer.ts b/packages/roosterjs-editor-core/lib/coreApi/ensureTypeInContainer.ts index 9a544e4a169..3860ac2a4d7 100644 --- a/packages/roosterjs-editor-core/lib/coreApi/ensureTypeInContainer.ts +++ b/packages/roosterjs-editor-core/lib/coreApi/ensureTypeInContainer.ts @@ -1,11 +1,5 @@ -import { - ContentPosition, - EditorCore, - EnsureTypeInContainer, - KnownCreateElementDataIndex, - NodePosition, - PositionType, -} from 'roosterjs-editor-types'; +import { ContentPosition, KnownCreateElementDataIndex, PositionType } from 'roosterjs-editor-types'; +import type { EditorCore, EnsureTypeInContainer, NodePosition } from 'roosterjs-editor-types'; import { applyFormat, createElement, diff --git a/packages/roosterjs-editor-core/lib/coreApi/focus.ts b/packages/roosterjs-editor-core/lib/coreApi/focus.ts index 94580a23c31..97068203bc2 100644 --- a/packages/roosterjs-editor-core/lib/coreApi/focus.ts +++ b/packages/roosterjs-editor-core/lib/coreApi/focus.ts @@ -1,5 +1,6 @@ import { createRange, getFirstLeafNode } from 'roosterjs-editor-dom'; -import { EditorCore, Focus, PositionType } from 'roosterjs-editor-types'; +import { PositionType } from 'roosterjs-editor-types'; +import type { EditorCore, Focus } from 'roosterjs-editor-types'; /** * @internal diff --git a/packages/roosterjs-editor-core/lib/coreApi/getContent.ts b/packages/roosterjs-editor-core/lib/coreApi/getContent.ts index d4ecdba5d8d..8135e2866e3 100644 --- a/packages/roosterjs-editor-core/lib/coreApi/getContent.ts +++ b/packages/roosterjs-editor-core/lib/coreApi/getContent.ts @@ -1,10 +1,5 @@ -import { - ColorTransformDirection, - EditorCore, - GetContent, - GetContentMode, - PluginEventType, -} from 'roosterjs-editor-types'; +import { ColorTransformDirection, GetContentMode, PluginEventType } from 'roosterjs-editor-types'; +import type { EditorCore, GetContent } from 'roosterjs-editor-types'; import { createRange, getHtmlWithSelectionPath, diff --git a/packages/roosterjs-editor-core/lib/coreApi/getPendableFormatState.ts b/packages/roosterjs-editor-core/lib/coreApi/getPendableFormatState.ts index 1ff93b6af43..9d558c92ae7 100644 --- a/packages/roosterjs-editor-core/lib/coreApi/getPendableFormatState.ts +++ b/packages/roosterjs-editor-core/lib/coreApi/getPendableFormatState.ts @@ -1,15 +1,10 @@ -import { - contains, - getObjectKeys, - getTagOfNode, - PendableFormatNames, - Position, -} from 'roosterjs-editor-dom'; -import { +import { contains, getObjectKeys, getTagOfNode, Position } from 'roosterjs-editor-dom'; +import { NodeType } from 'roosterjs-editor-types'; +import type { PendableFormatNames } from 'roosterjs-editor-dom'; +import type { EditorCore, GetPendableFormatState, NodePosition, - NodeType, PendableFormatState, } from 'roosterjs-editor-types'; diff --git a/packages/roosterjs-editor-core/lib/coreApi/getSelectionRange.ts b/packages/roosterjs-editor-core/lib/coreApi/getSelectionRange.ts index 2c68a31c566..c9a04efb2a2 100644 --- a/packages/roosterjs-editor-core/lib/coreApi/getSelectionRange.ts +++ b/packages/roosterjs-editor-core/lib/coreApi/getSelectionRange.ts @@ -1,5 +1,5 @@ import { contains, createRange } from 'roosterjs-editor-dom'; -import { EditorCore, GetSelectionRange } from 'roosterjs-editor-types'; +import type { EditorCore, GetSelectionRange } from 'roosterjs-editor-types'; /** * @internal diff --git a/packages/roosterjs-editor-core/lib/coreApi/getSelectionRangeEx.ts b/packages/roosterjs-editor-core/lib/coreApi/getSelectionRangeEx.ts index 1687d6c5feb..3461e07f48f 100644 --- a/packages/roosterjs-editor-core/lib/coreApi/getSelectionRangeEx.ts +++ b/packages/roosterjs-editor-core/lib/coreApi/getSelectionRangeEx.ts @@ -1,10 +1,6 @@ import { contains, createRange, findClosestElementAncestor } from 'roosterjs-editor-dom'; -import { - EditorCore, - GetSelectionRangeEx, - SelectionRangeEx, - SelectionRangeTypes, -} from 'roosterjs-editor-types'; +import { SelectionRangeTypes } from 'roosterjs-editor-types'; +import type { EditorCore, GetSelectionRangeEx, SelectionRangeEx } from 'roosterjs-editor-types'; /** * @internal diff --git a/packages/roosterjs-editor-core/lib/coreApi/getStyleBasedFormatState.ts b/packages/roosterjs-editor-core/lib/coreApi/getStyleBasedFormatState.ts index 4f234594db9..d715364379d 100644 --- a/packages/roosterjs-editor-core/lib/coreApi/getStyleBasedFormatState.ts +++ b/packages/roosterjs-editor-core/lib/coreApi/getStyleBasedFormatState.ts @@ -1,5 +1,6 @@ import { contains, getComputedStyles } from 'roosterjs-editor-dom'; -import { EditorCore, GetStyleBasedFormatState, NodeType } from 'roosterjs-editor-types'; +import { NodeType } from 'roosterjs-editor-types'; +import type { EditorCore, GetStyleBasedFormatState } from 'roosterjs-editor-types'; /** * @internal diff --git a/packages/roosterjs-editor-core/lib/coreApi/hasFocus.ts b/packages/roosterjs-editor-core/lib/coreApi/hasFocus.ts index 07b80368b56..338df7f8db1 100644 --- a/packages/roosterjs-editor-core/lib/coreApi/hasFocus.ts +++ b/packages/roosterjs-editor-core/lib/coreApi/hasFocus.ts @@ -1,5 +1,5 @@ import { contains } from 'roosterjs-editor-dom'; -import { EditorCore, HasFocus } from 'roosterjs-editor-types'; +import type { EditorCore, HasFocus } from 'roosterjs-editor-types'; /** * @internal diff --git a/packages/roosterjs-editor-core/lib/coreApi/insertNode.ts b/packages/roosterjs-editor-core/lib/coreApi/insertNode.ts index b1775851c42..0371031d28f 100644 --- a/packages/roosterjs-editor-core/lib/coreApi/insertNode.ts +++ b/packages/roosterjs-editor-core/lib/coreApi/insertNode.ts @@ -1,13 +1,15 @@ -import { +import type { BlockElement, - ContentPosition, - ColorTransformDirection, EditorCore, InsertNode, InsertOption, + NodePosition, +} from 'roosterjs-editor-types'; +import { + ContentPosition, + ColorTransformDirection, NodeType, PositionType, - NodePosition, RegionType, } from 'roosterjs-editor-types'; import { diff --git a/packages/roosterjs-editor-core/lib/coreApi/restoreUndoSnapshot.ts b/packages/roosterjs-editor-core/lib/coreApi/restoreUndoSnapshot.ts index dd336fa5ab9..4433ebc3b7c 100644 --- a/packages/roosterjs-editor-core/lib/coreApi/restoreUndoSnapshot.ts +++ b/packages/roosterjs-editor-core/lib/coreApi/restoreUndoSnapshot.ts @@ -1,10 +1,6 @@ +import { EntityOperation, PluginEventType } from 'roosterjs-editor-types'; import { getEntityFromElement, getEntitySelector, queryElements } from 'roosterjs-editor-dom'; -import { - EditorCore, - EntityOperation, - PluginEventType, - RestoreUndoSnapshot, -} from 'roosterjs-editor-types'; +import type { EditorCore, RestoreUndoSnapshot } from 'roosterjs-editor-types'; /** * @internal diff --git a/packages/roosterjs-editor-core/lib/coreApi/select.ts b/packages/roosterjs-editor-core/lib/coreApi/select.ts index 24ca95f8687..ac5d151b140 100644 --- a/packages/roosterjs-editor-core/lib/coreApi/select.ts +++ b/packages/roosterjs-editor-core/lib/coreApi/select.ts @@ -1,13 +1,12 @@ import { contains, createRange, safeInstanceOf } from 'roosterjs-editor-dom'; -import { +import { PluginEventType, SelectionRangeTypes } from 'roosterjs-editor-types'; +import type { EditorCore, NodePosition, - PluginEventType, PositionType, Select, SelectionPath, SelectionRangeEx, - SelectionRangeTypes, TableSelection, } from 'roosterjs-editor-types'; diff --git a/packages/roosterjs-editor-core/lib/coreApi/selectImage.ts b/packages/roosterjs-editor-core/lib/coreApi/selectImage.ts index 4c55f9de061..44bcfb974e6 100644 --- a/packages/roosterjs-editor-core/lib/coreApi/selectImage.ts +++ b/packages/roosterjs-editor-core/lib/coreApi/selectImage.ts @@ -1,4 +1,5 @@ import addUniqueId from './utils/addUniqueId'; +import { PositionType, SelectionRangeTypes } from 'roosterjs-editor-types'; import { createRange, Position, @@ -6,13 +7,7 @@ import { removeImportantStyleRule, setGlobalCssStyles, } from 'roosterjs-editor-dom'; -import { - EditorCore, - ImageSelectionRange, - PositionType, - SelectImage, - SelectionRangeTypes, -} from 'roosterjs-editor-types'; +import type { EditorCore, ImageSelectionRange, SelectImage } from 'roosterjs-editor-types'; const IMAGE_ID = 'imageSelected'; const CONTENT_DIV_ID = 'contentDiv_'; diff --git a/packages/roosterjs-editor-core/lib/coreApi/selectRange.ts b/packages/roosterjs-editor-core/lib/coreApi/selectRange.ts index dbd83fb088d..d132ba7d163 100644 --- a/packages/roosterjs-editor-core/lib/coreApi/selectRange.ts +++ b/packages/roosterjs-editor-core/lib/coreApi/selectRange.ts @@ -1,5 +1,5 @@ -import { EditorCore, SelectRange } from 'roosterjs-editor-types'; import { hasFocus } from './hasFocus'; +import type { EditorCore, SelectRange } from 'roosterjs-editor-types'; import { contains, getPendableFormatState, diff --git a/packages/roosterjs-editor-core/lib/coreApi/selectTable.ts b/packages/roosterjs-editor-core/lib/coreApi/selectTable.ts index 67ae0a4e82f..060ed68e836 100644 --- a/packages/roosterjs-editor-core/lib/coreApi/selectTable.ts +++ b/packages/roosterjs-editor-core/lib/coreApi/selectTable.ts @@ -1,4 +1,5 @@ import addUniqueId from './utils/addUniqueId'; +import { PositionType, SelectionRangeTypes } from 'roosterjs-editor-types'; import { createRange, getTagOfNode, @@ -10,14 +11,7 @@ import { toArray, VTable, } from 'roosterjs-editor-dom'; -import { - EditorCore, - SelectionRangeTypes, - TableSelection, - SelectTable, - PositionType, - Coordinates, -} from 'roosterjs-editor-types'; +import type { EditorCore, TableSelection, SelectTable, Coordinates } from 'roosterjs-editor-types'; const TABLE_ID = 'tableSelected'; const CONTENT_DIV_ID = 'contentDiv_'; diff --git a/packages/roosterjs-editor-core/lib/coreApi/setContent.ts b/packages/roosterjs-editor-core/lib/coreApi/setContent.ts index f4ec3eed15b..58e8eb86c72 100644 --- a/packages/roosterjs-editor-core/lib/coreApi/setContent.ts +++ b/packages/roosterjs-editor-core/lib/coreApi/setContent.ts @@ -1,18 +1,16 @@ -import { - createRange, - extractContentMetadata, - queryElements, - restoreContentWithEntityPlaceholder, -} from 'roosterjs-editor-dom'; import { ChangeSource, ColorTransformDirection, - ContentMetadata, - EditorCore, PluginEventType, SelectionRangeTypes, - SetContent, } from 'roosterjs-editor-types'; +import { + createRange, + extractContentMetadata, + queryElements, + restoreContentWithEntityPlaceholder, +} from 'roosterjs-editor-dom'; +import type { ContentMetadata, EditorCore, SetContent } from 'roosterjs-editor-types'; /** * @internal diff --git a/packages/roosterjs-editor-core/lib/coreApi/switchShadowEdit.ts b/packages/roosterjs-editor-core/lib/coreApi/switchShadowEdit.ts index 7be80974110..95eb4b16965 100644 --- a/packages/roosterjs-editor-core/lib/coreApi/switchShadowEdit.ts +++ b/packages/roosterjs-editor-core/lib/coreApi/switchShadowEdit.ts @@ -1,16 +1,11 @@ +import { PluginEventType, SelectionRangeTypes } from 'roosterjs-editor-types'; import { createRange, getSelectionPath, moveContentWithEntityPlaceholders, restoreContentWithEntityPlaceholder, } from 'roosterjs-editor-dom'; -import { - EditorCore, - PluginEventType, - SelectionRangeEx, - SelectionRangeTypes, - SwitchShadowEdit, -} from 'roosterjs-editor-types'; +import type { EditorCore, SelectionRangeEx, SwitchShadowEdit } from 'roosterjs-editor-types'; /** * @internal diff --git a/packages/roosterjs-editor-core/lib/coreApi/transformColor.ts b/packages/roosterjs-editor-core/lib/coreApi/transformColor.ts index 96bf5fc8575..c0b89cc38c1 100644 --- a/packages/roosterjs-editor-core/lib/coreApi/transformColor.ts +++ b/packages/roosterjs-editor-core/lib/coreApi/transformColor.ts @@ -1,4 +1,5 @@ -import { ColorTransformDirection, EditorCore, TransformColor } from 'roosterjs-editor-types'; +import { ColorTransformDirection } from 'roosterjs-editor-types'; +import type { EditorCore, TransformColor } from 'roosterjs-editor-types'; import type { CompatibleColorTransformDirection } from 'roosterjs-editor-types/lib/compatibleTypes'; /** diff --git a/packages/roosterjs-editor-core/lib/coreApi/triggerEvent.ts b/packages/roosterjs-editor-core/lib/coreApi/triggerEvent.ts index e8a5faaeb78..7fc7272bf7d 100644 --- a/packages/roosterjs-editor-core/lib/coreApi/triggerEvent.ts +++ b/packages/roosterjs-editor-core/lib/coreApi/triggerEvent.ts @@ -1,10 +1,5 @@ -import { - EditorCore, - EditorPlugin, - PluginEvent, - PluginEventType, - TriggerEvent, -} from 'roosterjs-editor-types'; +import { PluginEventType } from 'roosterjs-editor-types'; +import type { EditorCore, EditorPlugin, PluginEvent, TriggerEvent } from 'roosterjs-editor-types'; import type { CompatiblePluginEventType } from 'roosterjs-editor-types/lib/compatibleTypes'; const allowedEventsInShadowEdit: (PluginEventType | CompatiblePluginEventType)[] = [ diff --git a/packages/roosterjs-editor-core/lib/corePlugins/CopyPastePlugin.ts b/packages/roosterjs-editor-core/lib/corePlugins/CopyPastePlugin.ts index 71da8535fed..b968d9a12cb 100644 --- a/packages/roosterjs-editor-core/lib/corePlugins/CopyPastePlugin.ts +++ b/packages/roosterjs-editor-core/lib/corePlugins/CopyPastePlugin.ts @@ -11,18 +11,20 @@ import { VTable, isWholeTableSelected, } from 'roosterjs-editor-dom'; -import { - ChangeSource, +import type { CopyPastePluginState, EditorOptions, - GetContentMode, IEditor, - PluginEventType, PluginWithState, - KnownCreateElementDataIndex, SelectionRangeEx, - SelectionRangeTypes, TableSelection, +} from 'roosterjs-editor-types'; +import { + ChangeSource, + GetContentMode, + PluginEventType, + KnownCreateElementDataIndex, + SelectionRangeTypes, TableOperation, } from 'roosterjs-editor-types'; diff --git a/packages/roosterjs-editor-core/lib/corePlugins/DOMEventPlugin.ts b/packages/roosterjs-editor-core/lib/corePlugins/DOMEventPlugin.ts index 9f0e9306113..206ab44193e 100644 --- a/packages/roosterjs-editor-core/lib/corePlugins/DOMEventPlugin.ts +++ b/packages/roosterjs-editor-core/lib/corePlugins/DOMEventPlugin.ts @@ -1,14 +1,12 @@ import { arrayPush, Browser, isCharacterValue } from 'roosterjs-editor-dom'; -import { - ChangeSource, +import { ChangeSource, Keys, PluginEventType } from 'roosterjs-editor-types'; +import type { ContextMenuProvider, DOMEventHandler, DOMEventPluginState, EditorOptions, EditorPlugin, IEditor, - Keys, - PluginEventType, PluginWithState, } from 'roosterjs-editor-types'; diff --git a/packages/roosterjs-editor-core/lib/corePlugins/EditPlugin.ts b/packages/roosterjs-editor-core/lib/corePlugins/EditPlugin.ts index 8cbaf4fa405..ea627186823 100644 --- a/packages/roosterjs-editor-core/lib/corePlugins/EditPlugin.ts +++ b/packages/roosterjs-editor-core/lib/corePlugins/EditPlugin.ts @@ -1,11 +1,10 @@ import { isCtrlOrMetaPressed } from 'roosterjs-editor-dom'; -import { +import { Keys, PluginEventType } from 'roosterjs-editor-types'; +import type { EditPluginState, GenericContentEditFeature, IEditor, - Keys, PluginEvent, - PluginEventType, PluginWithState, } from 'roosterjs-editor-types'; diff --git a/packages/roosterjs-editor-core/lib/corePlugins/EntityPlugin.ts b/packages/roosterjs-editor-core/lib/corePlugins/EntityPlugin.ts index 434edcdcfa5..df8d8198e33 100644 --- a/packages/roosterjs-editor-core/lib/corePlugins/EntityPlugin.ts +++ b/packages/roosterjs-editor-core/lib/corePlugins/EntityPlugin.ts @@ -16,24 +16,25 @@ import { isBlockElement, getObjectKeys, } from 'roosterjs-editor-dom'; -import { - ChangeSource, +import type { ContentChangedEvent, - ContentPosition, Entity, - EntityClasses, - EntityOperation, EntityOperationEvent, EntityPluginState, KnownEntityItem, - ExperimentalFeatures, HtmlSanitizerOptions, IEditor, - Keys, PluginEvent, - PluginEventType, PluginMouseUpEvent, PluginWithState, +} from 'roosterjs-editor-types'; +import { + ChangeSource, + ContentPosition, + EntityClasses, + EntityOperation, + Keys, + PluginEventType, QueryScope, } from 'roosterjs-editor-types'; import type { CompatibleEntityOperation } from 'roosterjs-editor-types/lib/compatibleTypes'; @@ -141,7 +142,7 @@ export default class EntityPlugin implements PluginWithState break; } - if (this.editor?.isFeatureEnabled(ExperimentalFeatures.InlineEntityReadOnlyDelimiters)) { + if (this.editor) { inlineEntityOnPluginEvent(event, this.editor); } } @@ -247,10 +248,7 @@ export default class EntityPlugin implements PluginWithState this.handleNewEntity(entity); }); - if ( - shouldNormalizeDelimiters && - this.editor?.isFeatureEnabled(ExperimentalFeatures.InlineEntityReadOnlyDelimiters) - ) { + if (shouldNormalizeDelimiters && this.editor) { normalizeDelimitersInEditor(this.editor); } } diff --git a/packages/roosterjs-editor-core/lib/corePlugins/ImageSelection.ts b/packages/roosterjs-editor-core/lib/corePlugins/ImageSelection.ts index a0f6a871ac8..3b635e1d06f 100644 --- a/packages/roosterjs-editor-core/lib/corePlugins/ImageSelection.ts +++ b/packages/roosterjs-editor-core/lib/corePlugins/ImageSelection.ts @@ -1,13 +1,6 @@ -import { safeInstanceOf } from 'roosterjs-editor-dom'; - -import { - EditorPlugin, - IEditor, - PluginEvent, - PluginEventType, - PositionType, - SelectionRangeTypes, -} from 'roosterjs-editor-types'; +import { PluginEventType, PositionType, SelectionRangeTypes } from 'roosterjs-editor-types'; +import { Position, safeInstanceOf } from 'roosterjs-editor-dom'; +import type { EditorPlugin, IEditor, PluginEvent } from 'roosterjs-editor-types'; const Escape = 'Escape'; const Delete = 'Delete'; @@ -45,14 +38,6 @@ export default class ImageSelection implements EditorPlugin { onPluginEvent(event: PluginEvent) { if (this.editor) { switch (event.eventType) { - case PluginEventType.EnteredShadowEdit: - case PluginEventType.LeavingShadowEdit: - const selection = this.editor.getSelectionRangeEx(); - if (selection.type == SelectionRangeTypes.ImageSelection) { - this.editor.select(selection.image); - } - break; - case PluginEventType.MouseUp: const target = event.rawEvent.target; if ( @@ -74,10 +59,17 @@ export default class ImageSelection implements EditorPlugin { this.editor.select(null); } break; - case PluginEventType.KeyUp: - const key = event.rawEvent.key; + case PluginEventType.KeyDown: + const rawEvent = event.rawEvent; + const key = rawEvent.key; const keyDownSelection = this.editor.getSelectionRangeEx(); - if (keyDownSelection.type === SelectionRangeTypes.ImageSelection) { + if ( + !rawEvent.ctrlKey && + !rawEvent.altKey && + !rawEvent.shiftKey && + !rawEvent.metaKey && + keyDownSelection.type === SelectionRangeTypes.ImageSelection + ) { if (key === Escape) { this.editor.select(keyDownSelection.image, PositionType.Before); this.editor.getSelectionRange()?.collapse(); @@ -86,7 +78,12 @@ export default class ImageSelection implements EditorPlugin { this.editor.deleteNode(keyDownSelection.image); event.rawEvent.preventDefault(); } else { - this.editor.select(keyDownSelection.ranges[0]); + const position = new Position( + keyDownSelection.image, + PositionType.Begin + ); + + this.editor.select(position); } } break; diff --git a/packages/roosterjs-editor-core/lib/corePlugins/LifecyclePlugin.ts b/packages/roosterjs-editor-core/lib/corePlugins/LifecyclePlugin.ts index e0b06e9526f..74cd4048bbd 100644 --- a/packages/roosterjs-editor-core/lib/corePlugins/LifecyclePlugin.ts +++ b/packages/roosterjs-editor-core/lib/corePlugins/LifecyclePlugin.ts @@ -1,13 +1,11 @@ import { Browser, getObjectKeys, setColor } from 'roosterjs-editor-dom'; -import { - DocumentCommand, +import { ChangeSource, DocumentCommand, PluginEventType } from 'roosterjs-editor-types'; +import type { EditorOptions, IEditor, LifecyclePluginState, - PluginEventType, PluginWithState, PluginEvent, - ChangeSource, } from 'roosterjs-editor-types'; const CONTENT_EDITABLE_ATTRIBUTE_NAME = 'contenteditable'; diff --git a/packages/roosterjs-editor-core/lib/corePlugins/MouseUpPlugin.ts b/packages/roosterjs-editor-core/lib/corePlugins/MouseUpPlugin.ts index 53009bea9f4..2f072e3b10d 100644 --- a/packages/roosterjs-editor-core/lib/corePlugins/MouseUpPlugin.ts +++ b/packages/roosterjs-editor-core/lib/corePlugins/MouseUpPlugin.ts @@ -1,4 +1,5 @@ -import { EditorPlugin, IEditor, PluginEvent, PluginEventType } from 'roosterjs-editor-types'; +import { PluginEventType } from 'roosterjs-editor-types'; +import type { EditorPlugin, IEditor, PluginEvent } from 'roosterjs-editor-types'; /** * @internal diff --git a/packages/roosterjs-editor-core/lib/corePlugins/NormalizeTablePlugin.ts b/packages/roosterjs-editor-core/lib/corePlugins/NormalizeTablePlugin.ts index d2b1f7397ab..79ce990da14 100644 --- a/packages/roosterjs-editor-core/lib/corePlugins/NormalizeTablePlugin.ts +++ b/packages/roosterjs-editor-core/lib/corePlugins/NormalizeTablePlugin.ts @@ -1,3 +1,4 @@ +import { PluginEventType, SelectionRangeTypes } from 'roosterjs-editor-types'; import { changeElementTag, getTagOfNode, @@ -5,13 +6,7 @@ import { safeInstanceOf, toArray, } from 'roosterjs-editor-dom'; -import { - EditorPlugin, - IEditor, - PluginEvent, - PluginEventType, - SelectionRangeTypes, -} from 'roosterjs-editor-types'; +import type { EditorPlugin, IEditor, PluginEvent } from 'roosterjs-editor-types'; /** * @internal diff --git a/packages/roosterjs-editor-core/lib/corePlugins/PendingFormatStatePlugin.ts b/packages/roosterjs-editor-core/lib/corePlugins/PendingFormatStatePlugin.ts index eff1d32c626..a39427c1380 100644 --- a/packages/roosterjs-editor-core/lib/corePlugins/PendingFormatStatePlugin.ts +++ b/packages/roosterjs-editor-core/lib/corePlugins/PendingFormatStatePlugin.ts @@ -1,14 +1,11 @@ +import { ChangeSource, Keys, PluginEventType, PositionType } from 'roosterjs-editor-types'; import { isCharacterValue, Position, setColor } from 'roosterjs-editor-dom'; -import { - ChangeSource, +import type { IEditor, - Keys, NodePosition, PendingFormatStatePluginState, PluginEvent, - PluginEventType, PluginWithState, - PositionType, } from 'roosterjs-editor-types'; const ZERO_WIDTH_SPACE = '\u200B'; diff --git a/packages/roosterjs-editor-core/lib/corePlugins/TypeInContainerPlugin.ts b/packages/roosterjs-editor-core/lib/corePlugins/TypeInContainerPlugin.ts index 2aaf930a6a6..dfda48501d6 100644 --- a/packages/roosterjs-editor-core/lib/corePlugins/TypeInContainerPlugin.ts +++ b/packages/roosterjs-editor-core/lib/corePlugins/TypeInContainerPlugin.ts @@ -1,4 +1,5 @@ -import { EditorPlugin, IEditor, PluginEvent, PluginEventType } from 'roosterjs-editor-types'; +import { PluginEventType } from 'roosterjs-editor-types'; +import type { EditorPlugin, IEditor, PluginEvent } from 'roosterjs-editor-types'; import { Browser, findClosestElementAncestor, diff --git a/packages/roosterjs-editor-core/lib/corePlugins/UndoPlugin.ts b/packages/roosterjs-editor-core/lib/corePlugins/UndoPlugin.ts index 17456e12881..ca5c2e20fe4 100644 --- a/packages/roosterjs-editor-core/lib/corePlugins/UndoPlugin.ts +++ b/packages/roosterjs-editor-core/lib/corePlugins/UndoPlugin.ts @@ -1,11 +1,9 @@ -import { - ChangeSource, +import { ChangeSource, Keys, PluginEventType } from 'roosterjs-editor-types'; +import type { ContentChangedEvent, EditorOptions, IEditor, - Keys, PluginEvent, - PluginEventType, PluginWithState, Snapshot, UndoPluginState, diff --git a/packages/roosterjs-editor-core/lib/corePlugins/createCorePlugins.ts b/packages/roosterjs-editor-core/lib/corePlugins/createCorePlugins.ts index c35e74e2773..f538a066bd6 100644 --- a/packages/roosterjs-editor-core/lib/corePlugins/createCorePlugins.ts +++ b/packages/roosterjs-editor-core/lib/corePlugins/createCorePlugins.ts @@ -9,7 +9,7 @@ import NormalizeTablePlugin from './NormalizeTablePlugin'; import PendingFormatStatePlugin from './PendingFormatStatePlugin'; import TypeInContainerPlugin from './TypeInContainerPlugin'; import UndoPlugin from './UndoPlugin'; -import { CorePlugins, EditorOptions, PluginState } from 'roosterjs-editor-types'; +import type { CorePlugins, EditorOptions, PluginState } from 'roosterjs-editor-types'; /** * @internal diff --git a/packages/roosterjs-editor-core/lib/corePlugins/utils/forEachSelectedCell.ts b/packages/roosterjs-editor-core/lib/corePlugins/utils/forEachSelectedCell.ts index 1803e8a1063..edd97016f3c 100644 --- a/packages/roosterjs-editor-core/lib/corePlugins/utils/forEachSelectedCell.ts +++ b/packages/roosterjs-editor-core/lib/corePlugins/utils/forEachSelectedCell.ts @@ -1,5 +1,5 @@ -import { VCell } from 'roosterjs-editor-types'; -import { VTable } from 'roosterjs-editor-dom'; +import type { VCell } from 'roosterjs-editor-types'; +import type { VTable } from 'roosterjs-editor-dom'; /** * @internal diff --git a/packages/roosterjs-editor-core/lib/corePlugins/utils/inlineEntityOnPluginEvent.ts b/packages/roosterjs-editor-core/lib/corePlugins/utils/inlineEntityOnPluginEvent.ts index 7579636c668..8385ef33524 100644 --- a/packages/roosterjs-editor-core/lib/corePlugins/utils/inlineEntityOnPluginEvent.ts +++ b/packages/roosterjs-editor-core/lib/corePlugins/utils/inlineEntityOnPluginEvent.ts @@ -12,16 +12,13 @@ import { safeInstanceOf, splitTextNode, } from 'roosterjs-editor-dom'; +import type { Entity, IEditor, PluginEvent, PluginKeyDownEvent } from 'roosterjs-editor-types'; import { ChangeSource, DelimiterClasses, - Entity, - IEditor, Keys, NodeType, - PluginEvent, PluginEventType, - PluginKeyDownEvent, PositionType, SelectionRangeTypes, } from 'roosterjs-editor-types'; diff --git a/packages/roosterjs-editor-core/lib/corePlugins/utils/removeCellsOutsideSelection.ts b/packages/roosterjs-editor-core/lib/corePlugins/utils/removeCellsOutsideSelection.ts index 8e66080dd3f..e2c75d2f55b 100644 --- a/packages/roosterjs-editor-core/lib/corePlugins/utils/removeCellsOutsideSelection.ts +++ b/packages/roosterjs-editor-core/lib/corePlugins/utils/removeCellsOutsideSelection.ts @@ -1,5 +1,6 @@ -import { isWholeTableSelected, VTable } from 'roosterjs-editor-dom'; -import { VCell } from 'roosterjs-editor-types'; +import { isWholeTableSelected } from 'roosterjs-editor-dom'; +import type { VTable } from 'roosterjs-editor-dom'; +import type { VCell } from 'roosterjs-editor-types'; /** * @internal diff --git a/packages/roosterjs-editor-core/lib/editor/DarkColorHandlerImpl.ts b/packages/roosterjs-editor-core/lib/editor/DarkColorHandlerImpl.ts index e10f1b5c17b..f87a42cb7f0 100644 --- a/packages/roosterjs-editor-core/lib/editor/DarkColorHandlerImpl.ts +++ b/packages/roosterjs-editor-core/lib/editor/DarkColorHandlerImpl.ts @@ -1,5 +1,9 @@ -import { ColorKeyAndValue, DarkColorHandler, ModeIndependentColor } from 'roosterjs-editor-types'; import { getObjectKeys, parseColor, setColor } from 'roosterjs-editor-dom'; +import type { + ColorKeyAndValue, + DarkColorHandler, + ModeIndependentColor, +} from 'roosterjs-editor-types'; const VARIABLE_REGEX = /^\s*var\(\s*(\-\-[a-zA-Z0-9\-_]+)\s*(?:,\s*(.*))?\)\s*$/; const VARIABLE_PREFIX = 'var('; diff --git a/packages/roosterjs-editor-core/lib/editor/Editor.ts b/packages/roosterjs-editor-core/lib/editor/Editor.ts index 60a89324382..2d2ddbc9251 100644 --- a/packages/roosterjs-editor-core/lib/editor/Editor.ts +++ b/packages/roosterjs-editor-core/lib/editor/Editor.ts @@ -1,6 +1,6 @@ import { createEditorCore } from './createEditorCore'; import { EditorBase } from './EditorBase'; -import { EditorCore, EditorOptions } from 'roosterjs-editor-types'; +import type { EditorCore, EditorOptions } from 'roosterjs-editor-types'; /** * RoosterJs core editor class diff --git a/packages/roosterjs-editor-core/lib/editor/EditorBase.ts b/packages/roosterjs-editor-core/lib/editor/EditorBase.ts index 4b949700a32..8712c689075 100644 --- a/packages/roosterjs-editor-core/lib/editor/EditorBase.ts +++ b/packages/roosterjs-editor-core/lib/editor/EditorBase.ts @@ -1,11 +1,18 @@ import { isFeatureEnabled } from './isFeatureEnabled'; import { - BlockElement, ChangeSource, - ClipboardData, ColorTransformDirection, - ContentChangedData, ContentPosition, + GetContentMode, + PluginEventType, + PositionType, + QueryScope, + RegionType, +} from 'roosterjs-editor-types'; +import type { + BlockElement, + ClipboardData, + ContentChangedData, CoreCreator, DarkColorHandler, DefaultFormat, @@ -15,7 +22,6 @@ import { EditorUndoState, ExperimentalFeatures, GenericContentEditFeature, - GetContentMode, IContentTraverser, IEditor, InsertOption, @@ -25,12 +31,8 @@ import { PluginEvent, PluginEventData, PluginEventFromType, - PluginEventType, - PositionType, - QueryScope, Rect, Region, - RegionType, SelectionPath, SelectionRangeEx, SizeTransformer, @@ -109,8 +111,16 @@ export class EditorBase= 0; i--) { - core.plugins[i].dispose(); + const plugin = core.plugins[i]; + + try { + plugin.dispose(); + } catch (e) { + // Cache the error and pass it out, then keep going since dispose should always succeed + core.disposeErrorHandler?.(plugin, e as Error); + } } core.darkColorHandler.reset(); diff --git a/packages/roosterjs-editor-core/lib/editor/createEditorCore.ts b/packages/roosterjs-editor-core/lib/editor/createEditorCore.ts index 591866f2c58..7bdaca0b31e 100644 --- a/packages/roosterjs-editor-core/lib/editor/createEditorCore.ts +++ b/packages/roosterjs-editor-core/lib/editor/createEditorCore.ts @@ -2,7 +2,7 @@ import createCorePlugins, { getPluginState } from '../corePlugins/createCorePlug import DarkColorHandlerImpl from './DarkColorHandlerImpl'; import { arrayPush, getIntersectedRect, getObjectKeys } from 'roosterjs-editor-dom'; import { coreApiMap } from '../coreApi/coreApiMap'; -import { CoreCreator, EditorCore, EditorOptions, EditorPlugin } from 'roosterjs-editor-types'; +import type { CoreCreator, EditorCore, EditorOptions, EditorPlugin } from 'roosterjs-editor-types'; /** * Create a new instance of Editor Core @@ -52,6 +52,7 @@ export const createEditorCore: CoreCreator = (content getVisibleViewport, imageSelectionBorderColor: options.imageSelectionBorderColor, darkColorHandler: new DarkColorHandlerImpl(contentDiv, pluginState.lifecycle.getDarkColor), + disposeErrorHandler: options.disposeErrorHandler, }; return core; diff --git a/packages/roosterjs-editor-core/lib/editor/isFeatureEnabled.ts b/packages/roosterjs-editor-core/lib/editor/isFeatureEnabled.ts index ac2016951e7..84a9b5f827e 100644 --- a/packages/roosterjs-editor-core/lib/editor/isFeatureEnabled.ts +++ b/packages/roosterjs-editor-core/lib/editor/isFeatureEnabled.ts @@ -1,4 +1,4 @@ -import { ExperimentalFeatures } from 'roosterjs-editor-types'; +import type { ExperimentalFeatures } from 'roosterjs-editor-types'; import type { CompatibleExperimentalFeatures } from 'roosterjs-editor-types/lib/compatibleTypes'; /** diff --git a/packages/roosterjs-editor-core/test/corePlugins/entityPluginTest.ts b/packages/roosterjs-editor-core/test/corePlugins/entityPluginTest.ts index 219f1eaa072..950449146f5 100644 --- a/packages/roosterjs-editor-core/test/corePlugins/entityPluginTest.ts +++ b/packages/roosterjs-editor-core/test/corePlugins/entityPluginTest.ts @@ -1,5 +1,6 @@ import * as commitEntity from 'roosterjs-editor-dom/lib/entity/commitEntity'; import * as getEntityFromElement from 'roosterjs-editor-dom/lib/entity/getEntityFromElement'; +import * as inlineEntityOnPluginEvent from '../../lib/corePlugins/utils/inlineEntityOnPluginEvent'; import EntityPlugin from '../../lib/corePlugins/EntityPlugin'; import { createDefaultHtmlSanitizerOptions } from 'roosterjs-editor-dom'; import { @@ -23,6 +24,8 @@ describe('EntityPlugin', () => { beforeEach(() => { triggerPluginEvent = jasmine.createSpy('triggerPluginEvent'); + spyOn(inlineEntityOnPluginEvent, 'inlineEntityOnPluginEvent'); + plugin = new EntityPlugin(); state = plugin.getState(); editor = ({ diff --git a/packages/roosterjs-editor-core/test/corePlugins/imageSelectionTest.ts b/packages/roosterjs-editor-core/test/corePlugins/imageSelectionTest.ts index 99972753678..60331218a62 100644 --- a/packages/roosterjs-editor-core/test/corePlugins/imageSelectionTest.ts +++ b/packages/roosterjs-editor-core/test/corePlugins/imageSelectionTest.ts @@ -67,26 +67,6 @@ describe('ImageSelectionPlugin |', () => { expect(selection.areAllCollapsed).toBe(false); }); - it('should be triggered in shadow Edit', () => { - editor.setContent(``); - const target = document.getElementById(imageId); - editorIsFeatureEnabled.and.returnValue(true); - editor.focus(); - editor.select(target); - - editor.startShadowEdit(); - - let selection = editor.getSelectionRangeEx(); - expect(selection.type).toBe(SelectionRangeTypes.ImageSelection); - expect(selection.areAllCollapsed).toBe(false); - - editor.stopShadowEdit(); - - selection = editor.getSelectionRangeEx(); - expect(selection.type).toBe(SelectionRangeTypes.ImageSelection); - expect(selection.areAllCollapsed).toBe(false); - }); - it('should handle a ESCAPE KEY in a image', () => { editor.setContent(``); const target = document.getElementById(imageId); @@ -127,6 +107,21 @@ describe('ImageSelectionPlugin |', () => { imageSelection.onPluginEvent(keyUp(Space)); const selection = editor.getSelectionRangeEx(); expect(selection.type).toBe(SelectionRangeTypes.Normal); + expect(selection.areAllCollapsed).toBe(true); + }); + + it('should not handle any key in a image in ctrl', () => { + editor.setContent(``); + const target = document.getElementById(imageId); + editorIsFeatureEnabled.and.returnValue(true); + editor.focus(); + editor.select(target); + const range = document.createRange(); + range.selectNode(target!); + imageSelection.onPluginEvent(keyDown(Space, true)); + imageSelection.onPluginEvent(keyUp(Space, true)); + const selection = editor.getSelectionRangeEx(); + expect(selection.type).toBe(SelectionRangeTypes.ImageSelection); expect(selection.areAllCollapsed).toBe(false); }); @@ -169,24 +164,32 @@ describe('ImageSelectionPlugin |', () => { expect(editor.select).not.toHaveBeenCalled(); }); - const keyDown = (key: string): PluginEvent => { + const keyDown = (key: string, ctrlKey: boolean = false): PluginEvent => { return { eventType: PluginEventType.KeyDown, rawEvent: { key: key, preventDefault: () => {}, stopPropagation: () => {}, + shiftKey: false, + ctrlKey: ctrlKey, + altKey: false, + metaKey: false, }, }; }; - const keyUp = (key: string): PluginEvent => { + const keyUp = (key: string, ctrlKey: boolean = false): PluginEvent => { return { eventType: PluginEventType.KeyUp, rawEvent: { key: key, preventDefault: () => {}, stopPropagation: () => {}, + shiftKey: false, + ctrlKey: ctrlKey, + altKey: false, + metaKey: false, }, }; }; diff --git a/packages/roosterjs-editor-core/test/editor/EditorTest.ts b/packages/roosterjs-editor-core/test/editor/EditorTest.ts index ccbcfa82e8e..cf990c0e170 100644 --- a/packages/roosterjs-editor-core/test/editor/EditorTest.ts +++ b/packages/roosterjs-editor-core/test/editor/EditorTest.ts @@ -1,6 +1,7 @@ import * as getSelectionRange from '../../lib/coreApi/getSelectionRange'; import * as TestHelper from '../TestHelper'; -import { ContentPosition, IEditor } from 'roosterjs-editor-types'; +import Editor from '../../lib/editor/Editor'; +import { ContentPosition, EditorPlugin, IEditor } from 'roosterjs-editor-types'; let editor: IEditor; let testID = 'EditorTest'; @@ -509,3 +510,40 @@ describe('Editor getCustomData()', () => { expect(objCount).toBe(0); }); }); + +describe('Dispose with exception', () => { + it('handle exception when dispose', () => { + const errorMsg = 'Test error'; + const disposeSpy1 = jasmine.createSpy('dispose1'); + const disposeSpy2 = jasmine.createSpy('dispose2').and.throwError(errorMsg); + const disposeSpy3 = jasmine.createSpy('dispose3'); + const handlerSpy = jasmine.createSpy('disposeErrorhandler'); + + const plugin1 = ({ + initialize: () => {}, + dispose: disposeSpy1, + } as any) as EditorPlugin; + const plugin2 = ({ + initialize: () => {}, + dispose: disposeSpy2, + } as any) as EditorPlugin; + const plugin3 = ({ + initialize: () => {}, + dispose: disposeSpy3, + } as any) as EditorPlugin; + + const div = document.createElement('div'); + const editor = new Editor(div, { + plugins: [plugin1, plugin2, plugin3], + disposeErrorHandler: handlerSpy, + }); + + editor.dispose(); + + expect(disposeSpy1).toHaveBeenCalledTimes(1); + expect(disposeSpy2).toHaveBeenCalledTimes(1); + expect(disposeSpy3).toHaveBeenCalledTimes(1); + expect(handlerSpy).toHaveBeenCalledTimes(1); + expect(handlerSpy).toHaveBeenCalledWith(plugin2, new Error(errorMsg)); + }); +}); diff --git a/packages/roosterjs-editor-dom/lib/blockElements/NodeBlockElement.ts b/packages/roosterjs-editor-dom/lib/blockElements/NodeBlockElement.ts index 3075a1ff4f7..7812a518e61 100644 --- a/packages/roosterjs-editor-dom/lib/blockElements/NodeBlockElement.ts +++ b/packages/roosterjs-editor-dom/lib/blockElements/NodeBlockElement.ts @@ -1,6 +1,6 @@ import contains from '../utils/contains'; import isNodeAfter from '../utils/isNodeAfter'; -import { BlockElement } from 'roosterjs-editor-types'; +import type { BlockElement } from 'roosterjs-editor-types'; /** * @internal diff --git a/packages/roosterjs-editor-dom/lib/blockElements/StartEndBlockElement.ts b/packages/roosterjs-editor-dom/lib/blockElements/StartEndBlockElement.ts index cf118de165d..8486a965e9b 100644 --- a/packages/roosterjs-editor-dom/lib/blockElements/StartEndBlockElement.ts +++ b/packages/roosterjs-editor-dom/lib/blockElements/StartEndBlockElement.ts @@ -5,8 +5,8 @@ import getTagOfNode from '../utils/getTagOfNode'; import isBlockElement from '../utils/isBlockElement'; import isNodeAfter from '../utils/isNodeAfter'; import wrap from '../utils/wrap'; -import { BlockElement } from 'roosterjs-editor-types'; import { splitBalancedNodeRange } from '../utils/splitParentNode'; +import type { BlockElement } from 'roosterjs-editor-types'; const STRUCTURE_NODE_TAGS = ['TD', 'TH', 'LI', 'BLOCKQUOTE']; diff --git a/packages/roosterjs-editor-dom/lib/blockElements/getBlockElementAtNode.ts b/packages/roosterjs-editor-dom/lib/blockElements/getBlockElementAtNode.ts index a024f72e0a8..80e59cfb77f 100644 --- a/packages/roosterjs-editor-dom/lib/blockElements/getBlockElementAtNode.ts +++ b/packages/roosterjs-editor-dom/lib/blockElements/getBlockElementAtNode.ts @@ -4,7 +4,7 @@ import getTagOfNode from '../utils/getTagOfNode'; import isBlockElement from '../utils/isBlockElement'; import NodeBlockElement from './NodeBlockElement'; import StartEndBlockElement from './StartEndBlockElement'; -import { BlockElement } from 'roosterjs-editor-types'; +import type { BlockElement } from 'roosterjs-editor-types'; /** * This produces a block element from a a node diff --git a/packages/roosterjs-editor-dom/lib/blockElements/getFirstLastBlockElement.ts b/packages/roosterjs-editor-dom/lib/blockElements/getFirstLastBlockElement.ts index 4ff66882819..267bad2ec47 100644 --- a/packages/roosterjs-editor-dom/lib/blockElements/getFirstLastBlockElement.ts +++ b/packages/roosterjs-editor-dom/lib/blockElements/getFirstLastBlockElement.ts @@ -1,5 +1,5 @@ import getBlockElementAtNode from './getBlockElementAtNode'; -import { BlockElement } from 'roosterjs-editor-types'; +import type { BlockElement } from 'roosterjs-editor-types'; /** * Get the first/last BlockElement of under the root node. diff --git a/packages/roosterjs-editor-dom/lib/clipboard/extractClipboardEvent.ts b/packages/roosterjs-editor-dom/lib/clipboard/extractClipboardEvent.ts index 2ee876ab686..ba7d7650c58 100644 --- a/packages/roosterjs-editor-dom/lib/clipboard/extractClipboardEvent.ts +++ b/packages/roosterjs-editor-dom/lib/clipboard/extractClipboardEvent.ts @@ -2,7 +2,7 @@ import extractClipboardItems from './extractClipboardItems'; import extractClipboardItemsForIE from './extractClipboardItemsForIE'; import toArray from '../jsUtils/toArray'; import { Browser } from '../utils/Browser'; -import { ClipboardData, ExtractClipboardEventOption } from 'roosterjs-editor-types'; +import type { ClipboardData, ExtractClipboardEventOption } from 'roosterjs-editor-types'; interface WindowForIE extends Window { clipboardData: DataTransfer; diff --git a/packages/roosterjs-editor-dom/lib/clipboard/extractClipboardItems.ts b/packages/roosterjs-editor-dom/lib/clipboard/extractClipboardItems.ts index ce4d1dcf239..4646b49c196 100644 --- a/packages/roosterjs-editor-dom/lib/clipboard/extractClipboardItems.ts +++ b/packages/roosterjs-editor-dom/lib/clipboard/extractClipboardItems.ts @@ -1,9 +1,8 @@ import readFile from '../utils/readFile'; import { Browser } from '../utils/Browser'; -import { +import { ContentType, ContentTypePrefix } from 'roosterjs-editor-types'; +import type { ClipboardData, - ContentType, - ContentTypePrefix, EdgeLinkPreview, ExtractClipboardItemsOption, } from 'roosterjs-editor-types'; diff --git a/packages/roosterjs-editor-dom/lib/clipboard/extractClipboardItemsForIE.ts b/packages/roosterjs-editor-dom/lib/clipboard/extractClipboardItemsForIE.ts index c5565be50a6..225b34183b1 100644 --- a/packages/roosterjs-editor-dom/lib/clipboard/extractClipboardItemsForIE.ts +++ b/packages/roosterjs-editor-dom/lib/clipboard/extractClipboardItemsForIE.ts @@ -1,10 +1,7 @@ import readFile from '../utils/readFile'; import toArray from '../jsUtils/toArray'; -import { - ClipboardData, - ContentTypePrefix, - ExtractClipboardItemsForIEOptions, -} from 'roosterjs-editor-types'; +import { ContentTypePrefix } from 'roosterjs-editor-types'; +import type { ClipboardData, ExtractClipboardItemsForIEOptions } from 'roosterjs-editor-types'; /** * Extract clipboard items to be a ClipboardData object for IE diff --git a/packages/roosterjs-editor-dom/lib/clipboard/handleTextPaste.ts b/packages/roosterjs-editor-dom/lib/clipboard/handleTextPaste.ts index fe1a5e84859..ecde693f8d0 100644 --- a/packages/roosterjs-editor-dom/lib/clipboard/handleTextPaste.ts +++ b/packages/roosterjs-editor-dom/lib/clipboard/handleTextPaste.ts @@ -1,5 +1,5 @@ import wrap from '../utils/wrap'; -import { NodePosition } from 'roosterjs-editor-types'; +import type { NodePosition } from 'roosterjs-editor-types'; const NBSP_HTML = '\u00A0'; const ENSP_HTML = '\u2002'; diff --git a/packages/roosterjs-editor-dom/lib/clipboard/retrieveMetadataFromClipboard.ts b/packages/roosterjs-editor-dom/lib/clipboard/retrieveMetadataFromClipboard.ts index f1edc3f8172..95623030e2c 100644 --- a/packages/roosterjs-editor-dom/lib/clipboard/retrieveMetadataFromClipboard.ts +++ b/packages/roosterjs-editor-dom/lib/clipboard/retrieveMetadataFromClipboard.ts @@ -1,6 +1,6 @@ import getTagOfNode from '../utils/getTagOfNode'; import toArray from '../jsUtils/toArray'; -import { BeforePasteEvent, TrustedHTMLHandler } from 'roosterjs-editor-types'; +import type { BeforePasteEvent, TrustedHTMLHandler } from 'roosterjs-editor-types'; const START_FRAGMENT = ''; const END_FRAGMENT = ''; diff --git a/packages/roosterjs-editor-dom/lib/clipboard/sanitizePasteContent.ts b/packages/roosterjs-editor-dom/lib/clipboard/sanitizePasteContent.ts index f754f26620a..a9676bce065 100644 --- a/packages/roosterjs-editor-dom/lib/clipboard/sanitizePasteContent.ts +++ b/packages/roosterjs-editor-dom/lib/clipboard/sanitizePasteContent.ts @@ -1,6 +1,6 @@ import getInheritableStyles from '../htmlSanitizer/getInheritableStyles'; import HtmlSanitizer from '../htmlSanitizer/HtmlSanitizer'; -import { BeforePasteEvent, NodePosition } from 'roosterjs-editor-types'; +import type { BeforePasteEvent, NodePosition } from 'roosterjs-editor-types'; /** * Sanitize the content from the pasted content diff --git a/packages/roosterjs-editor-dom/lib/contentTraverser/BodyScoper.ts b/packages/roosterjs-editor-dom/lib/contentTraverser/BodyScoper.ts index e46b53f46e4..4dc1163be2d 100644 --- a/packages/roosterjs-editor-dom/lib/contentTraverser/BodyScoper.ts +++ b/packages/roosterjs-editor-dom/lib/contentTraverser/BodyScoper.ts @@ -2,9 +2,9 @@ import contains from '../utils/contains'; import getBlockElementAtNode from '../blockElements/getBlockElementAtNode'; import getFirstLastBlockElement from '../blockElements/getFirstLastBlockElement'; import getInlineElementAtNode from '../inlineElements/getInlineElementAtNode'; -import TraversingScoper from './TraversingScoper'; -import { BlockElement, InlineElement } from 'roosterjs-editor-types'; import { getFirstInlineElement } from '../inlineElements/getFirstLastInlineElement'; +import type TraversingScoper from './TraversingScoper'; +import type { BlockElement, InlineElement } from 'roosterjs-editor-types'; /** * @internal diff --git a/packages/roosterjs-editor-dom/lib/contentTraverser/ContentTraverser.ts b/packages/roosterjs-editor-dom/lib/contentTraverser/ContentTraverser.ts index edaa619b2f2..82b9cf7a033 100644 --- a/packages/roosterjs-editor-dom/lib/contentTraverser/ContentTraverser.ts +++ b/packages/roosterjs-editor-dom/lib/contentTraverser/ContentTraverser.ts @@ -5,13 +5,13 @@ import getInlineElementAtNode from '../inlineElements/getInlineElementAtNode'; import PartialInlineElement from '../inlineElements/PartialInlineElement'; import SelectionBlockScoper from './SelectionBlockScoper'; import SelectionScoper from './SelectionScoper'; -import TraversingScoper from './TraversingScoper'; +import { ContentPosition } from 'roosterjs-editor-types'; import { getInlineElementBeforeAfter } from '../inlineElements/getInlineElementBeforeAfter'; import { getLeafSibling } from '../utils/getLeafSibling'; +import type TraversingScoper from './TraversingScoper'; import type { CompatibleContentPosition } from 'roosterjs-editor-types/lib/compatibleTypes'; -import { +import type { BlockElement, - ContentPosition, IContentTraverser, InlineElement, NodePosition, diff --git a/packages/roosterjs-editor-dom/lib/contentTraverser/PositionContentSearcher.ts b/packages/roosterjs-editor-dom/lib/contentTraverser/PositionContentSearcher.ts index a857bd33608..76919e66a83 100644 --- a/packages/roosterjs-editor-dom/lib/contentTraverser/PositionContentSearcher.ts +++ b/packages/roosterjs-editor-dom/lib/contentTraverser/PositionContentSearcher.ts @@ -1,6 +1,6 @@ import ContentTraverser from './ContentTraverser'; import createRange from '../selection/createRange'; -import { +import type { IContentTraverser, InlineElement, IPositionContentSearcher, diff --git a/packages/roosterjs-editor-dom/lib/contentTraverser/SelectionBlockScoper.ts b/packages/roosterjs-editor-dom/lib/contentTraverser/SelectionBlockScoper.ts index f9bd685228f..790ce7aef8f 100644 --- a/packages/roosterjs-editor-dom/lib/contentTraverser/SelectionBlockScoper.ts +++ b/packages/roosterjs-editor-dom/lib/contentTraverser/SelectionBlockScoper.ts @@ -4,9 +4,10 @@ import getInlineElementAtNode from '../inlineElements/getInlineElementAtNode'; import NodeBlockElement from '../blockElements/NodeBlockElement'; import Position from '../selection/Position'; import safeInstanceOf from '../utils/safeInstanceOf'; -import TraversingScoper from './TraversingScoper'; -import { BlockElement, ContentPosition, InlineElement, NodePosition } from 'roosterjs-editor-types'; +import { ContentPosition } from 'roosterjs-editor-types'; import { getInlineElementAfter } from '../inlineElements/getInlineElementBeforeAfter'; +import type TraversingScoper from './TraversingScoper'; +import type { BlockElement, InlineElement, NodePosition } from 'roosterjs-editor-types'; import { getFirstInlineElement, getLastInlineElement, diff --git a/packages/roosterjs-editor-dom/lib/contentTraverser/SelectionScoper.ts b/packages/roosterjs-editor-dom/lib/contentTraverser/SelectionScoper.ts index 62474b71615..d1ab5bc978f 100644 --- a/packages/roosterjs-editor-dom/lib/contentTraverser/SelectionScoper.ts +++ b/packages/roosterjs-editor-dom/lib/contentTraverser/SelectionScoper.ts @@ -1,9 +1,9 @@ import getBlockElementAtNode from '../blockElements/getBlockElementAtNode'; import PartialInlineElement from '../inlineElements/PartialInlineElement'; import Position from '../selection/Position'; -import TraversingScoper from './TraversingScoper'; -import { BlockElement, InlineElement, NodePosition } from 'roosterjs-editor-types'; import { getInlineElementAfter } from '../inlineElements/getInlineElementBeforeAfter'; +import type TraversingScoper from './TraversingScoper'; +import type { BlockElement, InlineElement, NodePosition } from 'roosterjs-editor-types'; /** * @internal diff --git a/packages/roosterjs-editor-dom/lib/contentTraverser/TraversingScoper.ts b/packages/roosterjs-editor-dom/lib/contentTraverser/TraversingScoper.ts index f4e7e2ef5ba..dd81e76e830 100644 --- a/packages/roosterjs-editor-dom/lib/contentTraverser/TraversingScoper.ts +++ b/packages/roosterjs-editor-dom/lib/contentTraverser/TraversingScoper.ts @@ -1,4 +1,4 @@ -import { BlockElement, InlineElement } from 'roosterjs-editor-types'; +import type { BlockElement, InlineElement } from 'roosterjs-editor-types'; /** * @internal diff --git a/packages/roosterjs-editor-dom/lib/edit/adjustInsertPosition.ts b/packages/roosterjs-editor-dom/lib/edit/adjustInsertPosition.ts index 64105203780..e2441aa1ea1 100644 --- a/packages/roosterjs-editor-dom/lib/edit/adjustInsertPosition.ts +++ b/packages/roosterjs-editor-dom/lib/edit/adjustInsertPosition.ts @@ -18,14 +18,9 @@ import splitTextNode from '../utils/splitTextNode'; import toArray from '../jsUtils/toArray'; import unwrap from '../utils/unwrap'; import wrap from '../utils/wrap'; +import { NodeType, PositionType, QueryScope } from 'roosterjs-editor-types'; import { splitBalancedNodeRange } from '../utils/splitParentNode'; -import { - BlockElement, - NodePosition, - NodeType, - PositionType, - QueryScope, -} from 'roosterjs-editor-types'; +import type { BlockElement, NodePosition } from 'roosterjs-editor-types'; const NOT_EDITABLE_SELECTOR = '[contenteditable=false]'; diff --git a/packages/roosterjs-editor-dom/lib/edit/deleteSelectedContent.ts b/packages/roosterjs-editor-dom/lib/edit/deleteSelectedContent.ts index 40c9b665c97..6d4f67a45af 100644 --- a/packages/roosterjs-editor-dom/lib/edit/deleteSelectedContent.ts +++ b/packages/roosterjs-editor-dom/lib/edit/deleteSelectedContent.ts @@ -7,7 +7,8 @@ import Position from '../selection/Position'; import queryElements from '../utils/queryElements'; import safeInstanceOf from '../utils/safeInstanceOf'; import splitTextNode from '../utils/splitTextNode'; -import { NodePosition, PositionType, QueryScope, RegionType } from 'roosterjs-editor-types'; +import { PositionType, QueryScope, RegionType } from 'roosterjs-editor-types'; +import type { NodePosition } from 'roosterjs-editor-types'; /** * Delete selected content, and return the new position to select diff --git a/packages/roosterjs-editor-dom/lib/entity/entityPlaceholderUtils.ts b/packages/roosterjs-editor-dom/lib/entity/entityPlaceholderUtils.ts index b41b14cea7c..092967f8661 100644 --- a/packages/roosterjs-editor-dom/lib/entity/entityPlaceholderUtils.ts +++ b/packages/roosterjs-editor-dom/lib/entity/entityPlaceholderUtils.ts @@ -1,7 +1,8 @@ import getEntityFromElement from './getEntityFromElement'; import getEntitySelector from './getEntitySelector'; import safeInstanceOf from '../utils/safeInstanceOf'; -import { Entity, EntityClasses, KnownEntityItem } from 'roosterjs-editor-types'; +import { EntityClasses } from 'roosterjs-editor-types'; +import type { Entity, KnownEntityItem } from 'roosterjs-editor-types'; const EntityPlaceHolderTagName = 'ENTITY-PLACEHOLDER'; diff --git a/packages/roosterjs-editor-dom/lib/entity/getEntityFromElement.ts b/packages/roosterjs-editor-dom/lib/entity/getEntityFromElement.ts index b5772cc5ab6..3b7fe9593b8 100644 --- a/packages/roosterjs-editor-dom/lib/entity/getEntityFromElement.ts +++ b/packages/roosterjs-editor-dom/lib/entity/getEntityFromElement.ts @@ -1,4 +1,5 @@ -import { Entity, EntityClasses } from 'roosterjs-editor-types'; +import { EntityClasses } from 'roosterjs-editor-types'; +import type { Entity } from 'roosterjs-editor-types'; /** * Get Entity object from an entity root element diff --git a/packages/roosterjs-editor-dom/lib/event/cacheGetEventData.ts b/packages/roosterjs-editor-dom/lib/event/cacheGetEventData.ts index 2db6df1fc4d..78a403d742a 100644 --- a/packages/roosterjs-editor-dom/lib/event/cacheGetEventData.ts +++ b/packages/roosterjs-editor-dom/lib/event/cacheGetEventData.ts @@ -1,4 +1,4 @@ -import { PluginEvent } from 'roosterjs-editor-types'; +import type { PluginEvent } from 'roosterjs-editor-types'; /** * Gets the cached event data by cache key from event object if there is already one. diff --git a/packages/roosterjs-editor-dom/lib/event/clearEventDataCache.ts b/packages/roosterjs-editor-dom/lib/event/clearEventDataCache.ts index a5a4d9b7846..c5fd6e4ee01 100644 --- a/packages/roosterjs-editor-dom/lib/event/clearEventDataCache.ts +++ b/packages/roosterjs-editor-dom/lib/event/clearEventDataCache.ts @@ -1,4 +1,4 @@ -import { PluginEvent } from 'roosterjs-editor-types'; +import type { PluginEvent } from 'roosterjs-editor-types'; /** * Clear a cached object by its key from an event object diff --git a/packages/roosterjs-editor-dom/lib/htmlSanitizer/HtmlSanitizer.ts b/packages/roosterjs-editor-dom/lib/htmlSanitizer/HtmlSanitizer.ts index d652b2577fb..bca500af19d 100644 --- a/packages/roosterjs-editor-dom/lib/htmlSanitizer/HtmlSanitizer.ts +++ b/packages/roosterjs-editor-dom/lib/htmlSanitizer/HtmlSanitizer.ts @@ -9,6 +9,7 @@ import setStyles from '../style/setStyles'; import toArray from '../jsUtils/toArray'; import { cloneObject } from './cloneObject'; import { isCssVariable, processCssVariable } from './processCssVariable'; +import { NodeType } from 'roosterjs-editor-types'; import { getAllowedAttributes, getAllowedCssClassesRegex, @@ -16,12 +17,11 @@ import { getDefaultStyleValues, getStyleCallbacks, } from './getAllowedValues'; -import { +import type { AttributeCallbackMap, CssStyleCallbackMap, ElementCallbackMap, HtmlSanitizerOptions, - NodeType, PredefinedCssMap, SanitizeHtmlOptions, StringMap, diff --git a/packages/roosterjs-editor-dom/lib/htmlSanitizer/createDefaultHtmlSanitizerOptions.ts b/packages/roosterjs-editor-dom/lib/htmlSanitizer/createDefaultHtmlSanitizerOptions.ts index fc83763f1d7..bc01bf1461d 100644 --- a/packages/roosterjs-editor-dom/lib/htmlSanitizer/createDefaultHtmlSanitizerOptions.ts +++ b/packages/roosterjs-editor-dom/lib/htmlSanitizer/createDefaultHtmlSanitizerOptions.ts @@ -1,4 +1,4 @@ -import { HtmlSanitizerOptions } from 'roosterjs-editor-types'; +import type { HtmlSanitizerOptions } from 'roosterjs-editor-types'; /** * Create default value of HtmlSanitizerOptions with every property set diff --git a/packages/roosterjs-editor-dom/lib/htmlSanitizer/getAllowedValues.ts b/packages/roosterjs-editor-dom/lib/htmlSanitizer/getAllowedValues.ts index 2ef508f1b39..9e945b2690a 100644 --- a/packages/roosterjs-editor-dom/lib/htmlSanitizer/getAllowedValues.ts +++ b/packages/roosterjs-editor-dom/lib/htmlSanitizer/getAllowedValues.ts @@ -1,6 +1,6 @@ import getObjectKeys from '../jsUtils/getObjectKeys'; import { cloneObject } from './cloneObject'; -import { CssStyleCallbackMap, StringMap } from 'roosterjs-editor-types'; +import type { CssStyleCallbackMap, StringMap } from 'roosterjs-editor-types'; const HTML_TAG_REPLACEMENT: Record = { // Allowed tags diff --git a/packages/roosterjs-editor-dom/lib/htmlSanitizer/getInheritableStyles.ts b/packages/roosterjs-editor-dom/lib/htmlSanitizer/getInheritableStyles.ts index aa7e05ab96c..1bb74ef8ef4 100644 --- a/packages/roosterjs-editor-dom/lib/htmlSanitizer/getInheritableStyles.ts +++ b/packages/roosterjs-editor-dom/lib/htmlSanitizer/getInheritableStyles.ts @@ -1,4 +1,4 @@ -import { StringMap } from 'roosterjs-editor-types'; +import type { StringMap } from 'roosterjs-editor-types'; // Inheritable CSS properties // Ref: https://www.w3.org/TR/CSS21/propidx.html diff --git a/packages/roosterjs-editor-dom/lib/htmlSanitizer/getPredefinedCssForElement.ts b/packages/roosterjs-editor-dom/lib/htmlSanitizer/getPredefinedCssForElement.ts index 959c0b34870..8495fc8e9a1 100644 --- a/packages/roosterjs-editor-dom/lib/htmlSanitizer/getPredefinedCssForElement.ts +++ b/packages/roosterjs-editor-dom/lib/htmlSanitizer/getPredefinedCssForElement.ts @@ -1,5 +1,5 @@ import getTagOfNode from '../utils/getTagOfNode'; -import { PredefinedCssMap, StringMap } from 'roosterjs-editor-types'; +import type { PredefinedCssMap, StringMap } from 'roosterjs-editor-types'; const PREDEFINED_CSS_FOR_ELEMENT: PredefinedCssMap = { B: { diff --git a/packages/roosterjs-editor-dom/lib/index.ts b/packages/roosterjs-editor-dom/lib/index.ts index 759f6a0cc01..cdc5c62f17b 100644 --- a/packages/roosterjs-editor-dom/lib/index.ts +++ b/packages/roosterjs-editor-dom/lib/index.ts @@ -71,6 +71,8 @@ export { default as VList } from './list/VList'; export { default as VListItem } from './list/VListItem'; export { default as createVListFromRegion } from './list/createVListFromRegion'; export { default as VListChain } from './list/VListChain'; +export { default as convertDecimalsToAlpha } from './list/convertDecimalsToAlpha'; +export { default as convertDecimalsToRoman } from './list/convertDecimalsToRomans'; export { default as setListItemStyle } from './list/setListItemStyle'; export { getTableFormatInfo } from './table/tableFormatInfo'; export { saveTableCellMetadata } from './table/tableCellInfo'; diff --git a/packages/roosterjs-editor-dom/lib/inlineElements/EmptyInlineElement.ts b/packages/roosterjs-editor-dom/lib/inlineElements/EmptyInlineElement.ts index 60749ade2e3..037b88b2b8b 100644 --- a/packages/roosterjs-editor-dom/lib/inlineElements/EmptyInlineElement.ts +++ b/packages/roosterjs-editor-dom/lib/inlineElements/EmptyInlineElement.ts @@ -1,4 +1,4 @@ -import { BlockElement, InlineElement, NodePosition } from 'roosterjs-editor-types'; +import type { BlockElement, InlineElement, NodePosition } from 'roosterjs-editor-types'; /** * @internal diff --git a/packages/roosterjs-editor-dom/lib/inlineElements/ImageInlineElement.ts b/packages/roosterjs-editor-dom/lib/inlineElements/ImageInlineElement.ts index c0ec521f92c..85fc11b61e0 100644 --- a/packages/roosterjs-editor-dom/lib/inlineElements/ImageInlineElement.ts +++ b/packages/roosterjs-editor-dom/lib/inlineElements/ImageInlineElement.ts @@ -1,5 +1,5 @@ import NodeInlineElement from './NodeInlineElement'; -import { BlockElement } from 'roosterjs-editor-types'; +import type { BlockElement } from 'roosterjs-editor-types'; /** * This is an inline element representing an Html image diff --git a/packages/roosterjs-editor-dom/lib/inlineElements/LinkInlineElement.ts b/packages/roosterjs-editor-dom/lib/inlineElements/LinkInlineElement.ts index fda1f4a0797..ebac1a92f0e 100644 --- a/packages/roosterjs-editor-dom/lib/inlineElements/LinkInlineElement.ts +++ b/packages/roosterjs-editor-dom/lib/inlineElements/LinkInlineElement.ts @@ -1,5 +1,5 @@ import NodeInlineElement from './NodeInlineElement'; -import { BlockElement } from 'roosterjs-editor-types'; +import type { BlockElement } from 'roosterjs-editor-types'; /** * This is inline element presenting an html hyperlink diff --git a/packages/roosterjs-editor-dom/lib/inlineElements/NodeInlineElement.ts b/packages/roosterjs-editor-dom/lib/inlineElements/NodeInlineElement.ts index 7cee5071f63..09367d36758 100644 --- a/packages/roosterjs-editor-dom/lib/inlineElements/NodeInlineElement.ts +++ b/packages/roosterjs-editor-dom/lib/inlineElements/NodeInlineElement.ts @@ -1,13 +1,8 @@ import applyTextStyle from './applyTextStyle'; import isNodeAfter from '../utils/isNodeAfter'; import Position from '../selection/Position'; -import { - BlockElement, - InlineElement, - NodePosition, - NodeType, - PositionType, -} from 'roosterjs-editor-types'; +import { NodeType, PositionType } from 'roosterjs-editor-types'; +import type { BlockElement, InlineElement, NodePosition } from 'roosterjs-editor-types'; /** * This presents an inline element that can be represented by a single html node. diff --git a/packages/roosterjs-editor-dom/lib/inlineElements/PartialInlineElement.ts b/packages/roosterjs-editor-dom/lib/inlineElements/PartialInlineElement.ts index dea695fac4d..63992e68690 100644 --- a/packages/roosterjs-editor-dom/lib/inlineElements/PartialInlineElement.ts +++ b/packages/roosterjs-editor-dom/lib/inlineElements/PartialInlineElement.ts @@ -1,8 +1,9 @@ import applyTextStyle from './applyTextStyle'; import createRange from '../selection/createRange'; import Position from '../selection/Position'; -import { BlockElement, InlineElement, NodePosition, PositionType } from 'roosterjs-editor-types'; import { getNextLeafSibling, getPreviousLeafSibling } from '../utils/getLeafSibling'; +import { PositionType } from 'roosterjs-editor-types'; +import type { BlockElement, InlineElement, NodePosition } from 'roosterjs-editor-types'; /** * This is a special version of inline element that identifies a section of an inline element diff --git a/packages/roosterjs-editor-dom/lib/inlineElements/applyTextStyle.ts b/packages/roosterjs-editor-dom/lib/inlineElements/applyTextStyle.ts index 74e34b0f6e3..a28a937aa4f 100644 --- a/packages/roosterjs-editor-dom/lib/inlineElements/applyTextStyle.ts +++ b/packages/roosterjs-editor-dom/lib/inlineElements/applyTextStyle.ts @@ -1,11 +1,12 @@ import getTagOfNode from '../utils/getTagOfNode'; import Position from '../selection/Position'; +import safeInstanceOf from '../utils/safeInstanceOf'; import splitTextNode from '../utils/splitTextNode'; import wrap from '../utils/wrap'; import { getNextLeafSibling } from '../utils/getLeafSibling'; -import { NodePosition, NodeType, PositionType } from 'roosterjs-editor-types'; +import { NodeType, PositionType } from 'roosterjs-editor-types'; import { splitBalancedNodeRange } from '../utils/splitParentNode'; -import safeInstanceOf from '../utils/safeInstanceOf'; +import type { NodePosition } from 'roosterjs-editor-types'; const STYLET_AGS = 'SPAN,B,I,U,EM,STRONG,STRIKE,S,SMALL,SUP,SUB'.split(','); diff --git a/packages/roosterjs-editor-dom/lib/inlineElements/getFirstLastInlineElement.ts b/packages/roosterjs-editor-dom/lib/inlineElements/getFirstLastInlineElement.ts index 9bc401e50fe..1f5979a027c 100644 --- a/packages/roosterjs-editor-dom/lib/inlineElements/getFirstLastInlineElement.ts +++ b/packages/roosterjs-editor-dom/lib/inlineElements/getFirstLastInlineElement.ts @@ -1,6 +1,6 @@ import getInlineElementAtNode from './getInlineElementAtNode'; import { getFirstLeafNode, getLastLeafNode } from '../utils/getLeafNode'; -import { InlineElement } from 'roosterjs-editor-types'; +import type { InlineElement } from 'roosterjs-editor-types'; /** * @internal diff --git a/packages/roosterjs-editor-dom/lib/inlineElements/getInlineElementAtNode.ts b/packages/roosterjs-editor-dom/lib/inlineElements/getInlineElementAtNode.ts index 5c33a116730..7fecaf7f111 100644 --- a/packages/roosterjs-editor-dom/lib/inlineElements/getInlineElementAtNode.ts +++ b/packages/roosterjs-editor-dom/lib/inlineElements/getInlineElementAtNode.ts @@ -4,7 +4,7 @@ import ImageInlineElement from './ImageInlineElement'; import LinkInlineElement from './LinkInlineElement'; import NodeInlineElement from './NodeInlineElement'; import safeInstanceOf from '../utils/safeInstanceOf'; -import { BlockElement, InlineElement } from 'roosterjs-editor-types'; +import type { BlockElement, InlineElement } from 'roosterjs-editor-types'; /** * Get the inline element at a node diff --git a/packages/roosterjs-editor-dom/lib/inlineElements/getInlineElementBeforeAfter.ts b/packages/roosterjs-editor-dom/lib/inlineElements/getInlineElementBeforeAfter.ts index a4c58ad1e43..f5a05166285 100644 --- a/packages/roosterjs-editor-dom/lib/inlineElements/getInlineElementBeforeAfter.ts +++ b/packages/roosterjs-editor-dom/lib/inlineElements/getInlineElementBeforeAfter.ts @@ -2,7 +2,8 @@ import getInlineElementAtNode from './getInlineElementAtNode'; import PartialInlineElement from './PartialInlineElement'; import shouldSkipNode from '../utils/shouldSkipNode'; import { getLeafSibling } from '../utils/getLeafSibling'; -import { InlineElement, NodePosition, NodeType } from 'roosterjs-editor-types'; +import { NodeType } from 'roosterjs-editor-types'; +import type { InlineElement, NodePosition } from 'roosterjs-editor-types'; /** * @internal diff --git a/packages/roosterjs-editor-dom/lib/list/VList.ts b/packages/roosterjs-editor-dom/lib/list/VList.ts index af1661064bf..ab2f3b94ab9 100644 --- a/packages/roosterjs-editor-dom/lib/list/VList.ts +++ b/packages/roosterjs-editor-dom/lib/list/VList.ts @@ -9,13 +9,14 @@ import safeInstanceOf from '../utils/safeInstanceOf'; import splitParentNode from '../utils/splitParentNode'; import toArray from '../jsUtils/toArray'; import unwrap from '../utils/unwrap'; -import VListItem, { ListStyleDefinitionMetadata, ListStyleMetadata } from './VListItem'; +import VListItem, { ListStyleDefinitionMetadata } from './VListItem'; import wrap from '../utils/wrap'; import { getMetadata, setMetadata } from '../metadata/metadata'; +import type { ListStyleMetadata } from './VListItem'; +import type { NodePosition } from 'roosterjs-editor-types'; import { Indentation, ListType, - NodePosition, PositionType, NodeType, Alignment, diff --git a/packages/roosterjs-editor-dom/lib/list/VListChain.ts b/packages/roosterjs-editor-dom/lib/list/VListChain.ts index 58285c2f09a..7737b9a62c9 100644 --- a/packages/roosterjs-editor-dom/lib/list/VListChain.ts +++ b/packages/roosterjs-editor-dom/lib/list/VListChain.ts @@ -4,7 +4,8 @@ import isNodeAfter from '../utils/isNodeAfter'; import isNodeInRegion from '../region/isNodeInRegion'; import queryElements from '../utils/queryElements'; import VList from './VList'; -import { ListType, RegionBase } from 'roosterjs-editor-types'; +import { ListType } from 'roosterjs-editor-types'; +import type { RegionBase } from 'roosterjs-editor-types'; const CHAIN_NAME_PREFIX = '__List_Chain_'; const CHAIN_DATASET_NAME = 'listchain'; diff --git a/packages/roosterjs-editor-dom/lib/list/convertDecimalsToAlpha.ts b/packages/roosterjs-editor-dom/lib/list/convertDecimalsToAlpha.ts index 567ad55d15c..b23c5c4c2b3 100644 --- a/packages/roosterjs-editor-dom/lib/list/convertDecimalsToAlpha.ts +++ b/packages/roosterjs-editor-dom/lib/list/convertDecimalsToAlpha.ts @@ -28,7 +28,6 @@ const ALPHABET: Record = { }; /** - * @internal * Convert decimal numbers into english alphabet letters * @param decimal The decimal number that needs to be converted * @param isLowerCase if true the roman value will appear in lower case diff --git a/packages/roosterjs-editor-dom/lib/list/convertDecimalsToRomans.ts b/packages/roosterjs-editor-dom/lib/list/convertDecimalsToRomans.ts index c2d4ff8f08d..093ddab509a 100644 --- a/packages/roosterjs-editor-dom/lib/list/convertDecimalsToRomans.ts +++ b/packages/roosterjs-editor-dom/lib/list/convertDecimalsToRomans.ts @@ -17,7 +17,6 @@ const RomanValues: Record = { }; /** - * @internal * Convert decimal numbers into roman numbers * @param decimal The decimal number that needs to be converted * @param isLowerCase if true the roman value will appear in lower case diff --git a/packages/roosterjs-editor-dom/lib/list/createVListFromRegion.ts b/packages/roosterjs-editor-dom/lib/list/createVListFromRegion.ts index c8b61c320e6..ddc83fb1465 100644 --- a/packages/roosterjs-editor-dom/lib/list/createVListFromRegion.ts +++ b/packages/roosterjs-editor-dom/lib/list/createVListFromRegion.ts @@ -10,12 +10,8 @@ import VList from './VList'; import wrap from '../utils/wrap'; import { getLeafSibling } from '../utils/getLeafSibling'; import { isListElement } from './getListTypeFromNode'; -import { - KnownCreateElementDataIndex, - ListType, - Region, - PositionType, -} from 'roosterjs-editor-types'; +import { KnownCreateElementDataIndex, ListType, PositionType } from 'roosterjs-editor-types'; +import type { Region } from 'roosterjs-editor-types'; const ListSelector = 'ol,ul'; diff --git a/packages/roosterjs-editor-dom/lib/list/getRootListNode.ts b/packages/roosterjs-editor-dom/lib/list/getRootListNode.ts index 374e885ce7a..cc4d7c0fc08 100644 --- a/packages/roosterjs-editor-dom/lib/list/getRootListNode.ts +++ b/packages/roosterjs-editor-dom/lib/list/getRootListNode.ts @@ -1,5 +1,5 @@ import findClosestElementAncestor from '../utils/findClosestElementAncestor'; -import { RegionBase } from 'roosterjs-editor-types'; +import type { RegionBase } from 'roosterjs-editor-types'; /** * @internal diff --git a/packages/roosterjs-editor-dom/lib/list/setListItemStyle.ts b/packages/roosterjs-editor-dom/lib/list/setListItemStyle.ts index 699ea1d6c45..98127c5b3e7 100644 --- a/packages/roosterjs-editor-dom/lib/list/setListItemStyle.ts +++ b/packages/roosterjs-editor-dom/lib/list/setListItemStyle.ts @@ -1,7 +1,7 @@ import ContentTraverser from '../contentTraverser/ContentTraverser'; import findClosestElementAncestor from '../utils/findClosestElementAncestor'; import safeInstanceOf from '../utils/safeInstanceOf'; -import { InlineElement } from 'roosterjs-editor-types'; +import type { InlineElement } from 'roosterjs-editor-types'; /** * Set the Style of a List Item provided, with the styles that the inline child elements have diff --git a/packages/roosterjs-editor-dom/lib/metadata/definitionCreators.ts b/packages/roosterjs-editor-dom/lib/metadata/definitionCreators.ts index 670ddc8586f..9545e104405 100644 --- a/packages/roosterjs-editor-dom/lib/metadata/definitionCreators.ts +++ b/packages/roosterjs-editor-dom/lib/metadata/definitionCreators.ts @@ -1,6 +1,6 @@ -import { +import { DefinitionType } from 'roosterjs-editor-types'; +import type { Definition, - DefinitionType, NumberDefinition, ArrayDefinition, BooleanDefinition, diff --git a/packages/roosterjs-editor-dom/lib/metadata/metadata.ts b/packages/roosterjs-editor-dom/lib/metadata/metadata.ts index b038f81f444..50b3d91d821 100644 --- a/packages/roosterjs-editor-dom/lib/metadata/metadata.ts +++ b/packages/roosterjs-editor-dom/lib/metadata/metadata.ts @@ -1,5 +1,5 @@ import validate from './validate'; -import { Definition } from 'roosterjs-editor-types'; +import type { Definition } from 'roosterjs-editor-types'; const MetadataDataSetName = 'editingInfo'; diff --git a/packages/roosterjs-editor-dom/lib/metadata/validate.ts b/packages/roosterjs-editor-dom/lib/metadata/validate.ts index cabec16e130..c751dafc4c5 100644 --- a/packages/roosterjs-editor-dom/lib/metadata/validate.ts +++ b/packages/roosterjs-editor-dom/lib/metadata/validate.ts @@ -1,5 +1,6 @@ import getObjectKeys from '../jsUtils/getObjectKeys'; -import { Definition, DefinitionType } from 'roosterjs-editor-types'; +import { DefinitionType } from 'roosterjs-editor-types'; +import type { Definition } from 'roosterjs-editor-types'; /** * Validate the given object with a type definition object diff --git a/packages/roosterjs-editor-dom/lib/pasteSourceValidations/getPasteSource.ts b/packages/roosterjs-editor-dom/lib/pasteSourceValidations/getPasteSource.ts index 3fd0a18ee05..6a33e887eb4 100644 --- a/packages/roosterjs-editor-dom/lib/pasteSourceValidations/getPasteSource.ts +++ b/packages/roosterjs-editor-dom/lib/pasteSourceValidations/getPasteSource.ts @@ -5,7 +5,8 @@ import isGoogleSheetDocument from './isGoogleSheetDocument'; import isPowerPointDesktopDocument from './isPowerPointDesktopDocument'; import isWordDesktopDocument from './isWordDesktopDocument'; import shouldConvertToSingleImage from './shouldConvertToSingleImage'; -import { BeforePasteEvent, ClipboardData, KnownPasteSourceType } from 'roosterjs-editor-types'; +import { KnownPasteSourceType } from 'roosterjs-editor-types'; +import type { BeforePasteEvent, ClipboardData } from 'roosterjs-editor-types'; /** * @internal diff --git a/packages/roosterjs-editor-dom/lib/region/collapseNodesInRegion.ts b/packages/roosterjs-editor-dom/lib/region/collapseNodesInRegion.ts index 2978121bba2..e987266583c 100644 --- a/packages/roosterjs-editor-dom/lib/region/collapseNodesInRegion.ts +++ b/packages/roosterjs-editor-dom/lib/region/collapseNodesInRegion.ts @@ -1,7 +1,7 @@ import collapseNode from '../utils/collapseNodes'; import isNodeInRegion from './isNodeInRegion'; import safeInstanceOf from '../utils/safeInstanceOf'; -import { BlockElement, RegionBase } from 'roosterjs-editor-types'; +import type { BlockElement, RegionBase } from 'roosterjs-editor-types'; /** * Collapse nodes within this region to their common ancestor node under this region diff --git a/packages/roosterjs-editor-dom/lib/region/getRegionsFromRange.ts b/packages/roosterjs-editor-dom/lib/region/getRegionsFromRange.ts index 51316a7614a..6243ad9d3e8 100644 --- a/packages/roosterjs-editor-dom/lib/region/getRegionsFromRange.ts +++ b/packages/roosterjs-editor-dom/lib/region/getRegionsFromRange.ts @@ -3,7 +3,8 @@ import findClosestElementAncestor from '../utils/findClosestElementAncestor'; import Position from '../selection/Position'; import queryElements from '../utils/queryElements'; import { getNextLeafSibling, getPreviousLeafSibling } from '../utils/getLeafSibling'; -import { QueryScope, Region, RegionType } from 'roosterjs-editor-types'; +import { QueryScope, RegionType } from 'roosterjs-editor-types'; +import type { Region } from 'roosterjs-editor-types'; import type { CompatibleRegionType } from 'roosterjs-editor-types/lib/compatibleTypes'; interface RegionTypeData { diff --git a/packages/roosterjs-editor-dom/lib/region/getSelectedBlockElementsInRegion.ts b/packages/roosterjs-editor-dom/lib/region/getSelectedBlockElementsInRegion.ts index 3ddd778e1fa..b957b831d42 100644 --- a/packages/roosterjs-editor-dom/lib/region/getSelectedBlockElementsInRegion.ts +++ b/packages/roosterjs-editor-dom/lib/region/getSelectedBlockElementsInRegion.ts @@ -3,7 +3,8 @@ import createElement from '../utils/createElement'; import getBlockElementAtNode from '../blockElements/getBlockElementAtNode'; import getSelectionRangeInRegion from './getSelectionRangeInRegion'; import shouldSkipNode from '../utils/shouldSkipNode'; -import { BlockElement, KnownCreateElementDataIndex, RegionBase } from 'roosterjs-editor-types'; +import { KnownCreateElementDataIndex } from 'roosterjs-editor-types'; +import type { BlockElement, RegionBase } from 'roosterjs-editor-types'; /** * Get all block elements covered by the selection under this region diff --git a/packages/roosterjs-editor-dom/lib/region/getSelectionRangeInRegion.ts b/packages/roosterjs-editor-dom/lib/region/getSelectionRangeInRegion.ts index f7e4db47a40..9a169e669cd 100644 --- a/packages/roosterjs-editor-dom/lib/region/getSelectionRangeInRegion.ts +++ b/packages/roosterjs-editor-dom/lib/region/getSelectionRangeInRegion.ts @@ -1,7 +1,7 @@ import createRange from '../selection/createRange'; import Position from '../selection/Position'; import { getNextLeafSibling, getPreviousLeafSibling } from '../utils/getLeafSibling'; -import { Region, RegionBase } from 'roosterjs-editor-types'; +import type { Region, RegionBase } from 'roosterjs-editor-types'; /** * Get the selection range in the given region. diff --git a/packages/roosterjs-editor-dom/lib/region/isNodeInRegion.ts b/packages/roosterjs-editor-dom/lib/region/isNodeInRegion.ts index 0e76718915b..08a544f7e8b 100644 --- a/packages/roosterjs-editor-dom/lib/region/isNodeInRegion.ts +++ b/packages/roosterjs-editor-dom/lib/region/isNodeInRegion.ts @@ -1,5 +1,6 @@ import contains from '../utils/contains'; -import { DocumentPosition, RegionBase } from 'roosterjs-editor-types'; +import { DocumentPosition } from 'roosterjs-editor-types'; +import type { RegionBase } from 'roosterjs-editor-types'; /** * Check if a given node is contained by the given region diff --git a/packages/roosterjs-editor-dom/lib/region/mergeBlocksInRegion.ts b/packages/roosterjs-editor-dom/lib/region/mergeBlocksInRegion.ts index fcfca254ce0..c77d30cdeab 100644 --- a/packages/roosterjs-editor-dom/lib/region/mergeBlocksInRegion.ts +++ b/packages/roosterjs-editor-dom/lib/region/mergeBlocksInRegion.ts @@ -6,8 +6,8 @@ import getStyles from '../style/getStyles'; import isNodeInRegion from './isNodeInRegion'; import safeInstanceOf from '../utils/safeInstanceOf'; import setStyles from '../style/setStyles'; -import { BlockElement, RegionBase } from 'roosterjs-editor-types'; import { collapse } from '../utils/collapseNodes'; +import type { BlockElement, RegionBase } from 'roosterjs-editor-types'; /** * Merge a BlockElement of given node after another node diff --git a/packages/roosterjs-editor-dom/lib/selection/Position.ts b/packages/roosterjs-editor-dom/lib/selection/Position.ts index 3a436fb2091..06e3694f2ce 100644 --- a/packages/roosterjs-editor-dom/lib/selection/Position.ts +++ b/packages/roosterjs-editor-dom/lib/selection/Position.ts @@ -1,6 +1,7 @@ import findClosestElementAncestor from '../utils/findClosestElementAncestor'; import isNodeAfter from '../utils/isNodeAfter'; -import { NodePosition, NodeType, PositionType } from 'roosterjs-editor-types'; +import { NodeType, PositionType } from 'roosterjs-editor-types'; +import type { NodePosition } from 'roosterjs-editor-types'; import type { CompatiblePositionType } from 'roosterjs-editor-types/lib/compatibleTypes'; /** diff --git a/packages/roosterjs-editor-dom/lib/selection/createRange.ts b/packages/roosterjs-editor-dom/lib/selection/createRange.ts index cebd889afd7..165f709bd05 100644 --- a/packages/roosterjs-editor-dom/lib/selection/createRange.ts +++ b/packages/roosterjs-editor-dom/lib/selection/createRange.ts @@ -1,7 +1,8 @@ import isVoidHtmlElement from '../utils/isVoidHtmlElement'; import Position from './Position'; import safeInstanceOf from '../utils/safeInstanceOf'; -import { NodePosition, NodeType, PositionType } from 'roosterjs-editor-types'; +import { NodeType, PositionType } from 'roosterjs-editor-types'; +import type { NodePosition } from 'roosterjs-editor-types'; /** * Create a range around the given node(s) diff --git a/packages/roosterjs-editor-dom/lib/selection/getPositionRect.ts b/packages/roosterjs-editor-dom/lib/selection/getPositionRect.ts index 7f63ed0aba7..1e6b37dfa89 100644 --- a/packages/roosterjs-editor-dom/lib/selection/getPositionRect.ts +++ b/packages/roosterjs-editor-dom/lib/selection/getPositionRect.ts @@ -1,7 +1,8 @@ import createElement from '../utils/createElement'; import createRange from './createRange'; import normalizeRect from '../utils/normalizeRect'; -import { NodePosition, NodeType, Rect } from 'roosterjs-editor-types'; +import { NodeType } from 'roosterjs-editor-types'; +import type { NodePosition, Rect } from 'roosterjs-editor-types'; /** * Get bounding rect of this position diff --git a/packages/roosterjs-editor-dom/lib/selection/getSelectionPath.ts b/packages/roosterjs-editor-dom/lib/selection/getSelectionPath.ts index a670eaf1428..c5685d6e1fc 100644 --- a/packages/roosterjs-editor-dom/lib/selection/getSelectionPath.ts +++ b/packages/roosterjs-editor-dom/lib/selection/getSelectionPath.ts @@ -1,6 +1,7 @@ import contains from '../utils/contains'; import Position from './Position'; -import { NodePosition, NodeType, SelectionPath } from 'roosterjs-editor-types'; +import { NodeType } from 'roosterjs-editor-types'; +import type { NodePosition, SelectionPath } from 'roosterjs-editor-types'; /** * Get path of the given selection range related to the given rootNode diff --git a/packages/roosterjs-editor-dom/lib/selection/isPositionAtBeginningOf.ts b/packages/roosterjs-editor-dom/lib/selection/isPositionAtBeginningOf.ts index c9779984e07..1ecc82b77ac 100644 --- a/packages/roosterjs-editor-dom/lib/selection/isPositionAtBeginningOf.ts +++ b/packages/roosterjs-editor-dom/lib/selection/isPositionAtBeginningOf.ts @@ -1,7 +1,7 @@ import contains from '../utils/contains'; import getTagOfNode from '../utils/getTagOfNode'; import isNodeEmpty from '../utils/isNodeEmpty'; -import { NodePosition } from 'roosterjs-editor-types'; +import type { NodePosition } from 'roosterjs-editor-types'; /** * Check if this position is at beginning of the given node. diff --git a/packages/roosterjs-editor-dom/lib/selection/setHtmlWithSelectionPath.ts b/packages/roosterjs-editor-dom/lib/selection/setHtmlWithSelectionPath.ts index 3d37ceedabc..bfa31d07338 100644 --- a/packages/roosterjs-editor-dom/lib/selection/setHtmlWithSelectionPath.ts +++ b/packages/roosterjs-editor-dom/lib/selection/setHtmlWithSelectionPath.ts @@ -1,6 +1,7 @@ import createRange from './createRange'; import safeInstanceOf from '../utils/safeInstanceOf'; import validate from '../metadata/validate'; +import { SelectionRangeTypes } from 'roosterjs-editor-types'; import { createArrayDefinition, createBooleanDefinition, @@ -8,9 +9,8 @@ import { createObjectDefinition, createStringDefinition, } from '../metadata/definitionCreators'; -import { +import type { ContentMetadata, - SelectionRangeTypes, TrustedHTMLHandler, ImageContentMetadata, NormalContentMetadata, diff --git a/packages/roosterjs-editor-dom/lib/snapshots/addSnapshot.ts b/packages/roosterjs-editor-dom/lib/snapshots/addSnapshot.ts index 98e1b2a3040..e91abb0ad1b 100644 --- a/packages/roosterjs-editor-dom/lib/snapshots/addSnapshot.ts +++ b/packages/roosterjs-editor-dom/lib/snapshots/addSnapshot.ts @@ -1,5 +1,5 @@ import clearProceedingSnapshots from './clearProceedingSnapshots'; -import { Snapshot, Snapshots } from 'roosterjs-editor-types'; +import type { Snapshot, Snapshots } from 'roosterjs-editor-types'; /** * Add a new snapshot to the given snapshots data structure diff --git a/packages/roosterjs-editor-dom/lib/snapshots/canMoveCurrentSnapshot.ts b/packages/roosterjs-editor-dom/lib/snapshots/canMoveCurrentSnapshot.ts index 66ad785f4a8..b21d69f3fb3 100644 --- a/packages/roosterjs-editor-dom/lib/snapshots/canMoveCurrentSnapshot.ts +++ b/packages/roosterjs-editor-dom/lib/snapshots/canMoveCurrentSnapshot.ts @@ -1,4 +1,4 @@ -import { Snapshots } from 'roosterjs-editor-types'; +import type { Snapshots } from 'roosterjs-editor-types'; /** * Check whether can move current snapshot with the given step diff --git a/packages/roosterjs-editor-dom/lib/snapshots/canUndoAutoComplete.ts b/packages/roosterjs-editor-dom/lib/snapshots/canUndoAutoComplete.ts index 30857c90d4d..9ce1fc84c6c 100644 --- a/packages/roosterjs-editor-dom/lib/snapshots/canUndoAutoComplete.ts +++ b/packages/roosterjs-editor-dom/lib/snapshots/canUndoAutoComplete.ts @@ -1,4 +1,4 @@ -import { Snapshots } from 'roosterjs-editor-types'; +import type { Snapshots } from 'roosterjs-editor-types'; /** * Whether there is a snapshot added before auto complete and it can be undone now diff --git a/packages/roosterjs-editor-dom/lib/snapshots/clearProceedingSnapshots.ts b/packages/roosterjs-editor-dom/lib/snapshots/clearProceedingSnapshots.ts index c54d790694d..52ffb1ff28c 100644 --- a/packages/roosterjs-editor-dom/lib/snapshots/clearProceedingSnapshots.ts +++ b/packages/roosterjs-editor-dom/lib/snapshots/clearProceedingSnapshots.ts @@ -1,5 +1,5 @@ import canMoveCurrentSnapshot from './canMoveCurrentSnapshot'; -import { Snapshot, Snapshots } from 'roosterjs-editor-types'; +import type { Snapshot, Snapshots } from 'roosterjs-editor-types'; /** * Clear all snapshots after the current one diff --git a/packages/roosterjs-editor-dom/lib/snapshots/createSnapshots.ts b/packages/roosterjs-editor-dom/lib/snapshots/createSnapshots.ts index 54bde30d5c0..9c33351fc14 100644 --- a/packages/roosterjs-editor-dom/lib/snapshots/createSnapshots.ts +++ b/packages/roosterjs-editor-dom/lib/snapshots/createSnapshots.ts @@ -1,4 +1,4 @@ -import { Snapshots } from 'roosterjs-editor-types'; +import type { Snapshots } from 'roosterjs-editor-types'; /** * Create initial snapshots diff --git a/packages/roosterjs-editor-dom/lib/snapshots/moveCurrentSnapshot.ts b/packages/roosterjs-editor-dom/lib/snapshots/moveCurrentSnapshot.ts index d5c5c57e537..38974a0b25c 100644 --- a/packages/roosterjs-editor-dom/lib/snapshots/moveCurrentSnapshot.ts +++ b/packages/roosterjs-editor-dom/lib/snapshots/moveCurrentSnapshot.ts @@ -1,5 +1,5 @@ import canMoveCurrentSnapshot from './canMoveCurrentSnapshot'; -import { Snapshots } from 'roosterjs-editor-types'; +import type { Snapshots } from 'roosterjs-editor-types'; /** * Move current snapshot with the given step if can move this step. Otherwise no action and return null diff --git a/packages/roosterjs-editor-dom/lib/table/VTable.ts b/packages/roosterjs-editor-dom/lib/table/VTable.ts index 5fe335aedce..269ac75692a 100644 --- a/packages/roosterjs-editor-dom/lib/table/VTable.ts +++ b/packages/roosterjs-editor-dom/lib/table/VTable.ts @@ -7,11 +7,10 @@ import toArray from '../jsUtils/toArray'; import { getTableCellMetadata, saveTableCellMetadata } from './tableCellInfo'; import { getTableFormatInfo, saveTableInfo } from './tableFormatInfo'; import { removeMetadata } from '../metadata/metadata'; -import { +import { TableBorderFormat, TableOperation } from 'roosterjs-editor-types'; +import type { SizeTransformer, - TableBorderFormat, TableFormat, - TableOperation, TableSelection, VCell, DarkColorHandler, diff --git a/packages/roosterjs-editor-dom/lib/table/applyTableFormat.ts b/packages/roosterjs-editor-dom/lib/table/applyTableFormat.ts index 83cf73b5159..f3a99a13d7e 100644 --- a/packages/roosterjs-editor-dom/lib/table/applyTableFormat.ts +++ b/packages/roosterjs-editor-dom/lib/table/applyTableFormat.ts @@ -1,7 +1,8 @@ import changeElementTag from '../utils/changeElementTag'; import setColor from '../utils/setColor'; -import { DarkColorHandler, TableBorderFormat, TableFormat, VCell } from 'roosterjs-editor-types'; import { getTableCellMetadata } from './tableCellInfo'; +import { TableBorderFormat } from 'roosterjs-editor-types'; +import type { DarkColorHandler, TableFormat, VCell } from 'roosterjs-editor-types'; const TRANSPARENT = 'transparent'; const TABLE_CELL_TAG_NAME = 'TD'; const TABLE_HEADER_TAG_NAME = 'TH'; diff --git a/packages/roosterjs-editor-dom/lib/table/isWholeTableSelected.ts b/packages/roosterjs-editor-dom/lib/table/isWholeTableSelected.ts index 3c63cbc9c9b..8ba920a073f 100644 --- a/packages/roosterjs-editor-dom/lib/table/isWholeTableSelected.ts +++ b/packages/roosterjs-editor-dom/lib/table/isWholeTableSelected.ts @@ -1,5 +1,5 @@ -import VTable from './VTable'; -import { TableSelection } from 'roosterjs-editor-types'; +import type VTable from './VTable'; +import type { TableSelection } from 'roosterjs-editor-types'; /** * Check if the whole table is selected diff --git a/packages/roosterjs-editor-dom/lib/table/pasteTable.ts b/packages/roosterjs-editor-dom/lib/table/pasteTable.ts index 684ffe05da0..302db248ae9 100644 --- a/packages/roosterjs-editor-dom/lib/table/pasteTable.ts +++ b/packages/roosterjs-editor-dom/lib/table/pasteTable.ts @@ -1,7 +1,8 @@ import cloneCellStyles from './cloneCellStyles'; import moveChildNodes from '../utils/moveChildNodes'; import VTable from './VTable'; -import { NodePosition, TableOperation } from 'roosterjs-editor-types'; +import { TableOperation } from 'roosterjs-editor-types'; +import type { NodePosition } from 'roosterjs-editor-types'; /** * diff --git a/packages/roosterjs-editor-dom/lib/table/tableCellInfo.ts b/packages/roosterjs-editor-dom/lib/table/tableCellInfo.ts index ae022fae1a4..04ab27de530 100644 --- a/packages/roosterjs-editor-dom/lib/table/tableCellInfo.ts +++ b/packages/roosterjs-editor-dom/lib/table/tableCellInfo.ts @@ -1,6 +1,6 @@ import { createBooleanDefinition, createObjectDefinition } from '../metadata/definitionCreators'; import { getMetadata, setMetadata } from '../metadata/metadata'; -import { TableCellMetadataFormat } from 'roosterjs-editor-types'; +import type { TableCellMetadataFormat } from 'roosterjs-editor-types'; const BooleanDefinition = createBooleanDefinition( true /** isOptional */, diff --git a/packages/roosterjs-editor-dom/lib/table/tableFormatInfo.ts b/packages/roosterjs-editor-dom/lib/table/tableFormatInfo.ts index 4e145f9b4bf..0d308616082 100644 --- a/packages/roosterjs-editor-dom/lib/table/tableFormatInfo.ts +++ b/packages/roosterjs-editor-dom/lib/table/tableFormatInfo.ts @@ -1,5 +1,6 @@ import { getMetadata, setMetadata } from '../metadata/metadata'; -import { TableBorderFormat, TableFormat } from 'roosterjs-editor-types'; +import { TableBorderFormat } from 'roosterjs-editor-types'; +import type { TableFormat } from 'roosterjs-editor-types'; import { createBooleanDefinition, createNumberDefinition, diff --git a/packages/roosterjs-editor-dom/lib/utils/Browser.ts b/packages/roosterjs-editor-dom/lib/utils/Browser.ts index fa779db80d2..66d08457e3b 100644 --- a/packages/roosterjs-editor-dom/lib/utils/Browser.ts +++ b/packages/roosterjs-editor-dom/lib/utils/Browser.ts @@ -1,4 +1,4 @@ -import { BrowserInfo } from 'roosterjs-editor-types'; +import type { BrowserInfo } from 'roosterjs-editor-types'; const isAndroidRegex = /android/i; diff --git a/packages/roosterjs-editor-dom/lib/utils/applyFormat.ts b/packages/roosterjs-editor-dom/lib/utils/applyFormat.ts index a5828419de6..b62b9180c6c 100644 --- a/packages/roosterjs-editor-dom/lib/utils/applyFormat.ts +++ b/packages/roosterjs-editor-dom/lib/utils/applyFormat.ts @@ -1,5 +1,5 @@ import setColor from './setColor'; -import { DarkColorHandler, DefaultFormat } from 'roosterjs-editor-types'; +import type { DarkColorHandler, DefaultFormat } from 'roosterjs-editor-types'; /** * Apply format to an HTML element diff --git a/packages/roosterjs-editor-dom/lib/utils/createElement.ts b/packages/roosterjs-editor-dom/lib/utils/createElement.ts index b0c878c787d..5b4112151f2 100644 --- a/packages/roosterjs-editor-dom/lib/utils/createElement.ts +++ b/packages/roosterjs-editor-dom/lib/utils/createElement.ts @@ -1,7 +1,8 @@ import getObjectKeys from '../jsUtils/getObjectKeys'; import safeInstanceOf from './safeInstanceOf'; import { Browser } from './Browser'; -import { CreateElementData, KnownCreateElementDataIndex } from 'roosterjs-editor-types'; +import { KnownCreateElementDataIndex } from 'roosterjs-editor-types'; +import type { CreateElementData } from 'roosterjs-editor-types'; import type { CompatibleKnownCreateElementDataIndex } from 'roosterjs-editor-types/lib/compatibleTypes'; /** diff --git a/packages/roosterjs-editor-dom/lib/utils/getIntersectedRect.ts b/packages/roosterjs-editor-dom/lib/utils/getIntersectedRect.ts index 7134c974259..6371804723a 100644 --- a/packages/roosterjs-editor-dom/lib/utils/getIntersectedRect.ts +++ b/packages/roosterjs-editor-dom/lib/utils/getIntersectedRect.ts @@ -1,5 +1,5 @@ import normalizeRect from './normalizeRect'; -import { Rect } from 'roosterjs-editor-types'; +import type { Rect } from 'roosterjs-editor-types'; /** * Get the intersected Rect of elements provided diff --git a/packages/roosterjs-editor-dom/lib/utils/getPendableFormatState.ts b/packages/roosterjs-editor-dom/lib/utils/getPendableFormatState.ts index 526a72d5500..ef0e321c55a 100644 --- a/packages/roosterjs-editor-dom/lib/utils/getPendableFormatState.ts +++ b/packages/roosterjs-editor-dom/lib/utils/getPendableFormatState.ts @@ -1,5 +1,6 @@ import getObjectKeys from '../jsUtils/getObjectKeys'; -import { DocumentCommand, PendableFormatState } from 'roosterjs-editor-types'; +import { DocumentCommand } from 'roosterjs-editor-types'; +import type { PendableFormatState } from 'roosterjs-editor-types'; /** * Names of Pendable formats diff --git a/packages/roosterjs-editor-dom/lib/utils/matchLink.ts b/packages/roosterjs-editor-dom/lib/utils/matchLink.ts index 68481df4810..79121d479e0 100644 --- a/packages/roosterjs-editor-dom/lib/utils/matchLink.ts +++ b/packages/roosterjs-editor-dom/lib/utils/matchLink.ts @@ -1,5 +1,5 @@ import getObjectKeys from '../jsUtils/getObjectKeys'; -import { LinkData } from 'roosterjs-editor-types'; +import type { LinkData } from 'roosterjs-editor-types'; interface LinkMatchRule { match: RegExp; diff --git a/packages/roosterjs-editor-dom/lib/utils/normalizeRect.ts b/packages/roosterjs-editor-dom/lib/utils/normalizeRect.ts index 1635db5646c..38763027997 100644 --- a/packages/roosterjs-editor-dom/lib/utils/normalizeRect.ts +++ b/packages/roosterjs-editor-dom/lib/utils/normalizeRect.ts @@ -1,4 +1,4 @@ -import { Rect } from 'roosterjs-editor-types'; +import type { Rect } from 'roosterjs-editor-types'; /** * A ClientRect of all 0 is possible. i.e. chrome returns a ClientRect of 0 when the cursor is on an empty p diff --git a/packages/roosterjs-editor-dom/lib/utils/safeInstanceOf.ts b/packages/roosterjs-editor-dom/lib/utils/safeInstanceOf.ts index 111baa9d7a0..4c2eb31b734 100644 --- a/packages/roosterjs-editor-dom/lib/utils/safeInstanceOf.ts +++ b/packages/roosterjs-editor-dom/lib/utils/safeInstanceOf.ts @@ -1,4 +1,4 @@ -import { TargetWindow } from 'roosterjs-editor-types'; +import type { TargetWindow } from 'roosterjs-editor-types'; // NOTE: Type TargetWindow is an auto-generated type. // Run node ./tools/generateTargetWindow.js to generate it. diff --git a/packages/roosterjs-editor-dom/lib/utils/setColor.ts b/packages/roosterjs-editor-dom/lib/utils/setColor.ts index 93301c87305..eb60b49a83a 100644 --- a/packages/roosterjs-editor-dom/lib/utils/setColor.ts +++ b/packages/roosterjs-editor-dom/lib/utils/setColor.ts @@ -1,5 +1,5 @@ import parseColor from './parseColor'; -import { DarkColorHandler, ModeIndependentColor } from 'roosterjs-editor-types'; +import type { DarkColorHandler, ModeIndependentColor } from 'roosterjs-editor-types'; const WHITE = '#ffffff'; const GRAY = '#333333'; diff --git a/packages/roosterjs-editor-dom/lib/utils/wrap.ts b/packages/roosterjs-editor-dom/lib/utils/wrap.ts index c57efc48c68..cc1e7707d5f 100644 --- a/packages/roosterjs-editor-dom/lib/utils/wrap.ts +++ b/packages/roosterjs-editor-dom/lib/utils/wrap.ts @@ -1,7 +1,7 @@ import createElement from './createElement'; import fromHtml from './fromHtml'; import safeInstanceOf from './safeInstanceOf'; -import { CreateElementData, KnownCreateElementDataIndex } from 'roosterjs-editor-types'; +import type { CreateElementData, KnownCreateElementDataIndex } from 'roosterjs-editor-types'; import type { CompatibleKnownCreateElementDataIndex } from 'roosterjs-editor-types/lib/compatibleTypes'; /** diff --git a/packages/roosterjs-editor-plugins/lib/Announce.ts b/packages/roosterjs-editor-plugins/lib/Announce.ts new file mode 100644 index 00000000000..5561551056e --- /dev/null +++ b/packages/roosterjs-editor-plugins/lib/Announce.ts @@ -0,0 +1 @@ +export * from './plugins/Announce/index'; diff --git a/packages/roosterjs-editor-plugins/lib/index.ts b/packages/roosterjs-editor-plugins/lib/index.ts index 5cd296430b2..d6977ef38e9 100644 --- a/packages/roosterjs-editor-plugins/lib/index.ts +++ b/packages/roosterjs-editor-plugins/lib/index.ts @@ -11,3 +11,4 @@ export * from './TableResize'; export * from './Watermark'; export * from './TableCellSelection'; export * from './AutoFormat'; +export * from './Announce'; diff --git a/packages/roosterjs-editor-plugins/lib/pluginUtils/DragAndDropHelper.ts b/packages/roosterjs-editor-plugins/lib/pluginUtils/DragAndDropHelper.ts index eaf228822f1..dbac52991d5 100644 --- a/packages/roosterjs-editor-plugins/lib/pluginUtils/DragAndDropHelper.ts +++ b/packages/roosterjs-editor-plugins/lib/pluginUtils/DragAndDropHelper.ts @@ -1,6 +1,6 @@ -import Disposable from './Disposable'; -import DragAndDropHandler from './DragAndDropHandler'; import { Browser } from 'roosterjs-editor-dom'; +import type Disposable from './Disposable'; +import type DragAndDropHandler from './DragAndDropHandler'; /** * @internal diff --git a/packages/roosterjs-editor-plugins/lib/pluginUtils/announceData/getAnnounceDataForList.ts b/packages/roosterjs-editor-plugins/lib/pluginUtils/announceData/getAnnounceDataForList.ts new file mode 100644 index 00000000000..c55b15fdd98 --- /dev/null +++ b/packages/roosterjs-editor-plugins/lib/pluginUtils/announceData/getAnnounceDataForList.ts @@ -0,0 +1,50 @@ +import { KnownAnnounceStrings } from 'roosterjs-editor-types'; +import { + convertDecimalsToAlpha, + convertDecimalsToRoman, + safeInstanceOf, + VList, +} from 'roosterjs-editor-dom'; +import type { AnnounceData } from 'roosterjs-editor-types'; + +/** + * @internal + * Get the announce data for the current List + * @returns announce data for list or undefined. + */ +export default function getAnnounceDataForList( + list: HTMLElement | null, + li: HTMLElement | null +): AnnounceData | undefined { + if (!safeInstanceOf(li, 'HTMLLIElement')) { + return undefined; + } + + if (li && safeInstanceOf(list, 'HTMLOListElement')) { + const vList = new VList(list); + const listItemIndex = vList.getListItemIndex(li); + let stringToAnnounce = listItemIndex == -1 ? '' : listItemIndex.toString(); + switch (list.style.listStyleType) { + case 'lower-alpha': + case 'lower-latin': + case 'upper-alpha': + case 'upper-latin': + stringToAnnounce = convertDecimalsToAlpha(listItemIndex - 1); + break; + case 'lower-roman': + case 'upper-roman': + stringToAnnounce = convertDecimalsToRoman(listItemIndex); + break; + } + + return { + defaultStrings: KnownAnnounceStrings.AnnounceListItemNumbering, + formatStrings: [stringToAnnounce], + }; + } else if (safeInstanceOf(list, 'HTMLUListElement')) { + return { + defaultStrings: KnownAnnounceStrings.AnnounceListItemBullet, + }; + } + return undefined; +} diff --git a/packages/roosterjs-editor-plugins/lib/plugins/Announce/AnnounceFeature.ts b/packages/roosterjs-editor-plugins/lib/plugins/Announce/AnnounceFeature.ts new file mode 100644 index 00000000000..8797c93630b --- /dev/null +++ b/packages/roosterjs-editor-plugins/lib/plugins/Announce/AnnounceFeature.ts @@ -0,0 +1,17 @@ +import type { IEditor, AnnounceData } from 'roosterjs-editor-types'; + +/** + * Represents a Announce feature used in Announce Plugin. + * If the Should Handle Callback returns announce data, it will be announced by using a aria-live region. + */ +export interface AnnounceFeature { + /** + * Whether to handle this feature, if returns Announce Data, will be announced, otherwise will do nothing. + * @returns + */ + shouldHandle: (editor: IEditor, lastFocusedElement: HTMLElement | null) => AnnounceData | false; + /** + * Keys handled in the current event + */ + keys: number[]; +} diff --git a/packages/roosterjs-editor-plugins/lib/plugins/Announce/AnnouncePlugin.ts b/packages/roosterjs-editor-plugins/lib/plugins/Announce/AnnouncePlugin.ts new file mode 100644 index 00000000000..723dfb8edfa --- /dev/null +++ b/packages/roosterjs-editor-plugins/lib/plugins/Announce/AnnouncePlugin.ts @@ -0,0 +1,178 @@ +import { AnnounceFeatures } from './features/AnnounceFeatures'; +import { createElement, getObjectKeys } from 'roosterjs-editor-dom'; +import { PluginEventType } from 'roosterjs-editor-types'; +import type { AnnounceFeatureKey } from './features/AnnounceFeatures'; +import type { AnnounceFeature } from './AnnounceFeature'; +import type { CompatibleKnownAnnounceStrings } from 'roosterjs-editor-types/lib/compatibleTypes'; +import type { + EditorPlugin, + IEditor, + PluginEvent, + AnnounceData, + PluginKeyDownEvent, + KnownAnnounceStrings, +} from 'roosterjs-editor-types'; + +const ARIA_LIVE_STYLE = + 'clip: rect(0px, 0px, 0px, 0px); clip-path: inset(100%); height: 1px; overflow: hidden; position: absolute; white-space: nowrap; width: 1px;'; +const ARIA_LIVE_ASSERTIVE = 'assertive'; +const DIV_TAG = 'div'; +const createAriaLiveElement = (document: Document): HTMLDivElement => { + const element = createElement( + { + tag: DIV_TAG, + style: ARIA_LIVE_STYLE, + attributes: { + 'aria-live': ARIA_LIVE_ASSERTIVE, + }, + }, + document + ) as HTMLDivElement; + + document.body.appendChild(element); + + return element; +}; + +/** + * Announce messages to screen reader by using aria live element. + */ +export default class Announce implements EditorPlugin { + private ariaLiveElement: HTMLDivElement | undefined; + private editor: IEditor | undefined; + private features: AnnounceFeature[]; + private lastFocusedElement: HTMLElement | null = null; + + constructor( + private stringsMapOrGetter?: + | Map + | ((key: KnownAnnounceStrings | CompatibleKnownAnnounceStrings) => string) + | undefined, + skipAnnounceFeatures: AnnounceFeatureKey[] = [], + additionalFeatures?: AnnounceFeature[] + ) { + this.features = getObjectKeys(AnnounceFeatures) + .map(key => { + if (skipAnnounceFeatures.indexOf(key) == -1) { + return AnnounceFeatures[key]; + } + + return undefined; + }) + .filter(feature => !!feature) + .concat(additionalFeatures || []) as AnnounceFeature[]; + } + + /** + * Get a friendly name of this plugin + */ + getName() { + return 'Announce'; + } + + /** + * Initialize this plugin + * @param editor The editor instance + */ + initialize(editor: IEditor) { + this.editor = editor; + } + + /** + * Dispose this plugin + */ + dispose() { + this.ariaLiveElement?.parentElement?.removeChild(this.ariaLiveElement); + this.ariaLiveElement = undefined; + this.stringsMapOrGetter = undefined; + this.lastFocusedElement = null; + while (this.features.length > 0) { + this.features.pop(); + } + this.editor = undefined; + } + + /** + * Handle events triggered from editor + * @param event PluginEvent object + */ + onPluginEvent(ev: PluginEvent) { + if ( + this.editor && + ev.eventType == PluginEventType.ContentChanged && + ev.additionalData?.getAnnounceData + ) { + const data = ev.additionalData.getAnnounceData(); + if (data) { + this.announce(data, this.editor); + } + } + + if (ev.eventType == PluginEventType.KeyDown && this.editor) { + this.handleFeatures(ev, this.editor); + } + } + + private handleFeatures(event: PluginKeyDownEvent, editorInput: IEditor) { + editorInput.runAsync(editor => { + this.features + .filter(feature => feature.keys.indexOf(event.rawEvent.which) > -1) + .some(feature => { + const announceData = feature.shouldHandle(editor, this.lastFocusedElement); + if (announceData) { + this.announce(announceData, editor); + } + return !!announceData; + }); + + this.lastFocusedElement = editor.getElementAtCursor(); + }); + } + + protected announce(announceData: AnnounceData, editor: IEditor) { + const { text, defaultStrings, formatStrings = [] } = announceData; + let textToAnnounce = formatString(this.getString(defaultStrings) || text, formatStrings); + if (textToAnnounce) { + if (!this.ariaLiveElement || textToAnnounce == this.ariaLiveElement?.textContent) { + this.ariaLiveElement?.parentElement?.removeChild(this.ariaLiveElement); + this.ariaLiveElement = createAriaLiveElement(editor.getDocument()); + } + if (this.ariaLiveElement) { + this.ariaLiveElement.textContent = textToAnnounce; + } + } + } + + private getString(key: CompatibleKnownAnnounceStrings | KnownAnnounceStrings | undefined) { + if (this.stringsMapOrGetter == undefined || key == undefined) { + return undefined; + } + + if (typeof this.stringsMapOrGetter === 'function') { + return this.stringsMapOrGetter(key); + } else { + return this.stringsMapOrGetter.get(key); + } + } + + /** + * @internal + * Public only for unit testing. + * @returns + */ + public getAriaLiveElement() { + return this.ariaLiveElement; + } +} + +function formatString(text: string | undefined, formatStrings: string[]) { + if (text == undefined) { + return text; + } + + formatStrings.forEach((value, index) => { + text = text?.replace(`{${index}}`, value); + }); + + return text; +} diff --git a/packages/roosterjs-editor-plugins/lib/plugins/Announce/features/AnnounceFeatures.ts b/packages/roosterjs-editor-plugins/lib/plugins/Announce/features/AnnounceFeatures.ts new file mode 100644 index 00000000000..48d7e11a1f1 --- /dev/null +++ b/packages/roosterjs-editor-plugins/lib/plugins/Announce/features/AnnounceFeatures.ts @@ -0,0 +1,15 @@ +import announceNewListItemNumber from './announceNewListItem'; +import announceWarningOnLastCell from './announceWarningOnLastTableCell'; +import type { AnnounceFeature } from '../AnnounceFeature'; + +/** + * Announce feature keys + */ +export type AnnounceFeatureKey = 'announceNewListItem' | 'announceWarningOnLastTableCell'; +/** + * @internal + */ +export const AnnounceFeatures: Record = { + announceNewListItem: announceNewListItemNumber, + announceWarningOnLastTableCell: announceWarningOnLastCell, +}; diff --git a/packages/roosterjs-editor-plugins/lib/plugins/Announce/features/announceNewListItem.ts b/packages/roosterjs-editor-plugins/lib/plugins/Announce/features/announceNewListItem.ts new file mode 100644 index 00000000000..293d962ba90 --- /dev/null +++ b/packages/roosterjs-editor-plugins/lib/plugins/Announce/features/announceNewListItem.ts @@ -0,0 +1,17 @@ +import getAnnounceDataForList from '../../../pluginUtils/announceData/getAnnounceDataForList'; +import { Keys } from 'roosterjs-editor-types'; +import type { AnnounceFeature } from '../AnnounceFeature'; + +const LIST_SELECTOR = 'OL,UL'; +const LIST_ITEM_SELECTOR = 'LI'; + +const announceNewListItemNumber: AnnounceFeature = { + keys: [Keys.ENTER], + shouldHandle: editor => { + const li = editor.getElementAtCursor(LIST_ITEM_SELECTOR); + const list = editor.getElementAtCursor(LIST_SELECTOR); + return (!!(list && li) && getAnnounceDataForList(list, li)) || false; + }, +}; + +export default announceNewListItemNumber; diff --git a/packages/roosterjs-editor-plugins/lib/plugins/Announce/features/announceWarningOnLastTableCell.ts b/packages/roosterjs-editor-plugins/lib/plugins/Announce/features/announceWarningOnLastTableCell.ts new file mode 100644 index 00000000000..3c45a31cc8f --- /dev/null +++ b/packages/roosterjs-editor-plugins/lib/plugins/Announce/features/announceWarningOnLastTableCell.ts @@ -0,0 +1,40 @@ +import { contains, safeInstanceOf } from 'roosterjs-editor-dom'; +import { Keys, KnownAnnounceStrings, SelectionRangeTypes } from 'roosterjs-editor-types'; +import type { AnnounceFeature } from '../AnnounceFeature'; + +const TABLE_CELL_SELECTOR = 'td,th'; +const TABLE_SELECTOR = 'table'; + +const announceWarningOnLastCell: AnnounceFeature = { + shouldHandle: (editor, lastFocusedElement) => { + const selection = editor.getSelectionRangeEx(); + + return ( + selection?.type == SelectionRangeTypes.Normal && + selection.areAllCollapsed && + selection.ranges.length === 1 && + !contains( + lastFocusedElement, + selection.ranges[0].startContainer, + true /*treatSameNodeAsContain*/ + ) && + isLastCell() && { + defaultStrings: KnownAnnounceStrings.AnnounceOnFocusLastCell, + } + ); + function isLastCell(): boolean { + const table = editor.getElementAtCursor(TABLE_SELECTOR); + + if (safeInstanceOf(table, 'HTMLTableElement')) { + const allCells = table.querySelectorAll(TABLE_CELL_SELECTOR); + const focusedCell = editor.getElementAtCursor(TABLE_CELL_SELECTOR); + + return focusedCell == allCells.item(allCells.length - 1); + } + return false; + } + }, + keys: [Keys.TAB, Keys.UP, Keys.DOWN, Keys.LEFT, Keys.RIGHT], +}; + +export default announceWarningOnLastCell; diff --git a/packages/roosterjs-editor-plugins/lib/plugins/Announce/index.ts b/packages/roosterjs-editor-plugins/lib/plugins/Announce/index.ts new file mode 100644 index 00000000000..54373349ea0 --- /dev/null +++ b/packages/roosterjs-editor-plugins/lib/plugins/Announce/index.ts @@ -0,0 +1,3 @@ +export { AnnounceFeatureKey } from './features/AnnounceFeatures'; +export { AnnounceFeature } from './AnnounceFeature'; +export { default as Announce } from './AnnouncePlugin'; diff --git a/packages/roosterjs-editor-plugins/lib/plugins/AutoFormat/AutoFormat.ts b/packages/roosterjs-editor-plugins/lib/plugins/AutoFormat/AutoFormat.ts index 88c3ccd3b32..a71090d70dc 100644 --- a/packages/roosterjs-editor-plugins/lib/plugins/AutoFormat/AutoFormat.ts +++ b/packages/roosterjs-editor-plugins/lib/plugins/AutoFormat/AutoFormat.ts @@ -1,11 +1,5 @@ -import { - ChangeSource, - EditorPlugin, - IEditor, - PluginEvent, - PluginEventType, - PositionType, -} from 'roosterjs-editor-types'; +import { ChangeSource, PluginEventType, PositionType } from 'roosterjs-editor-types'; +import type { EditorPlugin, IEditor, PluginEvent } from 'roosterjs-editor-types'; const specialCharacters = /[`!@#$%^&*()_+\=\[\]{};':"\\|,.<>\/?~]/; diff --git a/packages/roosterjs-editor-plugins/lib/plugins/ContentEdit/ContentEdit.ts b/packages/roosterjs-editor-plugins/lib/plugins/ContentEdit/ContentEdit.ts index c76a5297644..a965f378322 100644 --- a/packages/roosterjs-editor-plugins/lib/plugins/ContentEdit/ContentEdit.ts +++ b/packages/roosterjs-editor-plugins/lib/plugins/ContentEdit/ContentEdit.ts @@ -1,6 +1,6 @@ import getAllFeatures from './getAllFeatures'; import { getObjectKeys } from 'roosterjs-editor-dom'; -import { +import type { ContentEditFeatureSettings, EditorPlugin, GenericContentEditFeature, diff --git a/packages/roosterjs-editor-plugins/lib/plugins/ContentEdit/features/autoLinkFeatures.ts b/packages/roosterjs-editor-plugins/lib/plugins/ContentEdit/features/autoLinkFeatures.ts index c70a0564c72..c8f92d740a3 100644 --- a/packages/roosterjs-editor-plugins/lib/plugins/ContentEdit/features/autoLinkFeatures.ts +++ b/packages/roosterjs-editor-plugins/lib/plugins/ContentEdit/features/autoLinkFeatures.ts @@ -1,14 +1,12 @@ +import { ChangeSource, Keys, PluginEventType } from 'roosterjs-editor-types'; import { removeLink, replaceWithNode } from 'roosterjs-editor-api'; -import { +import type { AutoLinkFeatureSettings, BuildInEditFeature, - ChangeSource, ClipboardData, IEditor, - Keys, LinkData, PluginEvent, - PluginEventType, PluginKeyboardEvent, } from 'roosterjs-editor-types'; import { diff --git a/packages/roosterjs-editor-plugins/lib/plugins/ContentEdit/features/codeFeatures.ts b/packages/roosterjs-editor-plugins/lib/plugins/ContentEdit/features/codeFeatures.ts index 0a35641a2aa..af636ad4676 100644 --- a/packages/roosterjs-editor-plugins/lib/plugins/ContentEdit/features/codeFeatures.ts +++ b/packages/roosterjs-editor-plugins/lib/plugins/ContentEdit/features/codeFeatures.ts @@ -1,3 +1,4 @@ +import { Keys, PositionType, QueryScope } from 'roosterjs-editor-types'; import { isNodeEmpty, cacheGetEventData, @@ -5,14 +6,11 @@ import { splitBalancedNodeRange, unwrap, } from 'roosterjs-editor-dom'; -import { +import type { BuildInEditFeature, PluginKeyboardEvent, - Keys, IEditor, - PositionType, CodeFeatureSettings, - QueryScope, } from 'roosterjs-editor-types'; const RemoveCodeWhenEnterOnEmptyLine: BuildInEditFeature = { diff --git a/packages/roosterjs-editor-plugins/lib/plugins/ContentEdit/features/cursorFeatures.ts b/packages/roosterjs-editor-plugins/lib/plugins/ContentEdit/features/cursorFeatures.ts index 9b34ee661dc..900b088ffca 100644 --- a/packages/roosterjs-editor-plugins/lib/plugins/ContentEdit/features/cursorFeatures.ts +++ b/packages/roosterjs-editor-plugins/lib/plugins/ContentEdit/features/cursorFeatures.ts @@ -1,8 +1,8 @@ import { Browser, getComputedStyle, Position } from 'roosterjs-editor-dom'; -import { +import { Keys } from 'roosterjs-editor-types'; +import type { BuildInEditFeature, CursorFeatureSettings, - Keys, PluginKeyboardEvent, } from 'roosterjs-editor-types'; diff --git a/packages/roosterjs-editor-plugins/lib/plugins/ContentEdit/features/entityFeatures.ts b/packages/roosterjs-editor-plugins/lib/plugins/ContentEdit/features/entityFeatures.ts index 72f55a1081f..f62bafe4763 100644 --- a/packages/roosterjs-editor-plugins/lib/plugins/ContentEdit/features/entityFeatures.ts +++ b/packages/roosterjs-editor-plugins/lib/plugins/ContentEdit/features/entityFeatures.ts @@ -1,5 +1,5 @@ -import { ContentTraverser } from 'roosterjs-editor-dom'; import { + ContentTraverser, addDelimiters, cacheGetEventData, createRange, @@ -12,18 +12,20 @@ import { Position, } from 'roosterjs-editor-dom'; import { + DelimiterClasses, + EntityOperation, + Keys, + NodeType, + PluginEventType, + PositionType, +} from 'roosterjs-editor-types'; + +import type { BuildInEditFeature, EntityFeatureSettings, - EntityOperation, IEditor, - Keys, PluginKeyboardEvent, - PositionType, - PluginEventType, - DelimiterClasses, PluginEvent, - NodeType, - ExperimentalFeatures, Entity, IContentTraverser, InlineElement, @@ -215,17 +217,13 @@ function cacheGetNeighborEntityElement( } /** - * @requires ExperimentalFeatures.InlineEntityReadOnlyDelimiters to be enabled * Content edit feature to move the cursor from Delimiters around Entities when using Right or Left Arrow Keys */ const MoveBetweenDelimitersFeature: BuildInEditFeature = { keys: [Keys.RIGHT, Keys.LEFT], allowFunctionKeys: true, shouldHandleEvent: (event: PluginKeyboardEvent, editor: IEditor) => { - if ( - event.rawEvent.altKey || - !editor.isFeatureEnabled(ExperimentalFeatures.InlineEntityReadOnlyDelimiters) - ) { + if (event.rawEvent.altKey) { return false; } @@ -270,16 +268,11 @@ const MoveBetweenDelimitersFeature: BuildInEditFeature = { }; /** - * @requires ExperimentalFeatures.InlineEntityReadOnlyDelimiters to be enabled * Content edit Feature to trigger a Delete Entity Operation when one of the Delimiter is about to be removed with DELETE or Backspace */ const RemoveEntityBetweenDelimitersFeature: BuildInEditFeature = { keys: [Keys.BACKSPACE, Keys.DELETE], shouldHandleEvent(event: PluginKeyboardEvent, editor: IEditor) { - if (!editor.isFeatureEnabled(ExperimentalFeatures.InlineEntityReadOnlyDelimiters)) { - return false; - } - const range = editor.getSelectionRange(); if (!range?.collapsed) { return false; @@ -433,11 +426,7 @@ function triggerOperation( entity, }); - if ( - entity.isReadonly && - !isBlockElement(entity.wrapper) && - editor.isFeatureEnabled(ExperimentalFeatures.InlineEntityReadOnlyDelimiters) - ) { + if (entity.isReadonly && !isBlockElement(entity.wrapper)) { if (event.rawEvent.defaultPrevented) { editor.runAsync(() => { if (!editor.contains(entity.wrapper)) { diff --git a/packages/roosterjs-editor-plugins/lib/plugins/ContentEdit/features/listFeatures.ts b/packages/roosterjs-editor-plugins/lib/plugins/ContentEdit/features/listFeatures.ts index 80533c6c19b..70bd3c0673f 100644 --- a/packages/roosterjs-editor-plugins/lib/plugins/ContentEdit/features/listFeatures.ts +++ b/packages/roosterjs-editor-plugins/lib/plugins/ContentEdit/features/listFeatures.ts @@ -1,44 +1,48 @@ +import getAnnounceDataForList from '../../../pluginUtils/announceData/getAnnounceDataForList'; import getAutoBulletListStyle from '../utils/getAutoBulletListStyle'; import getAutoNumberingListStyle from '../utils/getAutoNumberingListStyle'; -import { - blockFormat, - commitListChains, - setIndentation, - toggleBullet, - toggleNumbering, - toggleListType, -} from 'roosterjs-editor-api'; import { Browser, + cacheGetEventData, + createNumberDefinition, + createObjectDefinition, + createVListFromRegion, + findClosestElementAncestor, + getComputedStyle, + getMetadata, getTagOfNode, + isBlockElement, isNodeEmpty, isPositionAtBeginningOf, Position, - VListChain, - createVListFromRegion, - isBlockElement, - cacheGetEventData, safeInstanceOf, VList, - createObjectDefinition, - createNumberDefinition, - getMetadata, - findClosestElementAncestor, - getComputedStyle, + VListChain, } from 'roosterjs-editor-dom'; import { + blockFormat, + commitListChains, + setIndentation, + toggleBullet, + toggleNumbering, + toggleListType, +} from 'roosterjs-editor-api'; +import type { BuildInEditFeature, IEditor, - Indentation, ListFeatureSettings, - Keys, PluginKeyboardEvent, +} from 'roosterjs-editor-types'; +import { + Indentation, + Keys, QueryScope, ListType, ExperimentalFeatures, PositionType, NumberingListType, BulletListType, + ChangeSource, } from 'roosterjs-editor-types'; const PREVIOUS_BLOCK_CACHE_KEY = 'previousBlock'; @@ -92,7 +96,25 @@ const handleIndentationEvent = (indenting: boolean) => ( event.rawEvent.keyCode !== Keys.TAB && (currentElement = editor.getElementAtCursor()) && getComputedStyle(currentElement, 'direction') == 'rtl'; - setIndentation(editor, isRTL == indenting ? Indentation.Decrease : Indentation.Increase); + + editor.addUndoSnapshot( + () => { + setIndentation( + editor, + isRTL == indenting ? Indentation.Decrease : Indentation.Increase + ); + }, + ChangeSource.Format, + false /* canUndoByBackspace */, + { + getAnnounceData: () => + getAnnounceDataForList( + editor.getElementAtCursor('OL,UL'), + editor.getElementAtCursor('LI') + ), + } + ); + event.rawEvent.preventDefault(); }; diff --git a/packages/roosterjs-editor-plugins/lib/plugins/ContentEdit/features/markdownFeatures.ts b/packages/roosterjs-editor-plugins/lib/plugins/ContentEdit/features/markdownFeatures.ts index b8adae8ace8..ffda1b78d34 100644 --- a/packages/roosterjs-editor-plugins/lib/plugins/ContentEdit/features/markdownFeatures.ts +++ b/packages/roosterjs-editor-plugins/lib/plugins/ContentEdit/features/markdownFeatures.ts @@ -1,14 +1,12 @@ import { cacheGetEventData, createRange, Position, wrap } from 'roosterjs-editor-dom'; +import { ChangeSource, Keys, PositionType } from 'roosterjs-editor-types'; import type { CompatibleKeys } from 'roosterjs-editor-types/lib/compatibleTypes'; -import { +import type { BuildInEditFeature, - ChangeSource, IEditor, - Keys, MarkdownFeatureSettings, NodePosition, PluginKeyboardEvent, - PositionType, } from 'roosterjs-editor-types'; const ZERO_WIDTH_SPACE = '\u200B'; diff --git a/packages/roosterjs-editor-plugins/lib/plugins/ContentEdit/features/quoteFeatures.ts b/packages/roosterjs-editor-plugins/lib/plugins/ContentEdit/features/quoteFeatures.ts index 983e201cdbd..f03d89ef09f 100644 --- a/packages/roosterjs-editor-plugins/lib/plugins/ContentEdit/features/quoteFeatures.ts +++ b/packages/roosterjs-editor-plugins/lib/plugins/ContentEdit/features/quoteFeatures.ts @@ -1,10 +1,9 @@ import { clearFormat } from 'roosterjs-editor-api'; -import { +import { Keys, PositionType } from 'roosterjs-editor-types'; +import type { BuildInEditFeature, IEditor, - Keys, PluginKeyboardEvent, - PositionType, QuoteFeatureSettings, } from 'roosterjs-editor-types'; import { diff --git a/packages/roosterjs-editor-plugins/lib/plugins/ContentEdit/features/shortcutFeatures.ts b/packages/roosterjs-editor-plugins/lib/plugins/ContentEdit/features/shortcutFeatures.ts index 1488274e571..49551d13415 100644 --- a/packages/roosterjs-editor-plugins/lib/plugins/ContentEdit/features/shortcutFeatures.ts +++ b/packages/roosterjs-editor-plugins/lib/plugins/ContentEdit/features/shortcutFeatures.ts @@ -1,10 +1,8 @@ import { Browser, cacheGetEventData } from 'roosterjs-editor-dom'; -import { +import { FontSizeChange, Keys, PluginEventType } from 'roosterjs-editor-types'; +import type { BuildInEditFeature, - FontSizeChange, IEditor, - Keys, - PluginEventType, PluginKeyboardEvent, ShortcutFeatureSettings, } from 'roosterjs-editor-types'; diff --git a/packages/roosterjs-editor-plugins/lib/plugins/ContentEdit/features/structuredNodeFeatures.ts b/packages/roosterjs-editor-plugins/lib/plugins/ContentEdit/features/structuredNodeFeatures.ts index 7f980d5791f..a536459f897 100644 --- a/packages/roosterjs-editor-plugins/lib/plugins/ContentEdit/features/structuredNodeFeatures.ts +++ b/packages/roosterjs-editor-plugins/lib/plugins/ContentEdit/features/structuredNodeFeatures.ts @@ -1,10 +1,8 @@ -import { +import { Keys, KnownCreateElementDataIndex, PositionType } from 'roosterjs-editor-types'; +import type { BuildInEditFeature, IEditor, - Keys, - KnownCreateElementDataIndex, PluginKeyboardEvent, - PositionType, StructuredNodeFeatureSettings, } from 'roosterjs-editor-types'; import { diff --git a/packages/roosterjs-editor-plugins/lib/plugins/ContentEdit/features/tableFeatures.ts b/packages/roosterjs-editor-plugins/lib/plugins/ContentEdit/features/tableFeatures.ts index 3cf07da978a..ca4d39e2422 100644 --- a/packages/roosterjs-editor-plugins/lib/plugins/ContentEdit/features/tableFeatures.ts +++ b/packages/roosterjs-editor-plugins/lib/plugins/ContentEdit/features/tableFeatures.ts @@ -1,16 +1,18 @@ import { editTable, setIndentation } from 'roosterjs-editor-api'; -import { +import type { BuildInEditFeature, IEditor, + PluginEvent, + TableFeatureSettings, + PluginKeyboardEvent, + TableSelectionRange, +} from 'roosterjs-editor-types'; +import { Keys, NodeType, - PluginEvent, PositionType, - TableFeatureSettings, TableOperation, - PluginKeyboardEvent, SelectionRangeTypes, - TableSelectionRange, Indentation, ExperimentalFeatures, } from 'roosterjs-editor-types'; diff --git a/packages/roosterjs-editor-plugins/lib/plugins/ContentEdit/features/textFeatures.ts b/packages/roosterjs-editor-plugins/lib/plugins/ContentEdit/features/textFeatures.ts index 2a8e379fe8a..ddc9187282e 100644 --- a/packages/roosterjs-editor-plugins/lib/plugins/ContentEdit/features/textFeatures.ts +++ b/packages/roosterjs-editor-plugins/lib/plugins/ContentEdit/features/textFeatures.ts @@ -6,18 +6,20 @@ import { Position, queryElements, } from 'roosterjs-editor-dom'; -import { +import type { BuildInEditFeature, IEditor, - Indentation, TextFeatureSettings, - Keys, PluginKeyboardEvent, + NodePosition, +} from 'roosterjs-editor-types'; +import { + Indentation, + Keys, SelectionRangeTypes, ContentPosition, PositionType, ExperimentalFeatures, - NodePosition, QueryScope, } from 'roosterjs-editor-types'; diff --git a/packages/roosterjs-editor-plugins/lib/plugins/ContentEdit/getAllFeatures.ts b/packages/roosterjs-editor-plugins/lib/plugins/ContentEdit/getAllFeatures.ts index 32b438245f3..9f5e54394a0 100644 --- a/packages/roosterjs-editor-plugins/lib/plugins/ContentEdit/getAllFeatures.ts +++ b/packages/roosterjs-editor-plugins/lib/plugins/ContentEdit/getAllFeatures.ts @@ -1,4 +1,5 @@ import { AutoLinkFeatures } from './features/autoLinkFeatures'; +import { CodeFeatures } from './features/codeFeatures'; import { CursorFeatures } from './features/cursorFeatures'; import { EntityFeatures } from './features/entityFeatures'; import { ListFeatures } from './features/listFeatures'; @@ -8,8 +9,7 @@ import { ShortcutFeatures } from './features/shortcutFeatures'; import { StructuredNodeFeatures } from './features/structuredNodeFeatures'; import { TableFeatures } from './features/tableFeatures'; import { TextFeatures } from './features/textFeatures'; -import { CodeFeatures } from './features/codeFeatures'; -import { +import type { BuildInEditFeature, ContentEditFeatureSettings, PluginEvent, diff --git a/packages/roosterjs-editor-plugins/lib/plugins/ContentEdit/utils/getAutoNumberingListStyle.ts b/packages/roosterjs-editor-plugins/lib/plugins/ContentEdit/utils/getAutoNumberingListStyle.ts index 44a5295abf3..a8457eee1b5 100644 --- a/packages/roosterjs-editor-plugins/lib/plugins/ContentEdit/utils/getAutoNumberingListStyle.ts +++ b/packages/roosterjs-editor-plugins/lib/plugins/ContentEdit/utils/getAutoNumberingListStyle.ts @@ -1,6 +1,6 @@ import convertAlphaToDecimals from './convertAlphaToDecimals'; import { NumberingListType } from 'roosterjs-editor-types'; -import { VListChain } from 'roosterjs-editor-dom'; +import type { VListChain } from 'roosterjs-editor-dom'; const enum NumberingTypes { Decimal = 1, diff --git a/packages/roosterjs-editor-plugins/lib/plugins/ContextMenu/ContextMenu.ts b/packages/roosterjs-editor-plugins/lib/plugins/ContextMenu/ContextMenu.ts index d86aa3f1792..0f33ee24c25 100644 --- a/packages/roosterjs-editor-plugins/lib/plugins/ContextMenu/ContextMenu.ts +++ b/packages/roosterjs-editor-plugins/lib/plugins/ContextMenu/ContextMenu.ts @@ -1,11 +1,6 @@ import { createElement } from 'roosterjs-editor-dom'; -import { - EditorPlugin, - IEditor, - KnownCreateElementDataIndex, - PluginEvent, - PluginEventType, -} from 'roosterjs-editor-types'; +import { KnownCreateElementDataIndex, PluginEventType } from 'roosterjs-editor-types'; +import type { EditorPlugin, IEditor, PluginEvent } from 'roosterjs-editor-types'; /** * Context Menu options for ContextMenu plugin diff --git a/packages/roosterjs-editor-plugins/lib/plugins/CustomReplace/CustomReplace.ts b/packages/roosterjs-editor-plugins/lib/plugins/CustomReplace/CustomReplace.ts index 9c930c57731..596a71166fb 100644 --- a/packages/roosterjs-editor-plugins/lib/plugins/CustomReplace/CustomReplace.ts +++ b/packages/roosterjs-editor-plugins/lib/plugins/CustomReplace/CustomReplace.ts @@ -1,11 +1,5 @@ -import { - CustomReplacement, - EditorPlugin, - IEditor, - PluginEvent, - PluginEventType, - PositionType, -} from 'roosterjs-editor-types'; +import { PluginEventType, PositionType } from 'roosterjs-editor-types'; +import type { CustomReplacement, EditorPlugin, IEditor, PluginEvent } from 'roosterjs-editor-types'; const makeReplacement = ( sourceString: string, diff --git a/packages/roosterjs-editor-plugins/lib/plugins/CutPasteListChain/CutPasteListChain.ts b/packages/roosterjs-editor-plugins/lib/plugins/CutPasteListChain/CutPasteListChain.ts index a32d493b1ee..e4551fc6b47 100644 --- a/packages/roosterjs-editor-plugins/lib/plugins/CutPasteListChain/CutPasteListChain.ts +++ b/packages/roosterjs-editor-plugins/lib/plugins/CutPasteListChain/CutPasteListChain.ts @@ -1,12 +1,7 @@ +import { ChangeSource, PluginEventType } from 'roosterjs-editor-types'; import { commitListChains } from 'roosterjs-editor-api'; import { VListChain } from 'roosterjs-editor-dom'; -import { - ChangeSource, - EditorPlugin, - IEditor, - PluginEvent, - PluginEventType, -} from 'roosterjs-editor-types'; +import type { EditorPlugin, IEditor, PluginEvent } from 'roosterjs-editor-types'; import type { CompatibleChangeSource } from 'roosterjs-editor-types/lib/compatibleTypes'; /** diff --git a/packages/roosterjs-editor-plugins/lib/plugins/HyperLink/HyperLink.ts b/packages/roosterjs-editor-plugins/lib/plugins/HyperLink/HyperLink.ts index 77aad119c11..516329ef4f1 100644 --- a/packages/roosterjs-editor-plugins/lib/plugins/HyperLink/HyperLink.ts +++ b/packages/roosterjs-editor-plugins/lib/plugins/HyperLink/HyperLink.ts @@ -1,13 +1,6 @@ +import { ChangeSource, Keys, PluginEventType } from 'roosterjs-editor-types'; import { isCharacterValue, isCtrlOrMetaPressed, matchLink } from 'roosterjs-editor-dom'; -import { - ChangeSource, - DOMEventHandler, - EditorPlugin, - IEditor, - Keys, - PluginEvent, - PluginEventType, -} from 'roosterjs-editor-types'; +import type { DOMEventHandler, EditorPlugin, IEditor, PluginEvent } from 'roosterjs-editor-types'; /** * An editor plugin that show a tooltip for existing link diff --git a/packages/roosterjs-editor-plugins/lib/plugins/ImageEdit/ImageEdit.ts b/packages/roosterjs-editor-plugins/lib/plugins/ImageEdit/ImageEdit.ts index 4ef80aabcd7..79793a051be 100644 --- a/packages/roosterjs-editor-plugins/lib/plugins/ImageEdit/ImageEdit.ts +++ b/packages/roosterjs-editor-plugins/lib/plugins/ImageEdit/ImageEdit.ts @@ -1,17 +1,18 @@ import applyChange from './editInfoUtils/applyChange'; import canRegenerateImage from './api/canRegenerateImage'; -import DragAndDropContext, { DNDDirectionX, DnDDirectionY } from './types/DragAndDropContext'; -import DragAndDropHandler from '../../pluginUtils/DragAndDropHandler'; import DragAndDropHelper from '../../pluginUtils/DragAndDropHelper'; import getGeneratedImageSize from './editInfoUtils/getGeneratedImageSize'; -import ImageEditInfo from './types/ImageEditInfo'; -import ImageHtmlOptions from './types/ImageHtmlOptions'; import { Cropper, getCropHTML } from './imageEditors/Cropper'; import { deleteEditInfo, getEditInfoFromImage } from './editInfoUtils/editInfo'; import { getRotateHTML, Rotator, updateRotateHandleState } from './imageEditors/Rotator'; import { ImageEditElementClass } from './types/ImageEditElementClass'; import { MIN_HEIGHT_WIDTH } from './constants/constants'; import { tryToConvertGifToPng } from './editInfoUtils/tryToConvertGifToPng'; +import type { DNDDirectionX, DnDDirectionY } from './types/DragAndDropContext'; +import type DragAndDropContext from './types/DragAndDropContext'; +import type DragAndDropHandler from '../../pluginUtils/DragAndDropHandler'; +import type ImageEditInfo from './types/ImageEditInfo'; +import type ImageHtmlOptions from './types/ImageHtmlOptions'; import { arrayPush, Browser, @@ -23,24 +24,26 @@ import { unwrap, wrap, } from 'roosterjs-editor-dom'; +import type { OnShowResizeHandle } from './imageEditors/Resizer'; import { Resizer, doubleCheckResize, getSideResizeHTML, getCornerResizeHTML, - OnShowResizeHandle, getResizeBordersHTML, } from './imageEditors/Resizer'; -import { - ImageEditOperation, +import type { ImageEditOptions, EditorPlugin, IEditor, PluginEvent, - PluginEventType, CreateElementData, - KnownCreateElementDataIndex, ModeIndependentColor, +} from 'roosterjs-editor-types'; +import { + ImageEditOperation, + PluginEventType, + KnownCreateElementDataIndex, SelectionRangeTypes, ChangeSource, } from 'roosterjs-editor-types'; @@ -424,6 +427,7 @@ export default class ImageEdit implements EditorPlugin { this.clonedImage = this.image.cloneNode(true) as HTMLImageElement; this.clonedImage.removeAttribute('id'); this.clonedImage.style.removeProperty('max-width'); + this.clonedImage.style.removeProperty('max-height'); this.clonedImage.style.width = this.editInfo.widthPx + 'px'; this.clonedImage.style.height = this.editInfo.heightPx + 'px'; this.wrapper = createElement( diff --git a/packages/roosterjs-editor-plugins/lib/plugins/ImageEdit/api/resetImage.ts b/packages/roosterjs-editor-plugins/lib/plugins/ImageEdit/api/resetImage.ts index 20c404bac57..dcdeae880ae 100644 --- a/packages/roosterjs-editor-plugins/lib/plugins/ImageEdit/api/resetImage.ts +++ b/packages/roosterjs-editor-plugins/lib/plugins/ImageEdit/api/resetImage.ts @@ -1,5 +1,6 @@ -import { ChangeSource, IEditor } from 'roosterjs-editor-types'; +import { ChangeSource } from 'roosterjs-editor-types'; import { deleteEditInfo } from '../editInfoUtils/editInfo'; +import type { IEditor } from 'roosterjs-editor-types'; /** * Remove explicit width & height attributes on the image element. diff --git a/packages/roosterjs-editor-plugins/lib/plugins/ImageEdit/api/resizeByPercentage.ts b/packages/roosterjs-editor-plugins/lib/plugins/ImageEdit/api/resizeByPercentage.ts index b8927dff762..d9b27ee8bb0 100644 --- a/packages/roosterjs-editor-plugins/lib/plugins/ImageEdit/api/resizeByPercentage.ts +++ b/packages/roosterjs-editor-plugins/lib/plugins/ImageEdit/api/resizeByPercentage.ts @@ -1,8 +1,9 @@ import applyChange from '../editInfoUtils/applyChange'; import getTargetSizeByPercentage from '../editInfoUtils/getTargetSizeByPercentage'; import isResizedTo from './isResizedTo'; -import { ChangeSource, IEditor } from 'roosterjs-editor-types'; +import { ChangeSource } from 'roosterjs-editor-types'; import { getEditInfoFromImage } from '../editInfoUtils/editInfo'; +import type { IEditor } from 'roosterjs-editor-types'; /** * Resize the image by percentage of its natural size. If the image is cropped or rotated, diff --git a/packages/roosterjs-editor-plugins/lib/plugins/ImageEdit/constants/constants.ts b/packages/roosterjs-editor-plugins/lib/plugins/ImageEdit/constants/constants.ts index ab693701b73..204ef0397a8 100644 --- a/packages/roosterjs-editor-plugins/lib/plugins/ImageEdit/constants/constants.ts +++ b/packages/roosterjs-editor-plugins/lib/plugins/ImageEdit/constants/constants.ts @@ -1,4 +1,4 @@ -import { DNDDirectionX, DnDDirectionY } from '../types/DragAndDropContext'; +import type { DNDDirectionX, DnDDirectionY } from '../types/DragAndDropContext'; export const RESIZE_HANDLE_SIZE = 10; export const RESIZE_HANDLE_MARGIN = 6; diff --git a/packages/roosterjs-editor-plugins/lib/plugins/ImageEdit/editInfoUtils/applyChange.ts b/packages/roosterjs-editor-plugins/lib/plugins/ImageEdit/editInfoUtils/applyChange.ts index 7c33a4791e2..7b093c75c66 100644 --- a/packages/roosterjs-editor-plugins/lib/plugins/ImageEdit/editInfoUtils/applyChange.ts +++ b/packages/roosterjs-editor-plugins/lib/plugins/ImageEdit/editInfoUtils/applyChange.ts @@ -1,9 +1,10 @@ import checkEditInfoState, { ImageEditInfoState } from './checkEditInfoState'; import generateDataURL from './generateDataURL'; import getGeneratedImageSize from './getGeneratedImageSize'; -import ImageEditInfo from '../types/ImageEditInfo'; import { deleteEditInfo, getEditInfoFromImage, saveEditInfo } from './editInfo'; -import { IEditor, PluginEventType } from 'roosterjs-editor-types'; +import { PluginEventType } from 'roosterjs-editor-types'; +import type ImageEditInfo from '../types/ImageEditInfo'; +import type { IEditor } from 'roosterjs-editor-types'; /** * @internal @@ -79,5 +80,6 @@ export default function applyChange( image.style.removeProperty('width'); image.style.removeProperty('height'); image.style.removeProperty('max-width'); + image.style.removeProperty('max-height'); } } diff --git a/packages/roosterjs-editor-plugins/lib/plugins/ImageEdit/editInfoUtils/checkEditInfoState.ts b/packages/roosterjs-editor-plugins/lib/plugins/ImageEdit/editInfoUtils/checkEditInfoState.ts index 68bf055b964..33158689272 100644 --- a/packages/roosterjs-editor-plugins/lib/plugins/ImageEdit/editInfoUtils/checkEditInfoState.ts +++ b/packages/roosterjs-editor-plugins/lib/plugins/ImageEdit/editInfoUtils/checkEditInfoState.ts @@ -1,4 +1,5 @@ -import ImageEditInfo, { CropInfo, ResizeInfo, RotateInfo } from '../types/ImageEditInfo'; +import type { CropInfo, ResizeInfo, RotateInfo } from '../types/ImageEditInfo'; +import type ImageEditInfo from '../types/ImageEditInfo'; const RESIZE_KEYS: (keyof ResizeInfo)[] = ['widthPx', 'heightPx']; const ROTATE_KEYS: (keyof RotateInfo)[] = ['angleRad']; diff --git a/packages/roosterjs-editor-plugins/lib/plugins/ImageEdit/editInfoUtils/editInfo.ts b/packages/roosterjs-editor-plugins/lib/plugins/ImageEdit/editInfoUtils/editInfo.ts index f82fe54abcb..6a9c33fd471 100644 --- a/packages/roosterjs-editor-plugins/lib/plugins/ImageEdit/editInfoUtils/editInfo.ts +++ b/packages/roosterjs-editor-plugins/lib/plugins/ImageEdit/editInfoUtils/editInfo.ts @@ -1,6 +1,6 @@ import checkEditInfoState, { ImageEditInfoState } from './checkEditInfoState'; -import ImageEditInfo from '../types/ImageEditInfo'; import { getMetadata, removeMetadata, setMetadata } from 'roosterjs-editor-dom'; +import type ImageEditInfo from '../types/ImageEditInfo'; /** * @internal diff --git a/packages/roosterjs-editor-plugins/lib/plugins/ImageEdit/editInfoUtils/generateDataURL.ts b/packages/roosterjs-editor-plugins/lib/plugins/ImageEdit/editInfoUtils/generateDataURL.ts index d47400528b0..b438eab9c57 100644 --- a/packages/roosterjs-editor-plugins/lib/plugins/ImageEdit/editInfoUtils/generateDataURL.ts +++ b/packages/roosterjs-editor-plugins/lib/plugins/ImageEdit/editInfoUtils/generateDataURL.ts @@ -1,5 +1,5 @@ import getGeneratedImageSize from './getGeneratedImageSize'; -import ImageEditInfo from '../types/ImageEditInfo'; +import type ImageEditInfo from '../types/ImageEditInfo'; /** * @internal diff --git a/packages/roosterjs-editor-plugins/lib/plugins/ImageEdit/editInfoUtils/getGeneratedImageSize.ts b/packages/roosterjs-editor-plugins/lib/plugins/ImageEdit/editInfoUtils/getGeneratedImageSize.ts index 30befbdbe93..7553f2903b5 100644 --- a/packages/roosterjs-editor-plugins/lib/plugins/ImageEdit/editInfoUtils/getGeneratedImageSize.ts +++ b/packages/roosterjs-editor-plugins/lib/plugins/ImageEdit/editInfoUtils/getGeneratedImageSize.ts @@ -1,5 +1,5 @@ -import GeneratedImageSize from '../types/GeneratedImageSize'; -import ImageEditInfo from '../types/ImageEditInfo'; +import type GeneratedImageSize from '../types/GeneratedImageSize'; +import type ImageEditInfo from '../types/ImageEditInfo'; /** * @internal diff --git a/packages/roosterjs-editor-plugins/lib/plugins/ImageEdit/editInfoUtils/getTargetSizeByPercentage.ts b/packages/roosterjs-editor-plugins/lib/plugins/ImageEdit/editInfoUtils/getTargetSizeByPercentage.ts index 6501e95cd86..d8b96c17fd4 100644 --- a/packages/roosterjs-editor-plugins/lib/plugins/ImageEdit/editInfoUtils/getTargetSizeByPercentage.ts +++ b/packages/roosterjs-editor-plugins/lib/plugins/ImageEdit/editInfoUtils/getTargetSizeByPercentage.ts @@ -1,5 +1,5 @@ -import ImageEditInfo from '../types/ImageEditInfo'; -import ImageSize from '../types/ImageSize'; +import type ImageEditInfo from '../types/ImageEditInfo'; +import type ImageSize from '../types/ImageSize'; /** * @internal diff --git a/packages/roosterjs-editor-plugins/lib/plugins/ImageEdit/editInfoUtils/tryToConvertGifToPng.ts b/packages/roosterjs-editor-plugins/lib/plugins/ImageEdit/editInfoUtils/tryToConvertGifToPng.ts index b94ccf5bd0b..68eb410d8bd 100644 --- a/packages/roosterjs-editor-plugins/lib/plugins/ImageEdit/editInfoUtils/tryToConvertGifToPng.ts +++ b/packages/roosterjs-editor-plugins/lib/plugins/ImageEdit/editInfoUtils/tryToConvertGifToPng.ts @@ -1,5 +1,5 @@ import generateDataURL from './generateDataURL'; -import ImageEditInfo from '../types/ImageEditInfo'; +import type ImageEditInfo from '../types/ImageEditInfo'; /** * @internal diff --git a/packages/roosterjs-editor-plugins/lib/plugins/ImageEdit/imageEditors/Cropper.ts b/packages/roosterjs-editor-plugins/lib/plugins/ImageEdit/imageEditors/Cropper.ts index 181d53796c3..1f605d9fdcd 100644 --- a/packages/roosterjs-editor-plugins/lib/plugins/ImageEdit/imageEditors/Cropper.ts +++ b/packages/roosterjs-editor-plugins/lib/plugins/ImageEdit/imageEditors/Cropper.ts @@ -1,9 +1,10 @@ -import DragAndDropContext, { DNDDirectionX, DnDDirectionY } from '../types/DragAndDropContext'; -import DragAndDropHandler from '../../../pluginUtils/DragAndDropHandler'; -import { CreateElementData } from 'roosterjs-editor-types'; -import { CropInfo } from '../types/ImageEditInfo'; import { ImageEditElementClass } from '../types/ImageEditElementClass'; import { rotateCoordinate } from './Resizer'; +import type { DNDDirectionX, DnDDirectionY } from '../types/DragAndDropContext'; +import type DragAndDropContext from '../types/DragAndDropContext'; +import type DragAndDropHandler from '../../../pluginUtils/DragAndDropHandler'; +import type { CreateElementData } from 'roosterjs-editor-types'; +import type { CropInfo } from '../types/ImageEditInfo'; import { CROP_HANDLE_SIZE, CROP_HANDLE_WIDTH, diff --git a/packages/roosterjs-editor-plugins/lib/plugins/ImageEdit/imageEditors/Resizer.ts b/packages/roosterjs-editor-plugins/lib/plugins/ImageEdit/imageEditors/Resizer.ts index c9df9f99a2e..06292a4f988 100644 --- a/packages/roosterjs-editor-plugins/lib/plugins/ImageEdit/imageEditors/Resizer.ts +++ b/packages/roosterjs-editor-plugins/lib/plugins/ImageEdit/imageEditors/Resizer.ts @@ -1,10 +1,12 @@ -import DragAndDropContext, { DNDDirectionX, DnDDirectionY } from '../types/DragAndDropContext'; -import DragAndDropHandler from '../../../pluginUtils/DragAndDropHandler'; -import ImageEditInfo, { ResizeInfo } from '../types/ImageEditInfo'; -import ImageHtmlOptions from '../types/ImageHtmlOptions'; -import { CreateElementData } from 'roosterjs-editor-types'; import { ImageEditElementClass } from '../types/ImageEditElementClass'; import { RESIZE_HANDLE_MARGIN, RESIZE_HANDLE_SIZE, Xs, Ys } from '../constants/constants'; +import type { DNDDirectionX, DnDDirectionY } from '../types/DragAndDropContext'; +import type DragAndDropContext from '../types/DragAndDropContext'; +import type DragAndDropHandler from '../../../pluginUtils/DragAndDropHandler'; +import type { ResizeInfo } from '../types/ImageEditInfo'; +import type ImageEditInfo from '../types/ImageEditInfo'; +import type ImageHtmlOptions from '../types/ImageHtmlOptions'; +import type { CreateElementData } from 'roosterjs-editor-types'; /** * An optional callback to allow customize resize handle element of image resizing. diff --git a/packages/roosterjs-editor-plugins/lib/plugins/ImageEdit/imageEditors/Rotator.ts b/packages/roosterjs-editor-plugins/lib/plugins/ImageEdit/imageEditors/Rotator.ts index b40195e4ec8..c3970238edb 100644 --- a/packages/roosterjs-editor-plugins/lib/plugins/ImageEdit/imageEditors/Rotator.ts +++ b/packages/roosterjs-editor-plugins/lib/plugins/ImageEdit/imageEditors/Rotator.ts @@ -1,9 +1,9 @@ -import DragAndDropContext from '../types/DragAndDropContext'; -import DragAndDropHandler from '../../../pluginUtils/DragAndDropHandler'; -import ImageHtmlOptions from '../types/ImageHtmlOptions'; -import { CreateElementData, Rect } from 'roosterjs-editor-types'; import { ImageEditElementClass } from '../types/ImageEditElementClass'; -import { RotateInfo } from '../types/ImageEditInfo'; +import type DragAndDropContext from '../types/DragAndDropContext'; +import type DragAndDropHandler from '../../../pluginUtils/DragAndDropHandler'; +import type ImageHtmlOptions from '../types/ImageHtmlOptions'; +import type { CreateElementData, Rect } from 'roosterjs-editor-types'; +import type { RotateInfo } from '../types/ImageEditInfo'; import { DEFAULT_ROTATE_HANDLE_HEIGHT, DEG_PER_RAD, diff --git a/packages/roosterjs-editor-plugins/lib/plugins/ImageEdit/types/DragAndDropContext.ts b/packages/roosterjs-editor-plugins/lib/plugins/ImageEdit/types/DragAndDropContext.ts index 607a526ed5c..19f285dd008 100644 --- a/packages/roosterjs-editor-plugins/lib/plugins/ImageEdit/types/DragAndDropContext.ts +++ b/packages/roosterjs-editor-plugins/lib/plugins/ImageEdit/types/DragAndDropContext.ts @@ -1,6 +1,6 @@ -import ImageEditInfo from './ImageEditInfo'; -import { ImageEditElementClass } from './ImageEditElementClass'; -import { ImageEditOptions } from 'roosterjs-editor-types'; +import type ImageEditInfo from './ImageEditInfo'; +import type { ImageEditElementClass } from './ImageEditElementClass'; +import type { ImageEditOptions } from 'roosterjs-editor-types'; /** * Horizontal direction types for image edit diff --git a/packages/roosterjs-editor-plugins/lib/plugins/Paste/Paste.ts b/packages/roosterjs-editor-plugins/lib/plugins/Paste/Paste.ts index bbfdb8faabf..8e80cbc8157 100644 --- a/packages/roosterjs-editor-plugins/lib/plugins/Paste/Paste.ts +++ b/packages/roosterjs-editor-plugins/lib/plugins/Paste/Paste.ts @@ -8,13 +8,13 @@ import handleLineMerge from './lineMerge/handleLineMerge'; import sanitizeHtmlColorsFromPastedContent from './sanitizeHtmlColorsFromPastedContent/sanitizeHtmlColorsFromPastedContent'; import sanitizeLinks from './sanitizeLinks/sanitizeLinks'; import { chainSanitizerCallback, getPasteSource } from 'roosterjs-editor-dom'; -import { HtmlSanitizerOptions, KnownPasteSourceType } from 'roosterjs-editor-types'; -import { +import { KnownPasteSourceType, PasteType, PluginEventType } from 'roosterjs-editor-types'; + +import type { + HtmlSanitizerOptions, EditorPlugin, IEditor, - PasteType, PluginEvent, - PluginEventType, } from 'roosterjs-editor-types'; const GOOGLE_SHEET_NODE_NAME = 'google-sheets-html-origin'; diff --git a/packages/roosterjs-editor-plugins/lib/plugins/Paste/excelConverter/convertPastedContentFromExcel.ts b/packages/roosterjs-editor-plugins/lib/plugins/Paste/excelConverter/convertPastedContentFromExcel.ts index a5a0d47a7da..535fcca4412 100644 --- a/packages/roosterjs-editor-plugins/lib/plugins/Paste/excelConverter/convertPastedContentFromExcel.ts +++ b/packages/roosterjs-editor-plugins/lib/plugins/Paste/excelConverter/convertPastedContentFromExcel.ts @@ -1,5 +1,5 @@ -import { BeforePasteEvent, TrustedHTMLHandler } from 'roosterjs-editor-types'; import { chainSanitizerCallback, getTagOfNode, moveChildNodes } from 'roosterjs-editor-dom'; +import type { BeforePasteEvent, TrustedHTMLHandler } from 'roosterjs-editor-types'; const LAST_TD_END_REGEX = /<\/\s*td\s*>((?!<\/\s*tr\s*>)[\s\S])*$/i; const LAST_TR_END_REGEX = /<\/\s*tr\s*>((?!<\/\s*table\s*>)[\s\S])*$/i; diff --git a/packages/roosterjs-editor-plugins/lib/plugins/Paste/imageConverter/convertPasteContentForSingleImage.ts b/packages/roosterjs-editor-plugins/lib/plugins/Paste/imageConverter/convertPasteContentForSingleImage.ts index 3b7da6307a8..fdb6d311a67 100644 --- a/packages/roosterjs-editor-plugins/lib/plugins/Paste/imageConverter/convertPasteContentForSingleImage.ts +++ b/packages/roosterjs-editor-plugins/lib/plugins/Paste/imageConverter/convertPasteContentForSingleImage.ts @@ -1,5 +1,5 @@ -import { BeforePasteEvent, TrustedHTMLHandler } from 'roosterjs-editor-types'; import { moveChildNodes } from 'roosterjs-editor-dom'; +import type { BeforePasteEvent, TrustedHTMLHandler } from 'roosterjs-editor-types'; /** * @internal diff --git a/packages/roosterjs-editor-plugins/lib/plugins/Paste/officeOnlineConverter/convertPastedContentFromOfficeOnline.ts b/packages/roosterjs-editor-plugins/lib/plugins/Paste/officeOnlineConverter/convertPastedContentFromOfficeOnline.ts index 8a69427b7d1..719054ad96c 100644 --- a/packages/roosterjs-editor-plugins/lib/plugins/Paste/officeOnlineConverter/convertPastedContentFromOfficeOnline.ts +++ b/packages/roosterjs-editor-plugins/lib/plugins/Paste/officeOnlineConverter/convertPastedContentFromOfficeOnline.ts @@ -1,5 +1,5 @@ import { chainSanitizerCallback } from 'roosterjs-editor-dom'; -import { HtmlSanitizerOptions } from 'roosterjs-editor-types'; +import type { HtmlSanitizerOptions } from 'roosterjs-editor-types'; import convertPastedContentFromWordOnline, { isWordOnlineWithList, } from './convertPastedContentFromWordOnline'; diff --git a/packages/roosterjs-editor-plugins/lib/plugins/Paste/officeOnlineConverter/convertPastedContentFromWordOnline.ts b/packages/roosterjs-editor-plugins/lib/plugins/Paste/officeOnlineConverter/convertPastedContentFromWordOnline.ts index ca3e5645a3d..63f42090c9c 100644 --- a/packages/roosterjs-editor-plugins/lib/plugins/Paste/officeOnlineConverter/convertPastedContentFromWordOnline.ts +++ b/packages/roosterjs-editor-plugins/lib/plugins/Paste/officeOnlineConverter/convertPastedContentFromWordOnline.ts @@ -1,4 +1,5 @@ -import ListItemBlock, { createListItemBlock } from './ListItemBlock'; +import { createListItemBlock } from './ListItemBlock'; +import type ListItemBlock from './ListItemBlock'; import { splitParentNode, diff --git a/packages/roosterjs-editor-plugins/lib/plugins/Paste/pptConverter/convertPastedContentFromPowerPoint.ts b/packages/roosterjs-editor-plugins/lib/plugins/Paste/pptConverter/convertPastedContentFromPowerPoint.ts index 072c8a4f4ea..447be849a34 100644 --- a/packages/roosterjs-editor-plugins/lib/plugins/Paste/pptConverter/convertPastedContentFromPowerPoint.ts +++ b/packages/roosterjs-editor-plugins/lib/plugins/Paste/pptConverter/convertPastedContentFromPowerPoint.ts @@ -1,5 +1,5 @@ -import { BeforePasteEvent, TrustedHTMLHandler } from 'roosterjs-editor-types'; import { moveChildNodes } from 'roosterjs-editor-dom'; +import type { BeforePasteEvent, TrustedHTMLHandler } from 'roosterjs-editor-types'; /** * @internal diff --git a/packages/roosterjs-editor-plugins/lib/plugins/Paste/sanitizeHtmlColorsFromPastedContent/sanitizeHtmlColorsFromPastedContent.ts b/packages/roosterjs-editor-plugins/lib/plugins/Paste/sanitizeHtmlColorsFromPastedContent/sanitizeHtmlColorsFromPastedContent.ts index e3da37455a6..5bc527c6477 100644 --- a/packages/roosterjs-editor-plugins/lib/plugins/Paste/sanitizeHtmlColorsFromPastedContent/sanitizeHtmlColorsFromPastedContent.ts +++ b/packages/roosterjs-editor-plugins/lib/plugins/Paste/sanitizeHtmlColorsFromPastedContent/sanitizeHtmlColorsFromPastedContent.ts @@ -1,6 +1,6 @@ import { chainSanitizerCallback } from 'roosterjs-editor-dom'; import { DeprecatedColorList } from './deprecatedColorList'; -import { HtmlSanitizerOptions } from 'roosterjs-editor-types'; +import type { HtmlSanitizerOptions } from 'roosterjs-editor-types'; /** * @internal diff --git a/packages/roosterjs-editor-plugins/lib/plugins/Paste/sanitizeLinks/sanitizeLinks.ts b/packages/roosterjs-editor-plugins/lib/plugins/Paste/sanitizeLinks/sanitizeLinks.ts index 0d63b7d7f2d..f54bc7da9b5 100644 --- a/packages/roosterjs-editor-plugins/lib/plugins/Paste/sanitizeLinks/sanitizeLinks.ts +++ b/packages/roosterjs-editor-plugins/lib/plugins/Paste/sanitizeLinks/sanitizeLinks.ts @@ -1,5 +1,5 @@ import { chainSanitizerCallback } from 'roosterjs-editor-dom'; -import { HtmlSanitizerOptions } from 'roosterjs-editor-types'; +import type { HtmlSanitizerOptions } from 'roosterjs-editor-types'; const SUPPORTED_PROTOCOLS = ['http:', 'https:', 'notes:', 'mailto:', 'onenote:']; diff --git a/packages/roosterjs-editor-plugins/lib/plugins/Paste/wordConverter/LevelLists.ts b/packages/roosterjs-editor-plugins/lib/plugins/Paste/wordConverter/LevelLists.ts index bb0267b94a8..5541ea801e0 100644 --- a/packages/roosterjs-editor-plugins/lib/plugins/Paste/wordConverter/LevelLists.ts +++ b/packages/roosterjs-editor-plugins/lib/plugins/Paste/wordConverter/LevelLists.ts @@ -1,4 +1,4 @@ -import ListMetadata from './ListMetadata'; +import type ListMetadata from './ListMetadata'; /** * @internal diff --git a/packages/roosterjs-editor-plugins/lib/plugins/Paste/wordConverter/WordConverterArguments.ts b/packages/roosterjs-editor-plugins/lib/plugins/Paste/wordConverter/WordConverterArguments.ts index 15f7db40f87..a0924643a00 100644 --- a/packages/roosterjs-editor-plugins/lib/plugins/Paste/wordConverter/WordConverterArguments.ts +++ b/packages/roosterjs-editor-plugins/lib/plugins/Paste/wordConverter/WordConverterArguments.ts @@ -1,6 +1,7 @@ -import LevelLists, { createLevelLists } from './LevelLists'; -import ListItemMetadata from './ListItemMetadata'; -import ListMetadata from './ListMetadata'; +import { createLevelLists } from './LevelLists'; +import type LevelLists from './LevelLists'; +import type ListItemMetadata from './ListItemMetadata'; +import type ListMetadata from './ListMetadata'; /** * @internal diff --git a/packages/roosterjs-editor-plugins/lib/plugins/Paste/wordConverter/commentsRemoval.ts b/packages/roosterjs-editor-plugins/lib/plugins/Paste/wordConverter/commentsRemoval.ts index 48ca2627439..c8a5b0da336 100644 --- a/packages/roosterjs-editor-plugins/lib/plugins/Paste/wordConverter/commentsRemoval.ts +++ b/packages/roosterjs-editor-plugins/lib/plugins/Paste/wordConverter/commentsRemoval.ts @@ -1,4 +1,4 @@ -import { CssStyleCallbackMap, ElementCallbackMap } from 'roosterjs-editor-types'; +import type { CssStyleCallbackMap, ElementCallbackMap } from 'roosterjs-editor-types'; import { chainSanitizerCallback, getStyles, diff --git a/packages/roosterjs-editor-plugins/lib/plugins/Paste/wordConverter/convertPastedContentFromWord.ts b/packages/roosterjs-editor-plugins/lib/plugins/Paste/wordConverter/convertPastedContentFromWord.ts index f3b897faf07..03502096a03 100644 --- a/packages/roosterjs-editor-plugins/lib/plugins/Paste/wordConverter/convertPastedContentFromWord.ts +++ b/packages/roosterjs-editor-plugins/lib/plugins/Paste/wordConverter/convertPastedContentFromWord.ts @@ -1,9 +1,9 @@ import commentsRemoval from './commentsRemoval'; -import { BeforePasteEvent } from 'roosterjs-editor-types'; import { chainSanitizerCallback, moveChildNodes } from 'roosterjs-editor-dom'; import { createWordConverter } from './wordConverter'; import { createWordConverterArguments } from './WordConverterArguments'; import { processNodeConvert, processNodesDiscovery } from './converterUtils'; +import type { BeforePasteEvent } from 'roosterjs-editor-types'; const PERCENTAGE_REGEX = /%/; const DEFAULT_BROWSER_LINE_HEIGHT_PERCENTAGE = 120; diff --git a/packages/roosterjs-editor-plugins/lib/plugins/Paste/wordConverter/converterUtils.ts b/packages/roosterjs-editor-plugins/lib/plugins/Paste/wordConverter/converterUtils.ts index 45ba6a7ab65..19b118158c8 100644 --- a/packages/roosterjs-editor-plugins/lib/plugins/Paste/wordConverter/converterUtils.ts +++ b/packages/roosterjs-editor-plugins/lib/plugins/Paste/wordConverter/converterUtils.ts @@ -1,11 +1,11 @@ -import ListItemMetadata from './ListItemMetadata'; -import ListMetadata from './ListMetadata'; -import WordConverter from './wordConverter'; -import WordConverterArguments from './WordConverterArguments'; import { createLevelLists } from './LevelLists'; import { getObject, setObject } from './WordCustomData'; import { getStyles, getTagOfNode, moveChildNodes } from 'roosterjs-editor-dom'; import { NodeType } from 'roosterjs-editor-types'; +import type ListItemMetadata from './ListItemMetadata'; +import type ListMetadata from './ListMetadata'; +import type WordConverter from './wordConverter'; +import type WordConverterArguments from './WordConverterArguments'; /** Word list metadata style name */ const LOOKUP_DEPTH = 5; diff --git a/packages/roosterjs-editor-plugins/lib/plugins/Paste/wordConverter/wordConverter.ts b/packages/roosterjs-editor-plugins/lib/plugins/Paste/wordConverter/wordConverter.ts index 457aaa8e174..704695d8a05 100644 --- a/packages/roosterjs-editor-plugins/lib/plugins/Paste/wordConverter/wordConverter.ts +++ b/packages/roosterjs-editor-plugins/lib/plugins/Paste/wordConverter/wordConverter.ts @@ -1,5 +1,6 @@ -import WordConverterArguments from './WordConverterArguments'; -import WordCustomData, { createCustomData } from './WordCustomData'; +import { createCustomData } from './WordCustomData'; +import type WordConverterArguments from './WordConverterArguments'; +import type WordCustomData from './WordCustomData'; /** * @internal diff --git a/packages/roosterjs-editor-plugins/lib/plugins/Picker/PickerPlugin.ts b/packages/roosterjs-editor-plugins/lib/plugins/Picker/PickerPlugin.ts index b54ee6452c6..dcc27301edc 100644 --- a/packages/roosterjs-editor-plugins/lib/plugins/Picker/PickerPlugin.ts +++ b/packages/roosterjs-editor-plugins/lib/plugins/Picker/PickerPlugin.ts @@ -1,3 +1,4 @@ +import { ChangeSource, PluginEventType, PositionType } from 'roosterjs-editor-types'; import { replaceWithNode } from 'roosterjs-editor-api'; import { Browser, @@ -7,8 +8,7 @@ import { PartialInlineElement, safeInstanceOf, } from 'roosterjs-editor-dom'; -import { - ChangeSource, +import type { EditorPlugin, IEditor, NodePosition, @@ -16,10 +16,8 @@ import { PickerPluginOptions, PluginDomEvent, PluginEvent, - PluginEventType, PluginInputEvent, PluginKeyboardEvent, - PositionType, } from 'roosterjs-editor-types'; // Character codes. @@ -467,9 +465,11 @@ export default class PickerPlugin { + let mockEditor: IEditor = { + getDocument: () => document, + runAsync: (cb: (mockEditor: IEditor) => void) => cb(mockEditor), + isDisposed: () => false, + } as any; + + let getElementAtCursorSpy: jasmine.Spy; + + beforeEach(() => { + mockEditor = { + getDocument: () => document, + runAsync: (cb: (mockEditor: IEditor) => void) => cb(mockEditor), + isDisposed: () => false, + } as any; + getElementAtCursorSpy = jasmine.createSpy('getElementAtCursorSpy'); + mockEditor.getElementAtCursor = () => { + getElementAtCursorSpy(); + return null; + }; + }); + it('initialize', () => { + const plugin = new AnnouncePlugin(); + plugin.initialize(mockEditor); + + expect((plugin as any).editor).toEqual(mockEditor); + }); + + it('onPluginEvent & dispose', () => { + const mockStrings = 'MockStrings' as any; + + const plugin = new AnnouncePlugin(mockStrings); + const mockAnnounceData = { + text: 'Announcement text', + defaultStrings: undefined, + formatStrings: [], + } as any; + + plugin.initialize(mockEditor); + plugin.onPluginEvent({ + eventType: PluginEventType.ContentChanged, + source: 'Test', + additionalData: { + getAnnounceData: () => mockAnnounceData, + }, + }); + + expect(plugin.getAriaLiveElement()).toBeDefined(); + expect(plugin.getAriaLiveElement()?.textContent).toEqual(mockAnnounceData.text); + plugin.dispose(); + expect(plugin.getAriaLiveElement()).toBeUndefined(); + }); + + it('onPluginEvent & replace {0}, {1}! with [Hello, World] => Hello World', () => { + const mockStrings = 'MockStrings' as any; + + const plugin = new AnnouncePlugin(mockStrings); + const announceData = { + text: '{0}, {1}!', + defaultStrings: undefined, + formatStrings: ['Hello', 'World'], + } as any; + + plugin.initialize(mockEditor); + plugin.onPluginEvent({ + eventType: PluginEventType.ContentChanged, + source: 'Test', + additionalData: { + getAnnounceData: () => announceData, + }, + }); + + expect(plugin.getAriaLiveElement()).toBeDefined(); + expect(plugin.getAriaLiveElement()?.textContent).toEqual('Hello, World!'); + plugin.dispose(); + expect(plugin.getAriaLiveElement()).toBeUndefined(); + }); + + it('onPluginEvent & dispose, getAnnounceData returns undefined', () => { + const mockStrings = 'MockStrings' as any; + + const plugin = new AnnouncePlugin(mockStrings); + + plugin.initialize(mockEditor); + plugin.onPluginEvent({ + eventType: PluginEventType.ContentChanged, + source: 'Test', + additionalData: { + getAnnounceData: () => { + return undefined; + }, + }, + }); + + expect(plugin.getAriaLiveElement()).toBeUndefined(); + plugin.dispose(); + expect(plugin.getAriaLiveElement()).toBeUndefined(); + }); + + it('onPluginEvent & dispose, getAnnounceData is undefined', () => { + const mockStrings = 'MockStrings' as any; + + const plugin = new AnnouncePlugin(mockStrings); + + plugin.initialize(mockEditor); + plugin.onPluginEvent({ + eventType: PluginEventType.ContentChanged, + source: 'Test', + }); + + expect(plugin.getAriaLiveElement()).toBeUndefined(); + plugin.dispose(); + expect(plugin.getAriaLiveElement()).toBeUndefined(); + }); + + it('onPluginEvent & dispose, handle feature', () => { + const mockStrings = 'MockStrings' as any; + const spyTest = jasmine.createSpy('spyTest'); + const plugin = new AnnouncePlugin( + mockStrings, + ['announceNewListItem', 'announceWarningOnLastTableCell'], + [ + { + keys: [Keys.ENTER], + shouldHandle: () => { + spyTest(); + return { + text: 'mockedText', + }; + }, + }, + ] + ); + + plugin.initialize(mockEditor); + plugin.onPluginEvent({ + eventType: PluginEventType.KeyDown, + rawEvent: { + which: Keys.ENTER, + } as any, + }); + + expect(plugin.getAriaLiveElement()).toBeDefined(); + expect(plugin.getAriaLiveElement()?.textContent).toEqual('mockedText'); + expect(spyTest).toHaveBeenCalled(); + expect(getElementAtCursorSpy).toHaveBeenCalled(); + + plugin.dispose(); + + expect(plugin.getAriaLiveElement()).toBeUndefined(); + }); + + it('onPluginEvent & dispose, do not handle feature, event key not in features.', () => { + const mockStrings = 'MockStrings' as any; + const spyTest = jasmine.createSpy('spyTest'); + const plugin = new AnnouncePlugin( + mockStrings, + ['announceNewListItem', 'announceWarningOnLastTableCell'], + [ + { + keys: [Keys.B], + shouldHandle: () => { + spyTest(); + return { + text: 'mockedText', + }; + }, + }, + ] + ); + + plugin.initialize(mockEditor); + plugin.onPluginEvent({ + eventType: PluginEventType.KeyDown, + rawEvent: { + which: Keys.ENTER, + } as any, + }); + + expect(plugin.getAriaLiveElement()).toBeUndefined(); + expect(spyTest).not.toHaveBeenCalled(); + expect(getElementAtCursorSpy).toHaveBeenCalled(); + + plugin.dispose(); + + expect(plugin.getAriaLiveElement()).toBeUndefined(); + }); +}); diff --git a/packages/roosterjs-editor-plugins/test/Announce/features/announceNewListItemTest.ts b/packages/roosterjs-editor-plugins/test/Announce/features/announceNewListItemTest.ts new file mode 100644 index 00000000000..7625d41c8af --- /dev/null +++ b/packages/roosterjs-editor-plugins/test/Announce/features/announceNewListItemTest.ts @@ -0,0 +1,109 @@ +import announceNewListItem from '../../../lib/plugins/Announce/features/announceNewListItem'; +import { createElement } from 'roosterjs-editor-dom'; +import { + IEditor, + KnownAnnounceStrings, + PluginEventType, + PluginKeyboardEvent, +} from 'roosterjs-editor-types'; + +describe('announceNewListItem', () => { + const LIST_ITEM_SELECTOR = 'LI'; + + const mockEditor: IEditor = { + getElementAtCursor: (selector: string) => { + if (selector == LIST_ITEM_SELECTOR) { + return el?.firstChild; + } else { + return el; + } + }, + } as any; + let el: Element | null; + const eventmock: PluginKeyboardEvent = { + eventType: PluginEventType.KeyDown, + rawEvent: {} as any, + eventDataCache: {}, + }; + + beforeEach(() => { + eventmock.eventDataCache = {}; + + el = createElement( + { + tag: 'UL', + children: [ + { + tag: 'LI', + children: ['asd'], + }, + ], + }, + document + ) as any; + + el && document.body.appendChild(el); + }); + + afterEach(() => { + el?.parentElement?.removeChild(el); + }); + + it('announceNewListItem.shouldHandle | OL with start attribute', () => { + el = createElement( + { + tag: 'OL', + attributes: { + start: '5', + }, + children: [ + { + tag: 'LI', + children: ['asd'], + }, + ], + }, + document + ) as any; + + const result = announceNewListItem.shouldHandle(mockEditor, null); + + expect(result).toEqual({ + defaultStrings: KnownAnnounceStrings.AnnounceListItemNumbering, + formatStrings: ['5'], + }); + }); + + it('announceNewListItem.shouldHandle | UL', () => { + const result = announceNewListItem.shouldHandle(mockEditor, null); + + expect(result).toEqual({ defaultStrings: 2 }); + }); + + it('announceNewListItem.shouldHandle | OL', () => { + el = createElement( + { + tag: 'OL', + children: [ + { + tag: 'LI', + children: ['asd'], + }, + ], + }, + document + ) as any; + + const result = announceNewListItem.shouldHandle(mockEditor, null); + + expect(result).toEqual({ defaultStrings: 1, formatStrings: ['1'] }); + }); + + it('announceNewListItem.shouldHandle | Null', () => { + el = null as any; + + const result = announceNewListItem.shouldHandle(mockEditor, null); + + expect(result).toEqual(false); + }); +}); diff --git a/packages/roosterjs-editor-plugins/test/Announce/features/announceWarningOnLastTableCellTest.ts b/packages/roosterjs-editor-plugins/test/Announce/features/announceWarningOnLastTableCellTest.ts new file mode 100644 index 00000000000..421bf395b87 --- /dev/null +++ b/packages/roosterjs-editor-plugins/test/Announce/features/announceWarningOnLastTableCellTest.ts @@ -0,0 +1,128 @@ +import announceWarningOnLastTableCell from '../../../lib/plugins/Announce/features/announceWarningOnLastTableCell'; +import { createElement } from 'roosterjs-editor-dom'; +import { + IEditor, + PluginEventType, + PluginKeyboardEvent, + SelectionRangeEx, + SelectionRangeTypes, +} from 'roosterjs-editor-types'; + +describe('announceWarningOnLastTableCell', () => { + const TABLE_SELECTOR = 'table'; + const mockEditor: IEditor = { + getElementAtCursor: (selector: string) => { + if (selector == TABLE_SELECTOR) { + return el; + } else { + return el?.querySelectorAll('td').item(getLastFocused ? 1 : 0); + } + }, + getSelectionRangeEx: () => selectionRangeExMock, + } as any; + let el: Element | null; + let getLastFocused: boolean = true; + let selectionRangeExMock: SelectionRangeEx; + const eventmock: PluginKeyboardEvent = { + eventType: PluginEventType.KeyDown, + rawEvent: {} as any, + eventDataCache: {}, + }; + + beforeEach(() => { + eventmock.eventDataCache = {}; + + el = createElement( + { + tag: 'Table', + children: [ + { + tag: 'TR', + children: [ + { + tag: 'TD', + children: ['test'], + }, + { + tag: 'TD', + children: ['test'], + }, + ], + }, + ], + }, + document + ) as any; + + el && document.body.appendChild(el); + }); + + afterEach(() => { + el?.parentElement?.removeChild(el); + }); + + it('announceWarningOnLastTableCell.shouldHandle | True', () => { + getLastFocused = true; + selectionRangeExMock = { + areAllCollapsed: true, + ranges: ['asd'] as any, + type: SelectionRangeTypes.Normal, + }; + + const result = announceWarningOnLastTableCell.shouldHandle(mockEditor, null); + + expect(result).toEqual({ defaultStrings: 3 }); + }); + + it('announceWarningOnLastTableCell.shouldHandle | Not last cell', () => { + getLastFocused = false; + selectionRangeExMock = { + areAllCollapsed: true, + ranges: ['asd'] as any, + type: SelectionRangeTypes.Normal, + }; + + const result = announceWarningOnLastTableCell.shouldHandle(mockEditor, null); + + expect(result).toEqual(false); + }); + + it('announceWarningOnLastTableCell.shouldHandle | Not Selection range of type normal', () => { + getLastFocused = true; + selectionRangeExMock = { + areAllCollapsed: true, + ranges: ['asd'] as any, + type: SelectionRangeTypes.TableSelection, + } as any; + + const result = announceWarningOnLastTableCell.shouldHandle(mockEditor, null); + + expect(result).toEqual(false); + }); + + it('announceWarningOnLastTableCell.shouldHandle | More than one range in ranges', () => { + getLastFocused = true; + selectionRangeExMock = { + areAllCollapsed: true, + ranges: ['asd', '2'] as any, + type: SelectionRangeTypes.Normal, + } as any; + + const result = announceWarningOnLastTableCell.shouldHandle(mockEditor, null); + + expect(result).toEqual(false); + }); + + it('announceWarningOnLastTableCell.shouldHandle | Not all collapsed', () => { + getLastFocused = true; + selectionRangeExMock = { + areAllCollapsed: false, + ranges: ['asd'] as any, + type: SelectionRangeTypes.Normal, + } as any; + + const result = announceWarningOnLastTableCell.shouldHandle(mockEditor, null); + + expect(result).toEqual(false); + }); +}); diff --git a/packages/roosterjs-editor-plugins/test/ContentEdit/features/inlineEntityFeatureTest.ts b/packages/roosterjs-editor-plugins/test/ContentEdit/features/inlineEntityFeatureTest.ts index 02ac0a89ca7..cec8afcfa65 100644 --- a/packages/roosterjs-editor-plugins/test/ContentEdit/features/inlineEntityFeatureTest.ts +++ b/packages/roosterjs-editor-plugins/test/ContentEdit/features/inlineEntityFeatureTest.ts @@ -9,14 +9,7 @@ import { Position, PositionContentSearcher, } from 'roosterjs-editor-dom'; -import { - Entity, - ExperimentalFeatures, - IEditor, - Keys, - PluginKeyDownEvent, - BlockElement, -} from 'roosterjs-editor-types'; +import { Entity, IEditor, Keys, PluginKeyDownEvent, BlockElement } from 'roosterjs-editor-types'; describe('Content Edit Features |', () => { const { moveBetweenDelimitersFeature, removeEntityBetweenDelimiters } = EntityFeatures; @@ -68,8 +61,6 @@ describe('Content Edit Features |', () => { collapsed: true, }, select, - isFeatureEnabled: (feature: ExperimentalFeatures) => - feature === ExperimentalFeatures.InlineEntityReadOnlyDelimiters, getBodyTraverser: (startNode?: Node) => ContentTraverser.createBodyTraverser(testContainer, startNode), getBlockElementAtNode: (node: Node) => getBlockElementAtNode(document.body, node), diff --git a/packages/roosterjs-editor-plugins/test/imageEdit/imageEditTest.ts b/packages/roosterjs-editor-plugins/test/imageEdit/imageEditTest.ts index 6fb333f8131..18c75250913 100644 --- a/packages/roosterjs-editor-plugins/test/imageEdit/imageEditTest.ts +++ b/packages/roosterjs-editor-plugins/test/imageEdit/imageEditTest.ts @@ -362,6 +362,20 @@ describe('ImageEdit | wrapper', () => { expect(imageShadow?.style.maxWidth).toBe(''); }); + it('image selection, remove max-height', () => { + const IMG_ID = 'IMAGE_ID_SELECTION'; + const content = ``; + editor.setContent(content); + const image = document.getElementById(IMG_ID) as HTMLImageElement; + image.style.maxHeight = '100%'; + editor.focus(); + editor.select(image); + const imageParent = image.parentElement; + const shadowRoot = imageParent?.shadowRoot; + const imageShadow = shadowRoot?.querySelector('img'); + expect(imageShadow?.style.maxHeight).toBe(''); + }); + it('image selection, cloned image should use style width/height attributes', () => { const IMG_ID = 'IMAGE_ID_SELECTION_2'; const content = ``; diff --git a/packages/roosterjs-editor-plugins/test/pluginUtils/announceData/getAnnounceDataForListTest.ts b/packages/roosterjs-editor-plugins/test/pluginUtils/announceData/getAnnounceDataForListTest.ts new file mode 100644 index 00000000000..094f41857d2 --- /dev/null +++ b/packages/roosterjs-editor-plugins/test/pluginUtils/announceData/getAnnounceDataForListTest.ts @@ -0,0 +1,55 @@ +import getAnnounceDataForList from '../../../lib/pluginUtils/announceData/getAnnounceDataForList'; +import { createElement } from 'roosterjs-editor-dom'; +import { KnownAnnounceStrings } from 'roosterjs-editor-types'; + +describe('getAnnounceDataForList', () => { + it('should return announce data for numbered list item | OL', () => { + const el = createElement( + { + tag: 'OL', + children: [ + { + tag: 'LI', + children: ['asd'], + }, + ], + }, + document + ) as any; + + el && document.body.appendChild(el); + + const announceData = getAnnounceDataForList(el, el?.firstChild); + expect(announceData).toEqual({ + defaultStrings: KnownAnnounceStrings.AnnounceListItemNumbering, + formatStrings: ['1'], + }); + }); + + it('should return announce data for numbered list item | UL', () => { + const el = createElement( + { + tag: 'UL', + children: [ + { + tag: 'LI', + children: ['asd'], + }, + ], + }, + document + ) as any; + + el && document.body.appendChild(el); + + const announceData = getAnnounceDataForList(el, el?.firstChild); + expect(announceData).toEqual({ + defaultStrings: KnownAnnounceStrings.AnnounceListItemBullet, + }); + }); + + it('should return announce data for bullet list item | undefined', () => { + const announceData = getAnnounceDataForList(null, null); + expect(announceData).toEqual(undefined); + }); +}); diff --git a/packages/roosterjs-editor-types/lib/corePluginState/DOMEventPluginState.ts b/packages/roosterjs-editor-types/lib/corePluginState/DOMEventPluginState.ts index 596b4b0ff4a..f9cd963be08 100644 --- a/packages/roosterjs-editor-types/lib/corePluginState/DOMEventPluginState.ts +++ b/packages/roosterjs-editor-types/lib/corePluginState/DOMEventPluginState.ts @@ -1,5 +1,5 @@ -import ContextMenuProvider from '../interface/ContextMenuProvider'; -import { ImageSelectionRange, TableSelectionRange } from '../interface/SelectionRangeEx'; +import type ContextMenuProvider from '../interface/ContextMenuProvider'; +import type { ImageSelectionRange, TableSelectionRange } from '../interface/SelectionRangeEx'; /** * The state object for DOMEventPlugin diff --git a/packages/roosterjs-editor-types/lib/corePluginState/EditPluginState.ts b/packages/roosterjs-editor-types/lib/corePluginState/EditPluginState.ts index 6a32e941cc6..e3d42601646 100644 --- a/packages/roosterjs-editor-types/lib/corePluginState/EditPluginState.ts +++ b/packages/roosterjs-editor-types/lib/corePluginState/EditPluginState.ts @@ -1,5 +1,5 @@ -import { GenericContentEditFeature } from '../interface/ContentEditFeature'; -import { PluginEvent } from '../event/PluginEvent'; +import type { GenericContentEditFeature } from '../interface/ContentEditFeature'; +import type { PluginEvent } from '../event/PluginEvent'; /** * The state object for EditPlugin diff --git a/packages/roosterjs-editor-types/lib/corePluginState/EntityPluginState.ts b/packages/roosterjs-editor-types/lib/corePluginState/EntityPluginState.ts index 97de02de3a8..655f2febbc8 100644 --- a/packages/roosterjs-editor-types/lib/corePluginState/EntityPluginState.ts +++ b/packages/roosterjs-editor-types/lib/corePluginState/EntityPluginState.ts @@ -1,4 +1,4 @@ -import { KnownEntityItem } from '../interface/KnownEntityItem'; +import type { KnownEntityItem } from '../interface/KnownEntityItem'; /** * The state object for EntityPlugin diff --git a/packages/roosterjs-editor-types/lib/corePluginState/LifecyclePluginState.ts b/packages/roosterjs-editor-types/lib/corePluginState/LifecyclePluginState.ts index 0f60fd7e0da..fd2fa3f1028 100644 --- a/packages/roosterjs-editor-types/lib/corePluginState/LifecyclePluginState.ts +++ b/packages/roosterjs-editor-types/lib/corePluginState/LifecyclePluginState.ts @@ -1,8 +1,8 @@ -import CustomData from '../interface/CustomData'; -import DarkColorHandler from '../interface/DarkColorHandler'; -import DefaultFormat from '../interface/DefaultFormat'; -import SelectionPath from '../interface/SelectionPath'; -import { ExperimentalFeatures } from '../enum/ExperimentalFeatures'; +import type CustomData from '../interface/CustomData'; +import type DarkColorHandler from '../interface/DarkColorHandler'; +import type DefaultFormat from '../interface/DefaultFormat'; +import type SelectionPath from '../interface/SelectionPath'; +import type { ExperimentalFeatures } from '../enum/ExperimentalFeatures'; import type { CompatibleExperimentalFeatures } from '../compatibleEnum/ExperimentalFeatures'; /** diff --git a/packages/roosterjs-editor-types/lib/corePluginState/PendingFormatStatePluginState.ts b/packages/roosterjs-editor-types/lib/corePluginState/PendingFormatStatePluginState.ts index fb227f56134..6ece232cc24 100644 --- a/packages/roosterjs-editor-types/lib/corePluginState/PendingFormatStatePluginState.ts +++ b/packages/roosterjs-editor-types/lib/corePluginState/PendingFormatStatePluginState.ts @@ -1,5 +1,5 @@ -import NodePosition from '../interface/NodePosition'; -import { PendableFormatState } from '../interface/FormatState'; +import type NodePosition from '../interface/NodePosition'; +import type { PendableFormatState } from '../interface/FormatState'; /** * The state object for PendingFormatStatePlugin diff --git a/packages/roosterjs-editor-types/lib/corePluginState/UndoPluginState.ts b/packages/roosterjs-editor-types/lib/corePluginState/UndoPluginState.ts index 8c556059ea5..22f1a1579bf 100644 --- a/packages/roosterjs-editor-types/lib/corePluginState/UndoPluginState.ts +++ b/packages/roosterjs-editor-types/lib/corePluginState/UndoPluginState.ts @@ -1,6 +1,6 @@ -import NodePosition from '../interface/NodePosition'; -import Snapshot from '../interface/Snapshot'; -import UndoSnapshotsService from '../interface/UndoSnapshotsService'; +import type NodePosition from '../interface/NodePosition'; +import type Snapshot from '../interface/Snapshot'; +import type UndoSnapshotsService from '../interface/UndoSnapshotsService'; /** * The state object for UndoPlugin diff --git a/packages/roosterjs-editor-types/lib/enum/ExperimentalFeatures.ts b/packages/roosterjs-editor-types/lib/enum/ExperimentalFeatures.ts index c1efaa5fec3..05208beed1e 100644 --- a/packages/roosterjs-editor-types/lib/enum/ExperimentalFeatures.ts +++ b/packages/roosterjs-editor-types/lib/enum/ExperimentalFeatures.ts @@ -142,6 +142,18 @@ export const enum ExperimentalFeatures { */ AutoFormatList = 'AutoFormatList', + /** + * @deprecated This feature is always enabled + * Add entities around a Read Only Inline entity to prevent cursor to be hidden when cursor is next of it. + */ + InlineEntityReadOnlyDelimiters = 'InlineEntityReadOnlyDelimiters', + + /** + * @deprecated This feature is always enabled + * Paste with Content model + */ + ContentModelPaste = 'ContentModelPaste', + //#endregion /** @@ -162,16 +174,6 @@ export const enum ExperimentalFeatures { */ DeleteTableWithBackspace = 'DeleteTableWithBackspace', - /** - * Add entities around a Read Only Inline entity to prevent cursor to be hidden when cursor is next of it. - */ - InlineEntityReadOnlyDelimiters = 'InlineEntityReadOnlyDelimiters', - - /** - * Paste with Content model - */ - ContentModelPaste = 'ContentModelPaste', - /** * Disable list chain functionality */ diff --git a/packages/roosterjs-editor-types/lib/enum/KnownAnnounceStrings.ts b/packages/roosterjs-editor-types/lib/enum/KnownAnnounceStrings.ts new file mode 100644 index 00000000000..90a45c27c80 --- /dev/null +++ b/packages/roosterjs-editor-types/lib/enum/KnownAnnounceStrings.ts @@ -0,0 +1,22 @@ +/** + * Known announce strings + */ +export const enum KnownAnnounceStrings { + /** + * String announced for a list item in a OL List + * @example + * Auto corrected, {0} + * Where &lcub0&rcub is the new list item bullet + */ + AnnounceListItemNumbering = 1, + /** + * String announced for a list item in a UL List + * @example + * Auto corrected bullet + */ + AnnounceListItemBullet, + /** + * String announced when cursor is moved to the last cell in a table + */ + AnnounceOnFocusLastCell, +} diff --git a/packages/roosterjs-editor-types/lib/enum/index.ts b/packages/roosterjs-editor-types/lib/enum/index.ts index 0db87f996bd..1aa95cdd2f4 100644 --- a/packages/roosterjs-editor-types/lib/enum/index.ts +++ b/packages/roosterjs-editor-types/lib/enum/index.ts @@ -1,3 +1,4 @@ +export { KnownAnnounceStrings } from './KnownAnnounceStrings'; export { DocumentCommand } from './DocumentCommand'; export { DocumentPosition } from './DocumentPosition'; export { Keys } from './Keys'; diff --git a/packages/roosterjs-editor-types/lib/event/BasePluginEvent.ts b/packages/roosterjs-editor-types/lib/event/BasePluginEvent.ts index b27bcd33b24..84647b03a03 100644 --- a/packages/roosterjs-editor-types/lib/event/BasePluginEvent.ts +++ b/packages/roosterjs-editor-types/lib/event/BasePluginEvent.ts @@ -1,4 +1,4 @@ -import { PluginEventType } from '../enum/PluginEventType'; +import type { PluginEventType } from '../enum/PluginEventType'; import type { CompatiblePluginEventType } from '../compatibleEnum/PluginEventType'; /** diff --git a/packages/roosterjs-editor-types/lib/event/BeforeCutCopyEvent.ts b/packages/roosterjs-editor-types/lib/event/BeforeCutCopyEvent.ts index 443044c0f55..e298140be5c 100644 --- a/packages/roosterjs-editor-types/lib/event/BeforeCutCopyEvent.ts +++ b/packages/roosterjs-editor-types/lib/event/BeforeCutCopyEvent.ts @@ -1,5 +1,5 @@ -import BasePluginEvent from './BasePluginEvent'; -import { PluginEventType } from '../enum/PluginEventType'; +import type BasePluginEvent from './BasePluginEvent'; +import type { PluginEventType } from '../enum/PluginEventType'; import type { CompatiblePluginEventType } from '../compatibleEnum/PluginEventType'; /** diff --git a/packages/roosterjs-editor-types/lib/event/BeforeDisposeEvent.ts b/packages/roosterjs-editor-types/lib/event/BeforeDisposeEvent.ts index f79ac1882b9..6f22dbe4a34 100644 --- a/packages/roosterjs-editor-types/lib/event/BeforeDisposeEvent.ts +++ b/packages/roosterjs-editor-types/lib/event/BeforeDisposeEvent.ts @@ -1,5 +1,5 @@ -import BasePluginEvent from './BasePluginEvent'; -import { PluginEventType } from '../enum/PluginEventType'; +import type BasePluginEvent from './BasePluginEvent'; +import type { PluginEventType } from '../enum/PluginEventType'; import type { CompatiblePluginEventType } from '../compatibleEnum/PluginEventType'; /** diff --git a/packages/roosterjs-editor-types/lib/event/BeforeKeyboardEditingEvent.ts b/packages/roosterjs-editor-types/lib/event/BeforeKeyboardEditingEvent.ts index dae86f5949f..4ae666bf633 100644 --- a/packages/roosterjs-editor-types/lib/event/BeforeKeyboardEditingEvent.ts +++ b/packages/roosterjs-editor-types/lib/event/BeforeKeyboardEditingEvent.ts @@ -1,5 +1,5 @@ -import BasePluginEvent from './BasePluginEvent'; -import { PluginEventType } from '../enum/PluginEventType'; +import type BasePluginEvent from './BasePluginEvent'; +import type { PluginEventType } from '../enum/PluginEventType'; import type { CompatiblePluginEventType } from '../compatibleEnum/PluginEventType'; /** diff --git a/packages/roosterjs-editor-types/lib/event/BeforePasteEvent.ts b/packages/roosterjs-editor-types/lib/event/BeforePasteEvent.ts index 18a512d505c..1e317aacc0a 100644 --- a/packages/roosterjs-editor-types/lib/event/BeforePasteEvent.ts +++ b/packages/roosterjs-editor-types/lib/event/BeforePasteEvent.ts @@ -1,7 +1,7 @@ -import BasePluginEvent from './BasePluginEvent'; -import ClipboardData from '../interface/ClipboardData'; -import HtmlSanitizerOptions from '../interface/HtmlSanitizerOptions'; -import { PluginEventType } from '../enum/PluginEventType'; +import type BasePluginEvent from './BasePluginEvent'; +import type ClipboardData from '../interface/ClipboardData'; +import type HtmlSanitizerOptions from '../interface/HtmlSanitizerOptions'; +import type { PluginEventType } from '../enum/PluginEventType'; import type { CompatiblePasteType } from '../compatibleEnum/PasteType'; import type { PasteType } from '../enum/PasteType'; import type { CompatiblePluginEventType } from '../compatibleEnum/PluginEventType'; diff --git a/packages/roosterjs-editor-types/lib/event/BeforeSetContentEvent.ts b/packages/roosterjs-editor-types/lib/event/BeforeSetContentEvent.ts index b00b672dc77..13bcddd7196 100644 --- a/packages/roosterjs-editor-types/lib/event/BeforeSetContentEvent.ts +++ b/packages/roosterjs-editor-types/lib/event/BeforeSetContentEvent.ts @@ -1,5 +1,5 @@ -import BasePluginEvent from './BasePluginEvent'; -import { PluginEventType } from '../enum/PluginEventType'; +import type BasePluginEvent from './BasePluginEvent'; +import type { PluginEventType } from '../enum/PluginEventType'; import type { CompatiblePluginEventType } from '../compatibleEnum/PluginEventType'; /** diff --git a/packages/roosterjs-editor-types/lib/event/ContentChangedEvent.ts b/packages/roosterjs-editor-types/lib/event/ContentChangedEvent.ts index 1bec1bae161..6fc424ddccc 100644 --- a/packages/roosterjs-editor-types/lib/event/ContentChangedEvent.ts +++ b/packages/roosterjs-editor-types/lib/event/ContentChangedEvent.ts @@ -1,7 +1,7 @@ -import BasePluginEvent from './BasePluginEvent'; -import ContentChangedData from '../interface/ContentChangedData'; -import { ChangeSource } from '../enum/ChangeSource'; -import { PluginEventType } from '../enum/PluginEventType'; +import type BasePluginEvent from './BasePluginEvent'; +import type ContentChangedData from '../interface/ContentChangedData'; +import type { ChangeSource } from '../enum/ChangeSource'; +import type { PluginEventType } from '../enum/PluginEventType'; import type { CompatibleChangeSource } from '../compatibleEnum/ChangeSource'; import type { CompatiblePluginEventType } from '../compatibleEnum/PluginEventType'; diff --git a/packages/roosterjs-editor-types/lib/event/EditImageEvent.ts b/packages/roosterjs-editor-types/lib/event/EditImageEvent.ts index 1f92840dfec..24f0f402361 100644 --- a/packages/roosterjs-editor-types/lib/event/EditImageEvent.ts +++ b/packages/roosterjs-editor-types/lib/event/EditImageEvent.ts @@ -1,5 +1,5 @@ -import BasePluginEvent from './BasePluginEvent'; -import { PluginEventType } from '../enum/PluginEventType'; +import type BasePluginEvent from './BasePluginEvent'; +import type { PluginEventType } from '../enum/PluginEventType'; import type { CompatiblePluginEventType } from '../compatibleEnum/PluginEventType'; /** diff --git a/packages/roosterjs-editor-types/lib/event/EditorReadyEvent.ts b/packages/roosterjs-editor-types/lib/event/EditorReadyEvent.ts index 96e3cbb9116..dbf36d42137 100644 --- a/packages/roosterjs-editor-types/lib/event/EditorReadyEvent.ts +++ b/packages/roosterjs-editor-types/lib/event/EditorReadyEvent.ts @@ -1,5 +1,5 @@ -import BasePluginEvent from './BasePluginEvent'; -import { PluginEventType } from '../enum/PluginEventType'; +import type BasePluginEvent from './BasePluginEvent'; +import type { PluginEventType } from '../enum/PluginEventType'; import type { CompatiblePluginEventType } from '../compatibleEnum/PluginEventType'; /** diff --git a/packages/roosterjs-editor-types/lib/event/EntityOperationEvent.ts b/packages/roosterjs-editor-types/lib/event/EntityOperationEvent.ts index f65e61ed398..c489afb30d3 100644 --- a/packages/roosterjs-editor-types/lib/event/EntityOperationEvent.ts +++ b/packages/roosterjs-editor-types/lib/event/EntityOperationEvent.ts @@ -1,7 +1,7 @@ -import BasePluginEvent from './BasePluginEvent'; -import Entity from '../interface/Entity'; -import { EntityOperation } from '../enum/EntityOperation'; -import { PluginEventType } from '../enum/PluginEventType'; +import type BasePluginEvent from './BasePluginEvent'; +import type Entity from '../interface/Entity'; +import type { EntityOperation } from '../enum/EntityOperation'; +import type { PluginEventType } from '../enum/PluginEventType'; import type { CompatibleEntityOperation } from '../compatibleEnum/EntityOperation'; import type { CompatiblePluginEventType } from '../compatibleEnum/PluginEventType'; diff --git a/packages/roosterjs-editor-types/lib/event/ExtractContentWithDomEvent.ts b/packages/roosterjs-editor-types/lib/event/ExtractContentWithDomEvent.ts index 7f3110f55a4..0a6be92d0ac 100644 --- a/packages/roosterjs-editor-types/lib/event/ExtractContentWithDomEvent.ts +++ b/packages/roosterjs-editor-types/lib/event/ExtractContentWithDomEvent.ts @@ -1,5 +1,5 @@ -import BasePluginEvent from './BasePluginEvent'; -import { PluginEventType } from '../enum/PluginEventType'; +import type BasePluginEvent from './BasePluginEvent'; +import type { PluginEventType } from '../enum/PluginEventType'; import type { CompatiblePluginEventType } from '../compatibleEnum/PluginEventType'; /** diff --git a/packages/roosterjs-editor-types/lib/event/PendingFormatStateChangedEvent.ts b/packages/roosterjs-editor-types/lib/event/PendingFormatStateChangedEvent.ts index e73d1b598c0..5a4a10a1770 100644 --- a/packages/roosterjs-editor-types/lib/event/PendingFormatStateChangedEvent.ts +++ b/packages/roosterjs-editor-types/lib/event/PendingFormatStateChangedEvent.ts @@ -1,6 +1,6 @@ -import BasePluginEvent from './BasePluginEvent'; -import { PendableFormatState } from '../interface/FormatState'; -import { PluginEventType } from '../enum/PluginEventType'; +import type BasePluginEvent from './BasePluginEvent'; +import type { PendableFormatState } from '../interface/FormatState'; +import type { PluginEventType } from '../enum/PluginEventType'; import type { CompatiblePluginEventType } from '../compatibleEnum/PluginEventType'; /** diff --git a/packages/roosterjs-editor-types/lib/event/PluginDomEvent.ts b/packages/roosterjs-editor-types/lib/event/PluginDomEvent.ts index b440d08ba56..62ef6d48461 100644 --- a/packages/roosterjs-editor-types/lib/event/PluginDomEvent.ts +++ b/packages/roosterjs-editor-types/lib/event/PluginDomEvent.ts @@ -1,5 +1,5 @@ -import BasePluginEvent from './BasePluginEvent'; -import { PluginEventType } from '../enum/PluginEventType'; +import type BasePluginEvent from './BasePluginEvent'; +import type { PluginEventType } from '../enum/PluginEventType'; import type { CompatiblePluginEventType } from '../compatibleEnum/PluginEventType'; /** diff --git a/packages/roosterjs-editor-types/lib/event/PluginEvent.ts b/packages/roosterjs-editor-types/lib/event/PluginEvent.ts index 79265527701..1e6c23f884d 100644 --- a/packages/roosterjs-editor-types/lib/event/PluginEvent.ts +++ b/packages/roosterjs-editor-types/lib/event/PluginEvent.ts @@ -1,29 +1,36 @@ -import BeforeCutCopyEvent, { CompatibleBeforeCutCopyEvent } from './BeforeCutCopyEvent'; -import BeforeDisposeEvent, { CompatibleBeforeDisposeEvent } from './BeforeDisposeEvent'; -import BeforeKeyboardEditingEvent, { - CompatibleBeforeKeyboardEditingEvent, -} from './BeforeKeyboardEditingEvent'; -import BeforePasteEvent, { CompatibleBeforePasteEvent } from './BeforePasteEvent'; -import BeforeSetContentEvent, { CompatibleBeforeSetContentEvent } from './BeforeSetContentEvent'; -import ContentChangedEvent, { CompatibleContentChangedEvent } from './ContentChangedEvent'; -import EditImageEvent, { CompatibleEditImageEvent } from './EditImageEvent'; -import EditorReadyEvent, { CompatibleEditorReadyEvent } from './EditorReadyEvent'; -import EntityOperationEvent, { CompatibleEntityOperationEvent } from './EntityOperationEvent'; -import SelectionChangedEvent, { CompatibleSelectionChangedEvent } from './SelectionChangeEvent'; -import ZoomChangedEvent, { CompatibleZoomChangedEvent } from './ZoomChangedEvent'; -import { CompatiblePluginDomEvent, PluginDomEvent } from './PluginDomEvent'; -import { +import type { CompatibleBeforeCutCopyEvent } from './BeforeCutCopyEvent'; +import type BeforeCutCopyEvent from './BeforeCutCopyEvent'; +import type { CompatibleBeforeDisposeEvent } from './BeforeDisposeEvent'; +import type BeforeDisposeEvent from './BeforeDisposeEvent'; +import type { CompatibleBeforeKeyboardEditingEvent } from './BeforeKeyboardEditingEvent'; +import type BeforeKeyboardEditingEvent from './BeforeKeyboardEditingEvent'; +import type { CompatibleBeforePasteEvent } from './BeforePasteEvent'; +import type BeforePasteEvent from './BeforePasteEvent'; +import type { CompatibleBeforeSetContentEvent } from './BeforeSetContentEvent'; +import type BeforeSetContentEvent from './BeforeSetContentEvent'; +import type { CompatibleContentChangedEvent } from './ContentChangedEvent'; +import type ContentChangedEvent from './ContentChangedEvent'; +import type { CompatibleEditImageEvent } from './EditImageEvent'; +import type EditImageEvent from './EditImageEvent'; +import type { CompatibleEditorReadyEvent } from './EditorReadyEvent'; +import type EditorReadyEvent from './EditorReadyEvent'; +import type { CompatibleEntityOperationEvent } from './EntityOperationEvent'; +import type EntityOperationEvent from './EntityOperationEvent'; +import type { CompatibleSelectionChangedEvent } from './SelectionChangeEvent'; +import type SelectionChangedEvent from './SelectionChangeEvent'; +import type { CompatibleZoomChangedEvent } from './ZoomChangedEvent'; +import type ZoomChangedEvent from './ZoomChangedEvent'; +import type { CompatiblePluginDomEvent, PluginDomEvent } from './PluginDomEvent'; +import type { CompatibleEnterShadowEditEvent, CompatibleLeaveShadowEditEvent, EnterShadowEditEvent, LeaveShadowEditEvent, } from './ShadowEditEvent'; -import PendingFormatStateChangedEvent, { - CompatiblePendingFormatStateChangedEvent, -} from './PendingFormatStateChangedEvent'; -import ExtractContentWithDomEvent, { - CompatibleExtractContentWithDomEvent, -} from './ExtractContentWithDomEvent'; +import type { CompatiblePendingFormatStateChangedEvent } from './PendingFormatStateChangedEvent'; +import type PendingFormatStateChangedEvent from './PendingFormatStateChangedEvent'; +import type { CompatibleExtractContentWithDomEvent } from './ExtractContentWithDomEvent'; +import type ExtractContentWithDomEvent from './ExtractContentWithDomEvent'; /** * Editor plugin event interface diff --git a/packages/roosterjs-editor-types/lib/event/PluginEventData.ts b/packages/roosterjs-editor-types/lib/event/PluginEventData.ts index 885c5c11a1d..104b209630c 100644 --- a/packages/roosterjs-editor-types/lib/event/PluginEventData.ts +++ b/packages/roosterjs-editor-types/lib/event/PluginEventData.ts @@ -1,6 +1,6 @@ -import BasePluginEvent from './BasePluginEvent'; -import { PluginEvent } from './PluginEvent'; -import { PluginEventType } from '../enum/PluginEventType'; +import type BasePluginEvent from './BasePluginEvent'; +import type { PluginEvent } from './PluginEvent'; +import type { PluginEventType } from '../enum/PluginEventType'; import type { CompatiblePluginEventType } from '../compatibleEnum/PluginEventType'; /** diff --git a/packages/roosterjs-editor-types/lib/event/SelectionChangeEvent.ts b/packages/roosterjs-editor-types/lib/event/SelectionChangeEvent.ts index c2726446267..bcb0148436e 100644 --- a/packages/roosterjs-editor-types/lib/event/SelectionChangeEvent.ts +++ b/packages/roosterjs-editor-types/lib/event/SelectionChangeEvent.ts @@ -1,6 +1,6 @@ -import BasePluginEvent from './BasePluginEvent'; -import { PluginEventType } from '../enum/PluginEventType'; -import { SelectionRangeEx } from '../interface/SelectionRangeEx'; +import type BasePluginEvent from './BasePluginEvent'; +import type { PluginEventType } from '../enum/PluginEventType'; +import type { SelectionRangeEx } from '../interface/SelectionRangeEx'; import type { CompatiblePluginEventType } from '../compatibleEnum/PluginEventType'; /** diff --git a/packages/roosterjs-editor-types/lib/event/ShadowEditEvent.ts b/packages/roosterjs-editor-types/lib/event/ShadowEditEvent.ts index 15d128c87f8..d4f9f6b1532 100644 --- a/packages/roosterjs-editor-types/lib/event/ShadowEditEvent.ts +++ b/packages/roosterjs-editor-types/lib/event/ShadowEditEvent.ts @@ -1,6 +1,6 @@ -import BasePluginEvent from './BasePluginEvent'; -import SelectionPath from '../interface/SelectionPath'; -import { PluginEventType } from '../enum/PluginEventType'; +import type BasePluginEvent from './BasePluginEvent'; +import type SelectionPath from '../interface/SelectionPath'; +import type { PluginEventType } from '../enum/PluginEventType'; import type { CompatiblePluginEventType } from '../compatibleEnum/PluginEventType'; /** diff --git a/packages/roosterjs-editor-types/lib/event/ZoomChangedEvent.ts b/packages/roosterjs-editor-types/lib/event/ZoomChangedEvent.ts index 92c1eb8c5c9..e47ea8c009c 100644 --- a/packages/roosterjs-editor-types/lib/event/ZoomChangedEvent.ts +++ b/packages/roosterjs-editor-types/lib/event/ZoomChangedEvent.ts @@ -1,5 +1,5 @@ -import BasePluginEvent from './BasePluginEvent'; -import { PluginEventType } from '../enum/PluginEventType'; +import type BasePluginEvent from './BasePluginEvent'; +import type { PluginEventType } from '../enum/PluginEventType'; import type { CompatiblePluginEventType } from '../compatibleEnum/PluginEventType'; /** diff --git a/packages/roosterjs-editor-types/lib/interface/AnnounceData.ts b/packages/roosterjs-editor-types/lib/interface/AnnounceData.ts new file mode 100644 index 00000000000..113c7361f98 --- /dev/null +++ b/packages/roosterjs-editor-types/lib/interface/AnnounceData.ts @@ -0,0 +1,23 @@ +import type { CompatibleKnownAnnounceStrings } from '../compatibleEnum/KnownAnnounceStrings'; +import type { KnownAnnounceStrings } from '../enum/KnownAnnounceStrings'; + +/** + * Represents data, that can be used to announce text to screen reader. + */ +export default interface AnnounceData { + /** + * @optional Default announce strings built in Rooster + */ + defaultStrings?: KnownAnnounceStrings | CompatibleKnownAnnounceStrings; + + /** + * @optional string to announce from this Content Changed event, will be the fallback value if default string + * is not provided or if it is not found in the strings map. + */ + text?: string; + + /** + * @optional if provided, will attempt to replace {n} with each of the values inside of the array. + */ + formatStrings?: string[]; +} diff --git a/packages/roosterjs-editor-types/lib/interface/ClipboardData.ts b/packages/roosterjs-editor-types/lib/interface/ClipboardData.ts index c1536300401..bf1380881a2 100644 --- a/packages/roosterjs-editor-types/lib/interface/ClipboardData.ts +++ b/packages/roosterjs-editor-types/lib/interface/ClipboardData.ts @@ -1,4 +1,4 @@ -import EdgeLinkPreview from '../browser/EdgeLinkPreview'; +import type EdgeLinkPreview from '../browser/EdgeLinkPreview'; /** * An object contains all related data for pasting diff --git a/packages/roosterjs-editor-types/lib/interface/ContentChangedData.ts b/packages/roosterjs-editor-types/lib/interface/ContentChangedData.ts index 9b17977cacd..b74129d496a 100644 --- a/packages/roosterjs-editor-types/lib/interface/ContentChangedData.ts +++ b/packages/roosterjs-editor-types/lib/interface/ContentChangedData.ts @@ -1,4 +1,5 @@ -import { EntityState } from './Snapshot'; +import type AnnounceData from './AnnounceData'; +import type { EntityState } from './Snapshot'; /** * Property that is going to store additional data related to the Content Changed Event @@ -15,4 +16,11 @@ export default interface ContentChangedData { * @returns Related entity state array */ getEntityState?: () => EntityState[]; + + /** + * @optional + * Get Announce data from this content changed event. + * @returns + */ + getAnnounceData?: () => AnnounceData | undefined; } diff --git a/packages/roosterjs-editor-types/lib/interface/ContentEditFeature.ts b/packages/roosterjs-editor-types/lib/interface/ContentEditFeature.ts index dfa0b58c7e2..e61c55cbb09 100644 --- a/packages/roosterjs-editor-types/lib/interface/ContentEditFeature.ts +++ b/packages/roosterjs-editor-types/lib/interface/ContentEditFeature.ts @@ -1,6 +1,6 @@ -import IEditor from './IEditor'; -import { CompatiblePluginKeyboardEvent, PluginKeyboardEvent } from '../event/PluginDomEvent'; -import { PluginEvent } from '../event/PluginEvent'; +import type IEditor from './IEditor'; +import type { CompatiblePluginKeyboardEvent, PluginKeyboardEvent } from '../event/PluginDomEvent'; +import type { PluginEvent } from '../event/PluginEvent'; /** * Generic ContentEditFeature interface diff --git a/packages/roosterjs-editor-types/lib/interface/ContentMetadata.ts b/packages/roosterjs-editor-types/lib/interface/ContentMetadata.ts index db7104a9841..91c452b4712 100644 --- a/packages/roosterjs-editor-types/lib/interface/ContentMetadata.ts +++ b/packages/roosterjs-editor-types/lib/interface/ContentMetadata.ts @@ -1,6 +1,6 @@ -import SelectionPath from './SelectionPath'; -import TableSelection from './TableSelection'; -import { SelectionRangeTypes } from '../enum/SelectionRangeTypes'; +import type SelectionPath from './SelectionPath'; +import type TableSelection from './TableSelection'; +import type { SelectionRangeTypes } from '../enum/SelectionRangeTypes'; /** * Common part of NormalContentMetadata and TableContentMetadata diff --git a/packages/roosterjs-editor-types/lib/interface/ContextMenuProvider.ts b/packages/roosterjs-editor-types/lib/interface/ContextMenuProvider.ts index 7d406e60339..03876b34cbe 100644 --- a/packages/roosterjs-editor-types/lib/interface/ContextMenuProvider.ts +++ b/packages/roosterjs-editor-types/lib/interface/ContextMenuProvider.ts @@ -1,4 +1,4 @@ -import EditorPlugin from './EditorPlugin'; +import type EditorPlugin from './EditorPlugin'; /** * An extended Editor plugin interface which supports providing context menu items diff --git a/packages/roosterjs-editor-types/lib/interface/CorePlugins.ts b/packages/roosterjs-editor-types/lib/interface/CorePlugins.ts index 39ae92f5d97..94238bd6d33 100644 --- a/packages/roosterjs-editor-types/lib/interface/CorePlugins.ts +++ b/packages/roosterjs-editor-types/lib/interface/CorePlugins.ts @@ -1,12 +1,12 @@ -import CopyPastePluginState from '../corePluginState/CopyPastePluginState'; -import DOMEventPluginState from '../corePluginState/DOMEventPluginState'; -import EditorPlugin from './EditorPlugin'; -import EditPluginState from '../corePluginState/EditPluginState'; -import EntityPluginState from '../corePluginState/EntityPluginState'; -import LifecyclePluginState from '../corePluginState/LifecyclePluginState'; -import PendingFormatStatePluginState from '../corePluginState/PendingFormatStatePluginState'; -import PluginWithState from './PluginWithState'; -import UndoPluginState from '../corePluginState/UndoPluginState'; +import type CopyPastePluginState from '../corePluginState/CopyPastePluginState'; +import type DOMEventPluginState from '../corePluginState/DOMEventPluginState'; +import type EditorPlugin from './EditorPlugin'; +import type EditPluginState from '../corePluginState/EditPluginState'; +import type EntityPluginState from '../corePluginState/EntityPluginState'; +import type LifecyclePluginState from '../corePluginState/LifecyclePluginState'; +import type PendingFormatStatePluginState from '../corePluginState/PendingFormatStatePluginState'; +import type PluginWithState from './PluginWithState'; +import type UndoPluginState from '../corePluginState/UndoPluginState'; /** * An interface for editor core plugins. diff --git a/packages/roosterjs-editor-types/lib/interface/CustomReplacement.ts b/packages/roosterjs-editor-types/lib/interface/CustomReplacement.ts index 807717947f2..242abf2f1eb 100644 --- a/packages/roosterjs-editor-types/lib/interface/CustomReplacement.ts +++ b/packages/roosterjs-editor-types/lib/interface/CustomReplacement.ts @@ -1,4 +1,4 @@ -import IEditor from './IEditor'; +import type IEditor from './IEditor'; /** * An interface to define a replacement rule for CustomReplace plugin diff --git a/packages/roosterjs-editor-types/lib/interface/DarkColorHandler.ts b/packages/roosterjs-editor-types/lib/interface/DarkColorHandler.ts index eeb600ce45d..e37c9e7fdfb 100644 --- a/packages/roosterjs-editor-types/lib/interface/DarkColorHandler.ts +++ b/packages/roosterjs-editor-types/lib/interface/DarkColorHandler.ts @@ -1,4 +1,4 @@ -import ModeIndependentColor from './ModeIndependentColor'; +import type ModeIndependentColor from './ModeIndependentColor'; /** * Represents a combination of color key, light color and dark color, parsed from existing color value diff --git a/packages/roosterjs-editor-types/lib/interface/DefaultFormat.ts b/packages/roosterjs-editor-types/lib/interface/DefaultFormat.ts index 9484367209f..2532e42a8ff 100644 --- a/packages/roosterjs-editor-types/lib/interface/DefaultFormat.ts +++ b/packages/roosterjs-editor-types/lib/interface/DefaultFormat.ts @@ -1,4 +1,4 @@ -import ModeIndependentColor from './ModeIndependentColor'; +import type ModeIndependentColor from './ModeIndependentColor'; /** * Default format settings diff --git a/packages/roosterjs-editor-types/lib/interface/EditorCore.ts b/packages/roosterjs-editor-types/lib/interface/EditorCore.ts index 6c62bf73f6f..bf3d51e9a74 100644 --- a/packages/roosterjs-editor-types/lib/interface/EditorCore.ts +++ b/packages/roosterjs-editor-types/lib/interface/EditorCore.ts @@ -1,25 +1,28 @@ -import ClipboardData from './ClipboardData'; -import ContentChangedData from './ContentChangedData'; -import DarkColorHandler from './DarkColorHandler'; -import EditorPlugin from './EditorPlugin'; -import NodePosition from './NodePosition'; -import Rect from './Rect'; -import SelectionPath from './SelectionPath'; -import TableSelection from './TableSelection'; -import { ChangeSource } from '../enum/ChangeSource'; -import { ColorTransformDirection } from '../enum/ColorTransformDirection'; -import { ContentMetadata } from './ContentMetadata'; -import { DOMEventHandler } from '../type/domEventHandler'; -import { GetContentMode } from '../enum/GetContentMode'; -import { ImageSelectionRange, SelectionRangeEx } from './SelectionRangeEx'; -import { InsertOption } from './InsertOption'; -import { PendableFormatState, StyleBasedFormatState } from './FormatState'; -import { PluginEvent } from '../event/PluginEvent'; -import { PluginState } from './CorePlugins'; -import { PositionType } from '../enum/PositionType'; -import { SizeTransformer } from '../type/SizeTransformer'; -import { TableSelectionRange } from './SelectionRangeEx'; -import { TrustedHTMLHandler } from '../type/TrustedHTMLHandler'; +import type ClipboardData from './ClipboardData'; +import type ContentChangedData from './ContentChangedData'; +import type DarkColorHandler from './DarkColorHandler'; +import type EditorPlugin from './EditorPlugin'; +import type NodePosition from './NodePosition'; +import type Rect from './Rect'; +import type SelectionPath from './SelectionPath'; +import type TableSelection from './TableSelection'; +import type { ChangeSource } from '../enum/ChangeSource'; +import type { ColorTransformDirection } from '../enum/ColorTransformDirection'; +import type { ContentMetadata } from './ContentMetadata'; +import type { DOMEventHandler } from '../type/domEventHandler'; +import type { GetContentMode } from '../enum/GetContentMode'; +import type { + ImageSelectionRange, + SelectionRangeEx, + TableSelectionRange, +} from './SelectionRangeEx'; +import type { InsertOption } from './InsertOption'; +import type { PendableFormatState, StyleBasedFormatState } from './FormatState'; +import type { PluginEvent } from '../event/PluginEvent'; +import type { PluginState } from './CorePlugins'; +import type { PositionType } from '../enum/PositionType'; +import type { SizeTransformer } from '../type/SizeTransformer'; +import type { TrustedHTMLHandler } from '../type/TrustedHTMLHandler'; import type { CompatibleChangeSource } from '../compatibleEnum/ChangeSource'; import type { CompatibleColorTransformDirection } from '../compatibleEnum/ColorTransformDirection'; import type { CompatibleGetContentMode } from '../compatibleEnum/GetContentMode'; @@ -81,6 +84,13 @@ export default interface EditorCore extends PluginState { * If keep it null, editor will still use original dataset-based dark mode solution. */ darkColorHandler: DarkColorHandler; + + /** + * A callback to be invoked when any exception is thrown during disposing editor + * @param plugin The plugin that causes exception + * @param error The error object we got + */ + disposeErrorHandler?: (plugin: EditorPlugin, error: Error) => void; } /** diff --git a/packages/roosterjs-editor-types/lib/interface/EditorOptions.ts b/packages/roosterjs-editor-types/lib/interface/EditorOptions.ts index 5a48b426145..226c74f7365 100644 --- a/packages/roosterjs-editor-types/lib/interface/EditorOptions.ts +++ b/packages/roosterjs-editor-types/lib/interface/EditorOptions.ts @@ -1,14 +1,14 @@ -import CorePlugins from './CorePlugins'; -import DarkColorHandler from './DarkColorHandler'; -import DefaultFormat from './DefaultFormat'; -import EditorPlugin from './EditorPlugin'; -import Rect from './Rect'; -import Snapshot from './Snapshot'; -import UndoSnapshotsService from './UndoSnapshotsService'; -import { CoreApiMap } from './EditorCore'; -import { ExperimentalFeatures } from '../enum/ExperimentalFeatures'; -import { SizeTransformer } from '../type/SizeTransformer'; -import { TrustedHTMLHandler } from '../type/TrustedHTMLHandler'; +import type CorePlugins from './CorePlugins'; +import type DarkColorHandler from './DarkColorHandler'; +import type DefaultFormat from './DefaultFormat'; +import type EditorPlugin from './EditorPlugin'; +import type Rect from './Rect'; +import type Snapshot from './Snapshot'; +import type UndoSnapshotsService from './UndoSnapshotsService'; +import type { CoreApiMap } from './EditorCore'; +import type { ExperimentalFeatures } from '../enum/ExperimentalFeatures'; +import type { SizeTransformer } from '../type/SizeTransformer'; +import type { TrustedHTMLHandler } from '../type/TrustedHTMLHandler'; import type { CompatibleExperimentalFeatures } from '../compatibleEnum/ExperimentalFeatures'; /** @@ -144,4 +144,11 @@ export default interface EditorOptions { * Color of the border of a selectedImage. Default color: '#DB626C' */ imageSelectionBorderColor?: string; + + /** + * A callback to be invoked when any exception is thrown during disposing editor + * @param plugin The plugin that causes exception + * @param error The error object we got + */ + disposeErrorHandler?: (plugin: EditorPlugin, error: Error) => void; } diff --git a/packages/roosterjs-editor-types/lib/interface/EditorPlugin.ts b/packages/roosterjs-editor-types/lib/interface/EditorPlugin.ts index 4f8caf56743..c416ff24d0f 100644 --- a/packages/roosterjs-editor-types/lib/interface/EditorPlugin.ts +++ b/packages/roosterjs-editor-types/lib/interface/EditorPlugin.ts @@ -1,5 +1,5 @@ -import IEditor from './IEditor'; -import { PluginEvent } from '../event/PluginEvent'; +import type IEditor from './IEditor'; +import type { PluginEvent } from '../event/PluginEvent'; /** * Interface of an editor plugin diff --git a/packages/roosterjs-editor-types/lib/interface/FormatState.ts b/packages/roosterjs-editor-types/lib/interface/FormatState.ts index de2bd0bc932..a2e10c05c92 100644 --- a/packages/roosterjs-editor-types/lib/interface/FormatState.ts +++ b/packages/roosterjs-editor-types/lib/interface/FormatState.ts @@ -1,5 +1,5 @@ -import ModeIndependentColor from './ModeIndependentColor'; -import TableFormat from './TableFormat'; +import type ModeIndependentColor from './ModeIndependentColor'; +import type TableFormat from './TableFormat'; /** * Format states that can have pending state. diff --git a/packages/roosterjs-editor-types/lib/interface/HtmlSanitizerOptions.ts b/packages/roosterjs-editor-types/lib/interface/HtmlSanitizerOptions.ts index 7b3c3b25c0a..53b1ec28179 100644 --- a/packages/roosterjs-editor-types/lib/interface/HtmlSanitizerOptions.ts +++ b/packages/roosterjs-editor-types/lib/interface/HtmlSanitizerOptions.ts @@ -1,4 +1,4 @@ -import { +import type { AttributeCallbackMap, CssStyleCallbackMap, ElementCallbackMap, diff --git a/packages/roosterjs-editor-types/lib/interface/IContentTraverser.ts b/packages/roosterjs-editor-types/lib/interface/IContentTraverser.ts index 322bfb36c88..e5b442551c9 100644 --- a/packages/roosterjs-editor-types/lib/interface/IContentTraverser.ts +++ b/packages/roosterjs-editor-types/lib/interface/IContentTraverser.ts @@ -1,5 +1,5 @@ -import BlockElement from './BlockElement'; -import InlineElement from './InlineElement'; +import type BlockElement from './BlockElement'; +import type InlineElement from './InlineElement'; /** * Interface of ContentTraverser, provides traversing of content inside editor. diff --git a/packages/roosterjs-editor-types/lib/interface/IEditor.ts b/packages/roosterjs-editor-types/lib/interface/IEditor.ts index 077c7733219..64a34bad60f 100644 --- a/packages/roosterjs-editor-types/lib/interface/IEditor.ts +++ b/packages/roosterjs-editor-types/lib/interface/IEditor.ts @@ -1,33 +1,33 @@ -import BlockElement from './BlockElement'; -import ClipboardData from './ClipboardData'; -import ContentChangedData from './ContentChangedData'; -import DarkColorHandler from './DarkColorHandler'; -import DefaultFormat from './DefaultFormat'; -import IContentTraverser from './IContentTraverser'; -import IPositionContentSearcher from './IPositionContentSearcher'; -import NodePosition from './NodePosition'; -import Rect from './Rect'; -import Region from './Region'; -import SelectionPath from './SelectionPath'; -import TableSelection from './TableSelection'; -import { ChangeSource } from '../enum/ChangeSource'; -import { ColorTransformDirection } from '../enum/ColorTransformDirection'; -import { ContentPosition } from '../enum/ContentPosition'; -import { DOMEventHandler } from '../type/domEventHandler'; -import { EditorUndoState, PendableFormatState, StyleBasedFormatState } from './FormatState'; -import { ExperimentalFeatures } from '../enum/ExperimentalFeatures'; -import { GenericContentEditFeature } from './ContentEditFeature'; -import { GetContentMode } from '../enum/GetContentMode'; -import { InsertOption } from './InsertOption'; -import { PluginEvent } from '../event/PluginEvent'; -import { PluginEventData, PluginEventFromType } from '../event/PluginEventData'; -import { PluginEventType } from '../enum/PluginEventType'; -import { PositionType } from '../enum/PositionType'; -import { QueryScope } from '../enum/QueryScope'; -import { RegionType } from '../enum/RegionType'; -import { SelectionRangeEx } from './SelectionRangeEx'; -import { SizeTransformer } from '../type/SizeTransformer'; -import { TrustedHTMLHandler } from '../type/TrustedHTMLHandler'; +import type BlockElement from './BlockElement'; +import type ClipboardData from './ClipboardData'; +import type ContentChangedData from './ContentChangedData'; +import type DarkColorHandler from './DarkColorHandler'; +import type DefaultFormat from './DefaultFormat'; +import type IContentTraverser from './IContentTraverser'; +import type IPositionContentSearcher from './IPositionContentSearcher'; +import type NodePosition from './NodePosition'; +import type Rect from './Rect'; +import type Region from './Region'; +import type SelectionPath from './SelectionPath'; +import type TableSelection from './TableSelection'; +import type { ChangeSource } from '../enum/ChangeSource'; +import type { ColorTransformDirection } from '../enum/ColorTransformDirection'; +import type { ContentPosition } from '../enum/ContentPosition'; +import type { DOMEventHandler } from '../type/domEventHandler'; +import type { EditorUndoState, PendableFormatState, StyleBasedFormatState } from './FormatState'; +import type { ExperimentalFeatures } from '../enum/ExperimentalFeatures'; +import type { GenericContentEditFeature } from './ContentEditFeature'; +import type { GetContentMode } from '../enum/GetContentMode'; +import type { InsertOption } from './InsertOption'; +import type { PluginEvent } from '../event/PluginEvent'; +import type { PluginEventData, PluginEventFromType } from '../event/PluginEventData'; +import type { PluginEventType } from '../enum/PluginEventType'; +import type { PositionType } from '../enum/PositionType'; +import type { QueryScope } from '../enum/QueryScope'; +import type { RegionType } from '../enum/RegionType'; +import type { SelectionRangeEx } from './SelectionRangeEx'; +import type { SizeTransformer } from '../type/SizeTransformer'; +import type { TrustedHTMLHandler } from '../type/TrustedHTMLHandler'; import type { CompatiblePluginEventType } from '../compatibleEnum/PluginEventType'; import type { CompatibleChangeSource } from '../compatibleEnum/ChangeSource'; import type { CompatibleContentPosition } from '../compatibleEnum/ContentPosition'; @@ -576,7 +576,6 @@ export default interface IEditor { * @param keyboardEvent Optional keyboard event object */ ensureTypeInContainer(position: NodePosition, keyboardEvent?: KeyboardEvent): void; - //#endregion //#region Dark mode APIs diff --git a/packages/roosterjs-editor-types/lib/interface/IPositionContentSearcher.ts b/packages/roosterjs-editor-types/lib/interface/IPositionContentSearcher.ts index cdcd6e659b6..96ff53751b5 100644 --- a/packages/roosterjs-editor-types/lib/interface/IPositionContentSearcher.ts +++ b/packages/roosterjs-editor-types/lib/interface/IPositionContentSearcher.ts @@ -1,4 +1,4 @@ -import InlineElement from './InlineElement'; +import type InlineElement from './InlineElement'; /** * The class that helps search content around a position diff --git a/packages/roosterjs-editor-types/lib/interface/ImageEditOptions.ts b/packages/roosterjs-editor-types/lib/interface/ImageEditOptions.ts index bc1dbb037ec..e3511fc5cb7 100644 --- a/packages/roosterjs-editor-types/lib/interface/ImageEditOptions.ts +++ b/packages/roosterjs-editor-types/lib/interface/ImageEditOptions.ts @@ -1,5 +1,5 @@ -import ModeIndependentColor from './ModeIndependentColor'; -import { ImageEditOperation } from '../enum/ImageEditOperation'; +import type ModeIndependentColor from './ModeIndependentColor'; +import type { ImageEditOperation } from '../enum/ImageEditOperation'; import type { CompatibleImageEditOperation } from '../compatibleEnum/ImageEditOperation'; /** diff --git a/packages/roosterjs-editor-types/lib/interface/InlineElement.ts b/packages/roosterjs-editor-types/lib/interface/InlineElement.ts index 4c9142b36a1..ae21a9830a9 100644 --- a/packages/roosterjs-editor-types/lib/interface/InlineElement.ts +++ b/packages/roosterjs-editor-types/lib/interface/InlineElement.ts @@ -1,5 +1,5 @@ -import BlockElement from './BlockElement'; -import NodePosition from './NodePosition'; +import type BlockElement from './BlockElement'; +import type NodePosition from './NodePosition'; /** * This refers to an inline element (as opposed to block) in editor diff --git a/packages/roosterjs-editor-types/lib/interface/InsertOption.ts b/packages/roosterjs-editor-types/lib/interface/InsertOption.ts index f2bb370eef7..2b6c035d65b 100644 --- a/packages/roosterjs-editor-types/lib/interface/InsertOption.ts +++ b/packages/roosterjs-editor-types/lib/interface/InsertOption.ts @@ -1,4 +1,4 @@ -import { ContentPosition } from '../enum/ContentPosition'; +import type { ContentPosition } from '../enum/ContentPosition'; import type { CompatibleContentPosition } from '../compatibleEnum/ContentPosition'; /** diff --git a/packages/roosterjs-editor-types/lib/interface/PickerDataProvider.ts b/packages/roosterjs-editor-types/lib/interface/PickerDataProvider.ts index 0b59c4cf1b1..84e002ed94c 100644 --- a/packages/roosterjs-editor-types/lib/interface/PickerDataProvider.ts +++ b/packages/roosterjs-editor-types/lib/interface/PickerDataProvider.ts @@ -1,4 +1,4 @@ -import IEditor from './IEditor'; +import type IEditor from './IEditor'; /** * Data provider for PickerPlugin diff --git a/packages/roosterjs-editor-types/lib/interface/PluginWithState.ts b/packages/roosterjs-editor-types/lib/interface/PluginWithState.ts index 2590fc61127..2cb1b7f12dd 100644 --- a/packages/roosterjs-editor-types/lib/interface/PluginWithState.ts +++ b/packages/roosterjs-editor-types/lib/interface/PluginWithState.ts @@ -1,4 +1,4 @@ -import EditorPlugin from './EditorPlugin'; +import type EditorPlugin from './EditorPlugin'; /** * An editor plugin which have a state object stored on editor core diff --git a/packages/roosterjs-editor-types/lib/interface/Region.ts b/packages/roosterjs-editor-types/lib/interface/Region.ts index 541ac1b47e5..944d9bbfb5a 100644 --- a/packages/roosterjs-editor-types/lib/interface/Region.ts +++ b/packages/roosterjs-editor-types/lib/interface/Region.ts @@ -1,5 +1,5 @@ -import NodePosition from './NodePosition'; -import RegionBase from './RegionBase'; +import type NodePosition from './NodePosition'; +import type RegionBase from './RegionBase'; /** * Represents a DOM region with selection information diff --git a/packages/roosterjs-editor-types/lib/interface/SanitizeHtmlOptions.ts b/packages/roosterjs-editor-types/lib/interface/SanitizeHtmlOptions.ts index 33ef104c20a..c24d6a3bdc5 100644 --- a/packages/roosterjs-editor-types/lib/interface/SanitizeHtmlOptions.ts +++ b/packages/roosterjs-editor-types/lib/interface/SanitizeHtmlOptions.ts @@ -1,5 +1,5 @@ -import HtmlSanitizerOptions from './HtmlSanitizerOptions'; -import { StringMap } from '../type/htmlSanitizerCallbackTypes'; +import type HtmlSanitizerOptions from './HtmlSanitizerOptions'; +import type { StringMap } from '../type/htmlSanitizerCallbackTypes'; /** * @deprecated diff --git a/packages/roosterjs-editor-types/lib/interface/SelectionRangeEx.ts b/packages/roosterjs-editor-types/lib/interface/SelectionRangeEx.ts index 2cc8c811969..45e087ebeba 100644 --- a/packages/roosterjs-editor-types/lib/interface/SelectionRangeEx.ts +++ b/packages/roosterjs-editor-types/lib/interface/SelectionRangeEx.ts @@ -1,5 +1,5 @@ -import TableSelection from './TableSelection'; -import { SelectionRangeTypes } from '../enum/SelectionRangeTypes'; +import type TableSelection from './TableSelection'; +import type { SelectionRangeTypes } from '../enum/SelectionRangeTypes'; import type { CompatibleSelectionRangeTypes } from '../compatibleEnum/SelectionRangeTypes'; /** diff --git a/packages/roosterjs-editor-types/lib/interface/Snapshot.ts b/packages/roosterjs-editor-types/lib/interface/Snapshot.ts index 4586a692d42..cb0e72c78ab 100644 --- a/packages/roosterjs-editor-types/lib/interface/Snapshot.ts +++ b/packages/roosterjs-editor-types/lib/interface/Snapshot.ts @@ -1,5 +1,5 @@ -import ModeIndependentColor from './ModeIndependentColor'; -import { ContentMetadata } from './ContentMetadata'; +import type ModeIndependentColor from './ModeIndependentColor'; +import type { ContentMetadata } from './ContentMetadata'; /** * State for an entity. This is used for storing entity undo snapshot diff --git a/packages/roosterjs-editor-types/lib/interface/TableFormat.ts b/packages/roosterjs-editor-types/lib/interface/TableFormat.ts index 28710c2d821..ddacb2627be 100644 --- a/packages/roosterjs-editor-types/lib/interface/TableFormat.ts +++ b/packages/roosterjs-editor-types/lib/interface/TableFormat.ts @@ -1,4 +1,4 @@ -import { TableBorderFormat } from '../enum/TableBorderFormat'; +import type { TableBorderFormat } from '../enum/TableBorderFormat'; import type { CompatibleTableBorderFormat } from '../compatibleEnum/TableBorderFormat'; /** diff --git a/packages/roosterjs-editor-types/lib/interface/TableSelection.ts b/packages/roosterjs-editor-types/lib/interface/TableSelection.ts index 334354dc0eb..36fc65c009d 100644 --- a/packages/roosterjs-editor-types/lib/interface/TableSelection.ts +++ b/packages/roosterjs-editor-types/lib/interface/TableSelection.ts @@ -1,4 +1,4 @@ -import Coordinates from './Coordinates'; +import type Coordinates from './Coordinates'; /** * Represents a selection made inside of a table diff --git a/packages/roosterjs-editor-types/lib/interface/TargetWindow.ts b/packages/roosterjs-editor-types/lib/interface/TargetWindow.ts index c7779894989..6418b22290a 100644 --- a/packages/roosterjs-editor-types/lib/interface/TargetWindow.ts +++ b/packages/roosterjs-editor-types/lib/interface/TargetWindow.ts @@ -1,4 +1,4 @@ -import TargetWindowBase from './TargetWindowBase'; +import type TargetWindowBase from './TargetWindowBase'; /** * Define DOM types of window, used by safeInstanceOf() to check if a given object is of the specified type of its own window diff --git a/packages/roosterjs-editor-types/lib/interface/index.ts b/packages/roosterjs-editor-types/lib/interface/index.ts index 93fd12d35ea..334fdff5529 100644 --- a/packages/roosterjs-editor-types/lib/interface/index.ts +++ b/packages/roosterjs-editor-types/lib/interface/index.ts @@ -1,3 +1,4 @@ +export { default as AnnounceData } from './AnnounceData'; export { default as BlockElement } from './BlockElement'; export { default as ClipboardData } from './ClipboardData'; export { default as ContextMenuProvider } from './ContextMenuProvider'; diff --git a/packages/roosterjs-editor-types/lib/type/CoreCreator.ts b/packages/roosterjs-editor-types/lib/type/CoreCreator.ts index 98cbab5b77e..3c05543926b 100644 --- a/packages/roosterjs-editor-types/lib/type/CoreCreator.ts +++ b/packages/roosterjs-editor-types/lib/type/CoreCreator.ts @@ -1,5 +1,5 @@ -import EditorCore from '../interface/EditorCore'; -import EditorOptions from '../interface/EditorOptions'; +import type EditorCore from '../interface/EditorCore'; +import type EditorOptions from '../interface/EditorOptions'; /** * Type of Editor Core Creator diff --git a/packages/roosterjs-editor-types/lib/type/Definition.ts b/packages/roosterjs-editor-types/lib/type/Definition.ts index 20756f7e6ad..4b6dbbd9d01 100644 --- a/packages/roosterjs-editor-types/lib/type/Definition.ts +++ b/packages/roosterjs-editor-types/lib/type/Definition.ts @@ -1,4 +1,4 @@ -import { DefinitionType } from '../enum/DefinitionType'; +import type { DefinitionType } from '../enum/DefinitionType'; import type { CompatibleDefinitionType } from '../compatibleEnum/DefinitionType'; /** diff --git a/packages/roosterjs-editor-types/lib/type/domEventHandler.ts b/packages/roosterjs-editor-types/lib/type/domEventHandler.ts index d3e57e9fb91..4a02012a7a4 100644 --- a/packages/roosterjs-editor-types/lib/type/domEventHandler.ts +++ b/packages/roosterjs-editor-types/lib/type/domEventHandler.ts @@ -1,4 +1,4 @@ -import { PluginEventType } from '../enum/PluginEventType'; +import type { PluginEventType } from '../enum/PluginEventType'; import type { CompatiblePluginEventType } from '../compatibleEnum/PluginEventType'; /** diff --git a/packages/roosterjs/lib/createEditor.ts b/packages/roosterjs/lib/createEditor.ts index cb8fd0ac215..365d0cd7610 100644 --- a/packages/roosterjs/lib/createEditor.ts +++ b/packages/roosterjs/lib/createEditor.ts @@ -1,7 +1,7 @@ +import { ContentEdit, HyperLink, Paste } from 'roosterjs-editor-plugins'; import { Editor } from 'roosterjs-editor-core'; -import { EditorOptions, EditorPlugin, IEditor } from 'roosterjs-editor-types'; import { getDarkColor } from 'roosterjs-color-utils'; -import { ContentEdit, HyperLink, Paste } from 'roosterjs-editor-plugins'; +import type { EditorOptions, EditorPlugin, IEditor } from 'roosterjs-editor-types'; /** * Create an editor instance with most common options @@ -32,5 +32,6 @@ export default function createEditor( textColor: '#000000', }, }; + return new Editor(contentDiv, options); } diff --git a/tools/build.js b/tools/build.js index 3e6a78d8459..2a12391c59a 100644 --- a/tools/build.js +++ b/tools/build.js @@ -4,7 +4,7 @@ const ProgressBar = require('progress'); // Steps -const tslintStep = require('./buildTools/tslint'); +const eslintStep = require('./buildTools/eslint'); const checkDependencyStep = require('./buildTools/checkDependency'); const cleanStep = require('./buildTools/clean'); const normalizeStep = require('./buildTools/normalize'); @@ -18,7 +18,7 @@ const buildDocumentStep = require('./buildTools/buildDocument'); const publishStep = require('./buildTools/publish'); const buildTestStep = require('./buildTools/buildTest'); const allTasks = [ - tslintStep, + eslintStep, cleanStep, normalizeStep, checkDependencyStep, @@ -51,7 +51,7 @@ const allTasks = [ // Commands const commands = [ - 'tslint', // Run tslint to check code style + 'eslint', // Run eslint to check code style 'checkdep', // Check circular dependency among files 'clean', // Clean target folder 'normalize', // Normalize package.json files diff --git a/tools/buildTools/eslint.js b/tools/buildTools/eslint.js new file mode 100644 index 00000000000..090117c5553 --- /dev/null +++ b/tools/buildTools/eslint.js @@ -0,0 +1,27 @@ +'use strict'; + +const path = require('path'); +const { + nodeModulesPath, + runNode, + packagesPath, + packagesUiPath, + packagesContentModelPath, + rootPath, +} = require('./common'); + +function eslint() { + const eslintPath = path.join(nodeModulesPath, 'eslint/bin/eslint.js'); + [packagesPath, packagesUiPath, packagesContentModelPath].forEach(p => { + runNode( + eslintPath + ' -c ' + path.join(rootPath, '.eslintrc.js') + ' ./**/lib/**/*.{ts,tsx}', + p + ); + }); +} + +module.exports = { + message: 'Running eslint...', + callback: eslint, + enabled: options => options.eslint, +}; diff --git a/tools/buildTools/tslint.js b/tools/buildTools/tslint.js deleted file mode 100644 index ae03c64195d..00000000000 --- a/tools/buildTools/tslint.js +++ /dev/null @@ -1,16 +0,0 @@ -'use strict'; - -const path = require('path'); -const { rootPath, nodeModulesPath, runNode } = require('./common'); - -function tslint() { - const tslintPath = path.join(nodeModulesPath, 'tslint/bin/tslint'); - const projectPath = path.join(rootPath, 'tools/tsconfig.tslint.json'); - runNode(tslintPath + ' --project ' + projectPath, rootPath); -} - -module.exports = { - message: 'Running tslint...', - callback: tslint, - enabled: options => options.tslint, -}; diff --git a/tslint.json b/tslint.json deleted file mode 100644 index 7f8426e4fe8..00000000000 --- a/tslint.json +++ /dev/null @@ -1,81 +0,0 @@ -{ - "rules": { - "use-isnan": true, - "jquery-deferred-must-complete": true, - "no-backbone-get-set-outside-model": false, - "no-banned-terms": true, - "no-constant-condition": true, - "no-control-regex": true, - "no-cookies": true, - "no-delete-expression": true, - "no-document-write": true, - "no-disable-auto-sanitization": true, - "no-duplicate-switch-case": true, - "no-duplicate-parameter-names": true, - "no-empty-interface": true, - "no-exec-script": true, - "function-constructor": true, - "no-function-expression": false, - "no-increment-decrement": false, - "no-invalid-regexp": true, - "no-for-in": true, - "no-missing-visibility-modifiers": false, - "no-multiline-string": true, - "no-multiple-var-decl": true, - "unnecessary-bind": true, - "no-octal-literal": true, - "no-regex-spaces": true, - "no-reserved-keywords": false, - "no-sparse-arrays": true, - "no-string-based-set-immediate": true, - "no-string-based-set-interval": true, - "no-string-based-set-timeout": true, - "no-with-statement": true, - "prefer-array-literal": false, - "promise-must-complete": true, - "react-no-dangerous-html": true, - "use-named-parameter": true, - "class-name": true, - "curly": true, - "eofline": false, - "forin": true, - "indent": [true, "spaces"], - "label-position": true, - "max-line-length": false, - "no-arg": true, - "no-bitwise": false, - "no-console": [true, "debug", "info", "log", "time", "timeEnd", "trace"], - "no-construct": true, - "no-constructor-vars": false, - "no-debugger": true, - "no-duplicate-variable": true, - "no-empty": false, - "no-eval": true, - "no-string-literal": true, - "no-switch-case-fall-through": true, - "trailing-comma": true, - "no-trailing-whitespace": true, - "no-unused-expression": true, - "no-var-requires": false, - "one-line": [true, "check-open-brace", "check-catch", "check-else", "check-whitespace"], - "quotemark": [false], - "triple-equals": false, - "typedef": [true, "parameter"], - "use-strict": [false], - "variable-name": [true, "check-format", "allow-pascal-case"], - "whitespace": [ - true, - "check-branch", - "check-decl", - "check-operator", - "check-separator", - "check-type" - ], - - "no-namespace": true - }, - "rulesDirectory": [ - "node_modules/tslint-microsoft-contrib", - "node_modules/tslint-eslint-rules/dist/rules" - ] -} diff --git a/versions.json b/versions.json index c9be6fb802f..0b36f9bf23e 100644 --- a/versions.json +++ b/versions.json @@ -1,5 +1,5 @@ { - "packages": "8.56.0", - "packages-ui": "8.52.0", - "packages-content-model": "0.16.0" + "packages": "8.57.0", + "packages-ui": "8.53.0", + "packages-content-model": "0.17.0" } diff --git a/yarn.lock b/yarn.lock index 4f4154cc836..4b890382292 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,6 +2,11 @@ # yarn lockfile v1 +"@aashutoshrathi/word-wrap@^1.2.3": + version "1.2.6" + resolved "https://registry.yarnpkg.com/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz#bd9154aec9983f77b3a034ecaa015c2e4201f6cf" + integrity sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA== + "@babel/code-frame@^7.0.0": version "7.0.0" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.0.0.tgz#06e2ab19bdb535385559aabb5ba59729482800f8" @@ -199,6 +204,38 @@ lodash "^4.17.19" to-fast-properties "^2.0.0" +"@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0": + version "4.4.0" + resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" + integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA== + dependencies: + eslint-visitor-keys "^3.3.0" + +"@eslint-community/regexpp@^4.5.1", "@eslint-community/regexpp@^4.6.1": + version "4.8.2" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.8.2.tgz#26585b7c0ba36362893d3a3c206ee0c57c389616" + integrity sha512-0MGxAVt1m/ZK+LTJp/j0qF7Hz97D9O/FH9Ms3ltnyIdDD57cbb1ACIQTkbHvNXtWDv5TPq7w5Kq56+cNukbo7g== + +"@eslint/eslintrc@^2.1.2": + version "2.1.2" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.1.2.tgz#c6936b4b328c64496692f76944e755738be62396" + integrity sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g== + dependencies: + ajv "^6.12.4" + debug "^4.3.2" + espree "^9.6.0" + globals "^13.19.0" + ignore "^5.2.0" + import-fresh "^3.2.1" + js-yaml "^4.1.0" + minimatch "^3.1.2" + strip-json-comments "^3.1.1" + +"@eslint/js@8.50.0": + version "8.50.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.50.0.tgz#9e93b850f0f3fa35f5fa59adfd03adae8488e484" + integrity sha512-NCC3zz2+nvYd+Ckfh87rA47zfu2QsQpvc6k1yzTk+b9KzRj0wkGa8LSoGOXN6Zv4lRf/EIoZ80biDh9HOI+RNQ== + "@fluentui/date-time-utilities@^8.3.0": version "8.3.0" resolved "https://registry.yarnpkg.com/@fluentui/date-time-utilities/-/date-time-utilities-8.3.0.tgz#a51cd59ea327b948bdb76a083d9c42d95a71c98a" @@ -338,6 +375,25 @@ "@fluentui/set-version" "^8.1.5" tslib "^2.1.0" +"@humanwhocodes/config-array@^0.11.11": + version "0.11.11" + resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.11.tgz#88a04c570dbbc7dd943e4712429c3df09bc32844" + integrity sha512-N2brEuAadi0CcdeMXUkhbZB84eskAc8MEX1By6qEchoVywSgXPIjou4rYsl0V3Hj0ZnuGycGCjdNgockbzeWNA== + dependencies: + "@humanwhocodes/object-schema" "^1.2.1" + debug "^4.1.1" + minimatch "^3.0.5" + +"@humanwhocodes/module-importer@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" + integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== + +"@humanwhocodes/object-schema@^1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" + integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== + "@istanbuljs/schema@^0.1.2": version "0.1.2" resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.2.tgz#26520bf09abe4a5644cd5414e37125a8954241dd" @@ -417,6 +473,27 @@ "@microsoft/load-themed-styles" "1.10.44" loader-utils "~1.1.0" +"@nodelib/fs.scandir@2.1.5": + version "2.1.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" + integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== + dependencies: + "@nodelib/fs.stat" "2.0.5" + run-parallel "^1.1.9" + +"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" + integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== + +"@nodelib/fs.walk@^1.2.3", "@nodelib/fs.walk@^1.2.8": + version "1.2.8" + resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" + integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== + dependencies: + "@nodelib/fs.scandir" "2.1.5" + fastq "^1.6.0" + "@socket.io/base64-arraybuffer@~1.0.2": version "1.0.2" resolved "https://registry.yarnpkg.com/@socket.io/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz#568d9beae00b0d835f4f8c53fd55714986492e61" @@ -508,11 +585,21 @@ resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.12.tgz#d70faba7039d5fca54c83c7dbab41051d2b6f6cb" integrity sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA== +"@types/json-schema@^7.0.12": + version "7.0.13" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.13.tgz#02c24f4363176d2d18fc8b70b9f3c54aba178a85" + integrity sha512-RbSSoHliUbnXj3ny0CNFOoxrIDV6SUGyStHsvDqosw6CkdPV8TtWGlfecuK4ToyMEAql6pzNxgCFKanovUzlgQ== + "@types/json-schema@^7.0.4": version "7.0.5" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.5.tgz#dcce4430e64b443ba8945f0290fb564ad5bac6dd" integrity sha512-7+2BITlgjgDhH0vvwZU/HZJVyk+2XUlvxXe8dFMedNX/aMkaOq++rMAFXc0tM7ij15QaWlbdQASBR9dihi+bDQ== +"@types/json5@^0.0.29": + version "0.0.29" + resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" + integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== + "@types/minimatch@*", "@types/minimatch@^3.0.3": version "3.0.3" resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" @@ -563,11 +650,108 @@ "@types/prop-types" "*" csstype "^2.2.0" +"@types/semver@^7.5.0": + version "7.5.3" + resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.3.tgz#9a726e116beb26c24f1ccd6850201e1246122e04" + integrity sha512-OxepLK9EuNEIPxWNME+C6WwbRAOOI2o2BaQEGzz5Lu2e4Z5eDnEo+/aVEDMIXywoJitJ7xWd641wrGLZdtwRyw== + "@types/trusted-types@*": version "2.0.2" resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-2.0.2.tgz#fc25ad9943bcac11cceb8168db4f275e0e72e756" integrity sha512-F5DIZ36YVLE+PN+Zwws4kJogq47hNgX3Nx6WyDJ3kcplxyke3XIzB8uK5n/Lpm1HBsbGzd6nmGehL8cPekP+Tg== +"@typescript-eslint/eslint-plugin-tslint@^6.7.3": + version "6.7.3" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin-tslint/-/eslint-plugin-tslint-6.7.3.tgz#41409658f9706fa35c98698ab4022c17f90e5c29" + integrity sha512-20Ojx9+I1WT2USAqwMK1m1aoxaY106DDFPQy1XyjvfpzzngMCM1Uyy2bpYb9Q9Wsye5IAvHqK+qQR/dk0f8+Iw== + dependencies: + "@typescript-eslint/utils" "6.7.3" + +"@typescript-eslint/eslint-plugin@^6.7.3": + version "6.7.3" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.7.3.tgz#d98046e9f7102d49a93d944d413c6055c47fafd7" + integrity sha512-vntq452UHNltxsaaN+L9WyuMch8bMd9CqJ3zhzTPXXidwbf5mqqKCVXEuvRZUqLJSTLeWE65lQwyXsRGnXkCTA== + dependencies: + "@eslint-community/regexpp" "^4.5.1" + "@typescript-eslint/scope-manager" "6.7.3" + "@typescript-eslint/type-utils" "6.7.3" + "@typescript-eslint/utils" "6.7.3" + "@typescript-eslint/visitor-keys" "6.7.3" + debug "^4.3.4" + graphemer "^1.4.0" + ignore "^5.2.4" + natural-compare "^1.4.0" + semver "^7.5.4" + ts-api-utils "^1.0.1" + +"@typescript-eslint/parser@^6.7.3": + version "6.7.3" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-6.7.3.tgz#aaf40092a32877439e5957e18f2d6a91c82cc2fd" + integrity sha512-TlutE+iep2o7R8Lf+yoer3zU6/0EAUc8QIBB3GYBc1KGz4c4TRm83xwXUZVPlZ6YCLss4r77jbu6j3sendJoiQ== + dependencies: + "@typescript-eslint/scope-manager" "6.7.3" + "@typescript-eslint/types" "6.7.3" + "@typescript-eslint/typescript-estree" "6.7.3" + "@typescript-eslint/visitor-keys" "6.7.3" + debug "^4.3.4" + +"@typescript-eslint/scope-manager@6.7.3": + version "6.7.3" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-6.7.3.tgz#07e5709c9bdae3eaf216947433ef97b3b8b7d755" + integrity sha512-wOlo0QnEou9cHO2TdkJmzF7DFGvAKEnB82PuPNHpT8ZKKaZu6Bm63ugOTn9fXNJtvuDPanBc78lGUGGytJoVzQ== + dependencies: + "@typescript-eslint/types" "6.7.3" + "@typescript-eslint/visitor-keys" "6.7.3" + +"@typescript-eslint/type-utils@6.7.3": + version "6.7.3" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-6.7.3.tgz#c2c165c135dda68a5e70074ade183f5ad68f3400" + integrity sha512-Fc68K0aTDrKIBvLnKTZ5Pf3MXK495YErrbHb1R6aTpfK5OdSFj0rVN7ib6Tx6ePrZ2gsjLqr0s98NG7l96KSQw== + dependencies: + "@typescript-eslint/typescript-estree" "6.7.3" + "@typescript-eslint/utils" "6.7.3" + debug "^4.3.4" + ts-api-utils "^1.0.1" + +"@typescript-eslint/types@6.7.3": + version "6.7.3" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-6.7.3.tgz#0402b5628a63f24f2dc9d4a678e9a92cc50ea3e9" + integrity sha512-4g+de6roB2NFcfkZb439tigpAMnvEIg3rIjWQ+EM7IBaYt/CdJt6em9BJ4h4UpdgaBWdmx2iWsafHTrqmgIPNw== + +"@typescript-eslint/typescript-estree@6.7.3": + version "6.7.3" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-6.7.3.tgz#ec5bb7ab4d3566818abaf0e4a8fa1958561b7279" + integrity sha512-YLQ3tJoS4VxLFYHTw21oe1/vIZPRqAO91z6Uv0Ss2BKm/Ag7/RVQBcXTGcXhgJMdA4U+HrKuY5gWlJlvoaKZ5g== + dependencies: + "@typescript-eslint/types" "6.7.3" + "@typescript-eslint/visitor-keys" "6.7.3" + debug "^4.3.4" + globby "^11.1.0" + is-glob "^4.0.3" + semver "^7.5.4" + ts-api-utils "^1.0.1" + +"@typescript-eslint/utils@6.7.3": + version "6.7.3" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-6.7.3.tgz#96c655816c373135b07282d67407cb577f62e143" + integrity sha512-vzLkVder21GpWRrmSR9JxGZ5+ibIUSudXlW52qeKpzUEQhRSmyZiVDDj3crAth7+5tmN1ulvgKaCU2f/bPRCzg== + dependencies: + "@eslint-community/eslint-utils" "^4.4.0" + "@types/json-schema" "^7.0.12" + "@types/semver" "^7.5.0" + "@typescript-eslint/scope-manager" "6.7.3" + "@typescript-eslint/types" "6.7.3" + "@typescript-eslint/typescript-estree" "6.7.3" + semver "^7.5.4" + +"@typescript-eslint/visitor-keys@6.7.3": + version "6.7.3" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-6.7.3.tgz#83809631ca12909bd2083558d2f93f5747deebb2" + integrity sha512-HEVXkU9IB+nk9o63CeICMHxFWbHWr3E1mpilIQBe9+7L/lH97rleFLVtYsfnWB+JVMaiFnEaxvknvmIzX+CqVg== + dependencies: + "@typescript-eslint/types" "6.7.3" + eslint-visitor-keys "^3.4.1" + "@webassemblyjs/ast@1.11.6", "@webassemblyjs/ast@^1.11.5": version "1.11.6" resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.6.tgz#db046555d3c413f8966ca50a95176a0e2c642e24" @@ -717,11 +901,21 @@ acorn-import-assertions@^1.9.0: resolved "https://registry.yarnpkg.com/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz#507276249d684797c84e0734ef84860334cfb1ac" integrity sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA== +acorn-jsx@^5.3.2: + version "5.3.2" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" + integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== + acorn@^8.5.0, acorn@^8.7.1: version "8.8.2" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a" integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw== +acorn@^8.9.0: + version "8.10.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.10.0.tgz#8be5b3907a67221a81ab23c7889c4c5526b62ec5" + integrity sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw== + address@^1.0.1: version "1.1.2" resolved "https://registry.yarnpkg.com/address/-/address-1.1.2.tgz#bf1116c9c758c51b7a933d296b72c221ed9428b6" @@ -747,7 +941,7 @@ ajv-keywords@^3.5.2: resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== -ajv@^6.1.0, ajv@^6.12.0, ajv@^6.12.2, ajv@^6.12.5, ajv@^6.5.5: +ajv@^6.1.0, ajv@^6.12.0, ajv@^6.12.2, ajv@^6.12.4, ajv@^6.12.5, ajv@^6.5.5: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== @@ -787,6 +981,11 @@ ansi-regex@^5.0.0: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg== +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + ansi-styles@^3.2.0, ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" @@ -838,6 +1037,11 @@ argparse@^1.0.7: dependencies: sprintf-js "~1.0.2" +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + arr-diff@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" @@ -853,6 +1057,14 @@ arr-union@^3.1.0: resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ= +array-buffer-byte-length@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz#fabe8bc193fea865f317fe7807085ee0dee5aead" + integrity sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A== + dependencies: + call-bind "^1.0.2" + is-array-buffer "^3.0.1" + array-differ@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/array-differ/-/array-differ-3.0.0.tgz#3cbb3d0f316810eafcc47624734237d6aee4ae6b" @@ -868,6 +1080,17 @@ array-flatten@^2.1.0: resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-2.1.2.tgz#24ef80a28c1a893617e2149b0c6d0d788293b099" integrity sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ== +array-includes@^3.1.6: + version "3.1.7" + resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.7.tgz#8cd2e01b26f7a3086cbc87271593fe921c62abda" + integrity sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + get-intrinsic "^1.2.1" + is-string "^1.0.7" + array-union@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" @@ -890,6 +1113,61 @@ array-unique@^0.3.2: resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= +array.prototype.findlastindex@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.3.tgz#b37598438f97b579166940814e2c0493a4f50207" + integrity sha512-LzLoiOMAxvy+Gd3BAq3B7VeIgPdo+Q8hthvKtXybMvRV0jrXfJM/t8mw7nNlpEcVlVUnCnM2KSX4XU5HmpodOA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + es-shim-unscopables "^1.0.0" + get-intrinsic "^1.2.1" + +array.prototype.flat@^1.3.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz#1476217df8cff17d72ee8f3ba06738db5b387d18" + integrity sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + es-shim-unscopables "^1.0.0" + +array.prototype.flatmap@^1.3.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz#c9a7c6831db8e719d6ce639190146c24bbd3e527" + integrity sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + es-shim-unscopables "^1.0.0" + +array.prototype.tosorted@^1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/array.prototype.tosorted/-/array.prototype.tosorted-1.1.2.tgz#620eff7442503d66c799d95503f82b475745cefd" + integrity sha512-HuQCHOlk1Weat5jzStICBCd83NxiIMwqDg/dHEsoefabn/hJRj5pVdWcPUSpRrwhwxZOsQassMpgN/xRYFBMIg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + es-shim-unscopables "^1.0.0" + get-intrinsic "^1.2.1" + +arraybuffer.prototype.slice@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.2.tgz#98bd561953e3e74bb34938e77647179dfe6e9f12" + integrity sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw== + dependencies: + array-buffer-byte-length "^1.0.0" + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + get-intrinsic "^1.2.1" + is-array-buffer "^3.0.2" + is-shared-array-buffer "^1.0.2" + arrify@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/arrify/-/arrify-2.0.1.tgz#c9655e9331e0abcd588d2a7cad7e9956f66701fa" @@ -929,6 +1207,13 @@ async@^2.6.2: dependencies: lodash "^4.17.14" +asynciterator.prototype@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/asynciterator.prototype/-/asynciterator.prototype-1.0.0.tgz#8c5df0514936cdd133604dfcc9d3fb93f09b2b62" + integrity sha512-wwHYEIS0Q80f5mosx3L/dfG5t5rjEa9Ft51GTaNt862EnpyGHpgz2RkZvLPp1oF5TnAiTohkEKVEu8pQPJI7Vg== + dependencies: + has-symbols "^1.0.3" + asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" @@ -939,6 +1224,11 @@ atob@^2.1.1: resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== +available-typed-arrays@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7" + integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== + aws-sign2@~0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" @@ -1088,7 +1378,7 @@ buffer-indexof@^1.0.0: builtin-modules@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" - integrity sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8= + integrity sha512-wxXCdllwGhI2kCC0MnvTGYTMvnVZTvqgypkiTI8Pa5tcz2i6VqsqwYGgqwXji+4RgCzms6EajE4IxiUH6HH8nQ== bytes@3.0.0: version "3.0.0" @@ -1115,7 +1405,7 @@ cache-base@^1.0.1: union-value "^1.0.0" unset-value "^1.0.0" -call-bind@^1.0.0: +call-bind@^1.0.0, call-bind@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== @@ -1506,6 +1796,15 @@ cross-spawn@^7.0.0: shebang-command "^2.0.0" which "^2.0.1" +cross-spawn@^7.0.2: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" + integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + css-loader@3.5.3: version "3.5.3" resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-3.5.3.tgz#95ac16468e1adcd95c844729e0bb167639eb0bcf" @@ -1566,7 +1865,14 @@ debug@^3.1.1, debug@^3.2.5, debug@^3.2.6: dependencies: ms "^2.1.1" -debug@^4.1.0, debug@^4.1.1, debug@^4.3.3, debug@~4.3.1, debug@~4.3.2: +debug@^3.2.7: + version "3.2.7" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" + integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== + dependencies: + ms "^2.1.1" + +debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4, debug@~4.3.1, debug@~4.3.2: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== @@ -1593,6 +1899,11 @@ deep-extend@^0.6.0: resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== +deep-is@^0.1.3: + version "0.1.4" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" + integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== + default-gateway@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/default-gateway/-/default-gateway-4.2.0.tgz#167104c7500c2115f6dd69b0a536bb8ed720552b" @@ -1601,6 +1912,24 @@ default-gateway@^4.2.0: execa "^1.0.0" ip-regex "^2.1.0" +define-data-property@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.0.tgz#0db13540704e1d8d479a0656cf781267531b9451" + integrity sha512-UzGwzcjyv3OtAvolTj1GoyNYzfFR+iqbGjcnBEENZVCpM4/Ng1yhGNvS3lR/xDS74Tb2wGG9WzNSNIOS9UVb2g== + dependencies: + get-intrinsic "^1.2.1" + gopd "^1.0.1" + has-property-descriptors "^1.0.0" + +define-properties@^1.1.3, define-properties@^1.1.4, define-properties@^1.2.0, define-properties@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.1.tgz#10781cc616eb951a80a034bafcaa7377f6af2b6c" + integrity sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg== + dependencies: + define-data-property "^1.0.1" + has-property-descriptors "^1.0.0" + object-keys "^1.1.1" + define-property@^0.2.5: version "0.2.5" resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" @@ -1694,6 +2023,13 @@ diff@^4.0.1: resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== +dir-glob@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" + integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== + dependencies: + path-type "^4.0.0" + dns-equal@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/dns-equal/-/dns-equal-1.0.0.tgz#b39e7f1da6eb0a75ba9c17324b34753c47e0654d" @@ -1717,11 +2053,25 @@ dns-txt@^2.0.2: doctrine@0.7.2: version "0.7.2" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-0.7.2.tgz#7cb860359ba3be90e040b26b729ce4bfa654c523" - integrity sha1-fLhgNZujvpDgQLJrcpzkv6ZUxSM= + integrity sha512-qiB/Rir6Un6Ad/TIgTRzsremsTGWzs8j7woXvp14jgq00676uBiBT5eUOi+FgRywZFVy5Us/c04ISRpZhRbS6w== dependencies: esutils "^1.1.6" isarray "0.0.1" +doctrine@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" + integrity sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw== + dependencies: + esutils "^2.0.2" + +doctrine@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" + integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== + dependencies: + esutils "^2.0.2" + dom-serialize@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/dom-serialize/-/dom-serialize-2.2.1.tgz#562ae8999f44be5ea3076f5419dcd59eb43ac95b" @@ -1854,11 +2204,101 @@ error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" +es-abstract@^1.22.1: + version "1.22.2" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.22.2.tgz#90f7282d91d0ad577f505e423e52d4c1d93c1b8a" + integrity sha512-YoxfFcDmhjOgWPWsV13+2RNjq1F6UQnfs+8TftwNqtzlmFzEXvlUwdrNrYeaizfjQzRMxkZ6ElWMOJIFKdVqwA== + dependencies: + array-buffer-byte-length "^1.0.0" + arraybuffer.prototype.slice "^1.0.2" + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + es-set-tostringtag "^2.0.1" + es-to-primitive "^1.2.1" + function.prototype.name "^1.1.6" + get-intrinsic "^1.2.1" + get-symbol-description "^1.0.0" + globalthis "^1.0.3" + gopd "^1.0.1" + has "^1.0.3" + has-property-descriptors "^1.0.0" + has-proto "^1.0.1" + has-symbols "^1.0.3" + internal-slot "^1.0.5" + is-array-buffer "^3.0.2" + is-callable "^1.2.7" + is-negative-zero "^2.0.2" + is-regex "^1.1.4" + is-shared-array-buffer "^1.0.2" + is-string "^1.0.7" + is-typed-array "^1.1.12" + is-weakref "^1.0.2" + object-inspect "^1.12.3" + object-keys "^1.1.1" + object.assign "^4.1.4" + regexp.prototype.flags "^1.5.1" + safe-array-concat "^1.0.1" + safe-regex-test "^1.0.0" + string.prototype.trim "^1.2.8" + string.prototype.trimend "^1.0.7" + string.prototype.trimstart "^1.0.7" + typed-array-buffer "^1.0.0" + typed-array-byte-length "^1.0.0" + typed-array-byte-offset "^1.0.0" + typed-array-length "^1.0.4" + unbox-primitive "^1.0.2" + which-typed-array "^1.1.11" + +es-iterator-helpers@^1.0.12: + version "1.0.15" + resolved "https://registry.yarnpkg.com/es-iterator-helpers/-/es-iterator-helpers-1.0.15.tgz#bd81d275ac766431d19305923707c3efd9f1ae40" + integrity sha512-GhoY8uYqd6iwUl2kgjTm4CZAf6oo5mHK7BPqx3rKgx893YSsy0LGHV6gfqqQvZt/8xM8xeOnfXBCfqclMKkJ5g== + dependencies: + asynciterator.prototype "^1.0.0" + call-bind "^1.0.2" + define-properties "^1.2.1" + es-abstract "^1.22.1" + es-set-tostringtag "^2.0.1" + function-bind "^1.1.1" + get-intrinsic "^1.2.1" + globalthis "^1.0.3" + has-property-descriptors "^1.0.0" + has-proto "^1.0.1" + has-symbols "^1.0.3" + internal-slot "^1.0.5" + iterator.prototype "^1.1.2" + safe-array-concat "^1.0.1" + es-module-lexer@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-1.2.1.tgz#ba303831f63e6a394983fde2f97ad77b22324527" integrity sha512-9978wrXM50Y4rTMmW5kXIC09ZdXQZqkE4mxhwkd8VbzsGkXGPgV4zWuqQJgCEzYngdo2dYDa0l8xhX4fkSwJSg== +es-set-tostringtag@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz#338d502f6f674301d710b80c8592de8a15f09cd8" + integrity sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg== + dependencies: + get-intrinsic "^1.1.3" + has "^1.0.3" + has-tostringtag "^1.0.0" + +es-shim-unscopables@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz#702e632193201e3edf8713635d083d378e510241" + integrity sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w== + dependencies: + has "^1.0.3" + +es-to-primitive@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" + integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== + dependencies: + is-callable "^1.1.4" + is-date-object "^1.0.1" + is-symbol "^1.0.2" + es6-promise@^4.0.3: version "4.2.8" resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a" @@ -1879,6 +2319,72 @@ escape-string-regexp@^1.0.5: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= +escape-string-regexp@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + +eslint-import-resolver-node@^0.3.7: + version "0.3.9" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz#d4eaac52b8a2e7c3cd1903eb00f7e053356118ac" + integrity sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g== + dependencies: + debug "^3.2.7" + is-core-module "^2.13.0" + resolve "^1.22.4" + +eslint-module-utils@^2.8.0: + version "2.8.0" + resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz#e439fee65fc33f6bba630ff621efc38ec0375c49" + integrity sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw== + dependencies: + debug "^3.2.7" + +eslint-plugin-import@2.28.1: + version "2.28.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.28.1.tgz#63b8b5b3c409bfc75ebaf8fb206b07ab435482c4" + integrity sha512-9I9hFlITvOV55alzoKBI+K9q74kv0iKMeY6av5+umsNwayt59fz692daGyjR+oStBQgx6nwR9rXldDev3Clw+A== + dependencies: + array-includes "^3.1.6" + array.prototype.findlastindex "^1.2.2" + array.prototype.flat "^1.3.1" + array.prototype.flatmap "^1.3.1" + debug "^3.2.7" + doctrine "^2.1.0" + eslint-import-resolver-node "^0.3.7" + eslint-module-utils "^2.8.0" + has "^1.0.3" + is-core-module "^2.13.0" + is-glob "^4.0.3" + minimatch "^3.1.2" + object.fromentries "^2.0.6" + object.groupby "^1.0.0" + object.values "^1.1.6" + semver "^6.3.1" + tsconfig-paths "^3.14.2" + +eslint-plugin-react@^7.33.2: + version "7.33.2" + resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.33.2.tgz#69ee09443ffc583927eafe86ffebb470ee737608" + integrity sha512-73QQMKALArI8/7xGLNI/3LylrEYrlKZSb5C9+q3OtOewTnMQi5cT+aE9E41sLCmli3I9PGGmD1yiZydyo4FEPw== + dependencies: + array-includes "^3.1.6" + array.prototype.flatmap "^1.3.1" + array.prototype.tosorted "^1.1.1" + doctrine "^2.1.0" + es-iterator-helpers "^1.0.12" + estraverse "^5.3.0" + jsx-ast-utils "^2.4.1 || ^3.0.0" + minimatch "^3.1.2" + object.entries "^1.1.6" + object.fromentries "^2.0.6" + object.hasown "^1.1.2" + object.values "^1.1.6" + prop-types "^15.8.1" + resolve "^2.0.0-next.4" + semver "^6.3.1" + string.prototype.matchall "^4.0.8" + eslint-scope@5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" @@ -1887,11 +2393,83 @@ eslint-scope@5.1.1: esrecurse "^4.3.0" estraverse "^4.1.1" +eslint-scope@^7.2.2: + version "7.2.2" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.2.tgz#deb4f92563390f32006894af62a22dba1c46423f" + integrity sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg== + dependencies: + esrecurse "^4.3.0" + estraverse "^5.2.0" + +eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3: + version "3.4.3" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" + integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== + +eslint@^8.50.0: + version "8.50.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.50.0.tgz#2ae6015fee0240fcd3f83e1e25df0287f487d6b2" + integrity sha512-FOnOGSuFuFLv/Sa+FDVRZl4GGVAAFFi8LecRsI5a1tMO5HIE8nCm4ivAlzt4dT3ol/PaaGC0rJEEXQmHJBGoOg== + dependencies: + "@eslint-community/eslint-utils" "^4.2.0" + "@eslint-community/regexpp" "^4.6.1" + "@eslint/eslintrc" "^2.1.2" + "@eslint/js" "8.50.0" + "@humanwhocodes/config-array" "^0.11.11" + "@humanwhocodes/module-importer" "^1.0.1" + "@nodelib/fs.walk" "^1.2.8" + ajv "^6.12.4" + chalk "^4.0.0" + cross-spawn "^7.0.2" + debug "^4.3.2" + doctrine "^3.0.0" + escape-string-regexp "^4.0.0" + eslint-scope "^7.2.2" + eslint-visitor-keys "^3.4.3" + espree "^9.6.1" + esquery "^1.4.2" + esutils "^2.0.2" + fast-deep-equal "^3.1.3" + file-entry-cache "^6.0.1" + find-up "^5.0.0" + glob-parent "^6.0.2" + globals "^13.19.0" + graphemer "^1.4.0" + ignore "^5.2.0" + imurmurhash "^0.1.4" + is-glob "^4.0.0" + is-path-inside "^3.0.3" + js-yaml "^4.1.0" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.4.1" + lodash.merge "^4.6.2" + minimatch "^3.1.2" + natural-compare "^1.4.0" + optionator "^0.9.3" + strip-ansi "^6.0.1" + text-table "^0.2.0" + +espree@^9.6.0, espree@^9.6.1: + version "9.6.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f" + integrity sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ== + dependencies: + acorn "^8.9.0" + acorn-jsx "^5.3.2" + eslint-visitor-keys "^3.4.1" + esprima@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== +esquery@^1.4.2: + version "1.5.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.5.0.tgz#6ce17738de8577694edd7361c57182ac8cb0db0b" + integrity sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg== + dependencies: + estraverse "^5.1.0" + esrecurse@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" @@ -1904,6 +2482,11 @@ estraverse@^4.1.1: resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13" integrity sha1-De4/7TH81GlhjOc0IJn8GvoL2xM= +estraverse@^5.1.0, estraverse@^5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" + integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== + estraverse@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.2.0.tgz#307df42547e6cc7324d3cf03c155d5cdb8c53880" @@ -1912,7 +2495,7 @@ estraverse@^5.2.0: esutils@^1.1.6: version "1.1.6" resolved "https://registry.yarnpkg.com/esutils/-/esutils-1.1.6.tgz#c01ccaa9ae4b897c6d0c3e210ae52f3c7a844375" - integrity sha1-wBzKqa5LiXxtDD4hCuUvPHqEQ3U= + integrity sha512-RG1ZkUT7iFJG9LSHr7KDuuMSlujfeTtMNIcInURxKAxhMtwQhI3NrQhz26gZQYlsYZQKzsnwtpKrFKj9K9Qu1A== esutils@^2.0.2: version "2.0.2" @@ -2080,16 +2663,39 @@ extsprintf@^1.2.0: resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= -fast-deep-equal@^3.1.1: +fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== +fast-glob@^3.2.9: + version "3.3.1" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.1.tgz#784b4e897340f3dbbef17413b3f11acf03c874c4" + integrity sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.2" + merge2 "^1.3.0" + micromatch "^4.0.4" + fast-json-stable-stringify@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== +fast-levenshtein@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== + +fastq@^1.6.0: + version "1.15.0" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.15.0.tgz#d04d07c6a2a68fe4599fea8d2e103a937fae6b3a" + integrity sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw== + dependencies: + reusify "^1.0.4" + faye-websocket@^0.10.0: version "0.10.0" resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.10.0.tgz#4e492f8d04dfb6f89003507f6edbf2d501e7c6f4" @@ -2111,6 +2717,13 @@ fd-slicer@~1.0.1: dependencies: pend "~1.2.0" +file-entry-cache@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" + integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== + dependencies: + flat-cache "^3.0.4" + fill-range@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" @@ -2169,6 +2782,14 @@ find-up@^4.0.0, find-up@^4.1.0: locate-path "^5.0.0" path-exists "^4.0.0" +find-up@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + find-versions@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/find-versions/-/find-versions-3.2.0.tgz#10297f98030a786829681690545ef659ed1d254e" @@ -2186,16 +2807,37 @@ findup-sync@3.0.0: micromatch "^3.0.4" resolve-dir "^1.0.1" +flat-cache@^3.0.4: + version "3.1.0" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.1.0.tgz#0e54ab4a1a60fe87e2946b6b00657f1c99e1af3f" + integrity sha512-OHx4Qwrrt0E4jEIcI5/Xb+f+QmJYNj2rrK8wiIdQOIrB9WrrJL8cjZvXdXuBTkkEwEqLycb5BeZDV1o2i9bTew== + dependencies: + flatted "^3.2.7" + keyv "^4.5.3" + rimraf "^3.0.2" + flatted@^3.2.4: version "3.2.5" resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.5.tgz#76c8584f4fc843db64702a6bd04ab7a8bd666da3" integrity sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg== +flatted@^3.2.7: + version "3.2.9" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.9.tgz#7eb4c67ca1ba34232ca9d2d93e9886e611ad7daf" + integrity sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ== + follow-redirects@^1.0.0: version "1.14.8" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.8.tgz#016996fb9a11a100566398b1c6839337d7bfa8fc" integrity sha512-1x0S9UVJHsQprFcEC/qnNzBLcIxsjAV905f/UkQxbclCsoTWlacCNOpQa/anodLl2uaEKFhfWOvM2Qg77+15zA== +for-each@^0.3.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" + integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw== + dependencies: + is-callable "^1.1.3" + for-in@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" @@ -2280,6 +2922,21 @@ function-bind@^1.1.1: resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== +function.prototype.name@^1.1.5, function.prototype.name@^1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.6.tgz#cdf315b7d90ee77a4c6ee216c3c3362da07533fd" + integrity sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + functions-have-names "^1.2.3" + +functions-have-names@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" + integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== + gauge@~2.7.3: version "2.7.4" resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" @@ -2318,6 +2975,16 @@ get-intrinsic@^1.0.2: has "^1.0.3" has-symbols "^1.0.3" +get-intrinsic@^1.1.1, get-intrinsic@^1.1.3, get-intrinsic@^1.2.0, get-intrinsic@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.1.tgz#d295644fed4505fc9cde952c37ee12b477a83d82" + integrity sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw== + dependencies: + function-bind "^1.1.1" + has "^1.0.3" + has-proto "^1.0.1" + has-symbols "^1.0.3" + get-stream@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" @@ -2332,6 +2999,14 @@ get-stream@^5.0.0: dependencies: pump "^3.0.0" +get-symbol-description@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.0.tgz#7fdb81c900101fbd564dd5f1a30af5aadc1e58d6" + integrity sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.1" + get-value@^2.0.3, get-value@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" @@ -2352,13 +3027,20 @@ glob-parent@^3.1.0: is-glob "^3.1.0" path-dirname "^1.0.0" -glob-parent@~5.1.2: +glob-parent@^5.1.2, glob-parent@~5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== dependencies: is-glob "^4.0.1" +glob-parent@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" + integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== + dependencies: + is-glob "^4.0.3" + glob-to-regexp@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" @@ -2376,7 +3058,7 @@ glob@7.1.6: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^7.0.3, glob@^7.1.1, glob@^7.1.3, glob@^7.1.7: +glob@^7.0.3, glob@^7.1.3, glob@^7.1.7: version "7.2.0" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== @@ -2388,6 +3070,18 @@ glob@^7.0.3, glob@^7.1.1, glob@^7.1.3, glob@^7.1.7: once "^1.3.0" path-is-absolute "^1.0.0" +glob@^7.1.1: + version "7.2.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" + integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.1.1" + once "^1.3.0" + path-is-absolute "^1.0.0" + global-modules@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-2.0.0.tgz#997605ad2345f27f51539bea26574421215c7780" @@ -2429,6 +3123,32 @@ globals@^11.1.0: resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== +globals@^13.19.0: + version "13.22.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.22.0.tgz#0c9fcb9c48a2494fbb5edbfee644285543eba9d8" + integrity sha512-H1Ddc/PbZHTDVJSnj8kWptIRSD6AM3pK+mKytuIVF4uoBV7rshFlhhvA58ceJ5wp3Er58w6zj7bykMpYXt3ETw== + dependencies: + type-fest "^0.20.2" + +globalthis@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.3.tgz#5852882a52b80dc301b0660273e1ed082f0b6ccf" + integrity sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA== + dependencies: + define-properties "^1.1.3" + +globby@^11.1.0: + version "11.1.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" + integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== + dependencies: + array-union "^2.1.0" + dir-glob "^3.0.1" + fast-glob "^3.2.9" + ignore "^5.2.0" + merge2 "^1.4.1" + slash "^3.0.0" + globby@^6.1.0: version "6.1.0" resolved "https://registry.yarnpkg.com/globby/-/globby-6.1.0.tgz#f5a6d70e8395e21c858fb0489d64df02424d506c" @@ -2440,6 +3160,13 @@ globby@^6.1.0: pify "^2.0.0" pinkie-promise "^2.0.0" +gopd@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" + integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== + dependencies: + get-intrinsic "^1.1.3" + graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9, graceful-fs@^4.2.0, graceful-fs@^4.2.6: version "4.2.9" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.9.tgz#041b05df45755e587a24942279b9d113146e1c96" @@ -2450,6 +3177,11 @@ graceful-fs@^4.2.10, graceful-fs@^4.2.4, graceful-fs@^4.2.9: resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== +graphemer@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" + integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== + handle-thing@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-2.0.0.tgz#0e039695ff50c93fc288557d696f3c1dc6776754" @@ -2480,6 +3212,11 @@ har-validator@~5.1.0: ajv "^6.5.5" har-schema "^2.0.0" +has-bigints@^1.0.1, has-bigints@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa" + integrity sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ== + has-flag@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" @@ -2490,11 +3227,30 @@ has-flag@^4.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== -has-symbols@^1.0.3: +has-property-descriptors@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz#610708600606d36961ed04c196193b6a607fa861" + integrity sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ== + dependencies: + get-intrinsic "^1.1.1" + +has-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.1.tgz#1885c1305538958aff469fef37937c22795408e0" + integrity sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg== + +has-symbols@^1.0.2, has-symbols@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== +has-tostringtag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.0.tgz#7e133818a7d394734f941e73c3d3f9291e658b25" + integrity sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ== + dependencies: + has-symbols "^1.0.2" + has-unicode@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" @@ -2674,6 +3430,11 @@ ignore@^5.1.4: resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.4.tgz#84b7b3dbe64552b6ef0eca99f6743dbec6d97adf" integrity sha512-MzbUSahkTW1u7JpKKjY7LCARd1fU5W2rLdxlM4kdkayuCwZImjkpluF9CM1aLewYJguPDqewLam18Y6AU69A8A== +ignore@^5.2.0, ignore@^5.2.4: + version "5.2.4" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.4.tgz#a291c0c6178ff1b960befe47fcdec301674a6324" + integrity sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ== + immutable@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.0.0.tgz#b86f78de6adef3608395efb269a91462797e2c23" @@ -2687,6 +3448,14 @@ import-fresh@^3.1.0: parent-module "^1.0.0" resolve-from "^4.0.0" +import-fresh@^3.2.1: + version "3.3.0" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" + integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + import-local@2.0.0, import-local@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/import-local/-/import-local-2.0.0.tgz#55070be38a5993cf18ef6db7e961f5bee5c5a09d" @@ -2695,6 +3464,11 @@ import-local@2.0.0, import-local@^2.0.0: pkg-dir "^3.0.0" resolve-cwd "^2.0.0" +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== + indexes-of@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607" @@ -2731,6 +3505,15 @@ internal-ip@^4.3.0: default-gateway "^4.2.0" ipaddr.js "^1.9.0" +internal-slot@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.5.tgz#f2a2ee21f668f8627a4667f309dc0f4fb6674986" + integrity sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ== + dependencies: + get-intrinsic "^1.2.0" + has "^1.0.3" + side-channel "^1.0.4" + interpret@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.2.0.tgz#d5061a6224be58e8083985f5014d844359576296" @@ -2780,6 +3563,15 @@ is-accessor-descriptor@^1.0.0: dependencies: kind-of "^6.0.0" +is-array-buffer@^3.0.1, is-array-buffer@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.2.tgz#f2653ced8412081638ecb0ebbd0c41c6e0aecbbe" + integrity sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.2.0" + is-typed-array "^1.1.10" + is-arrayish@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" @@ -2790,6 +3582,20 @@ is-arrayish@^0.3.1: resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03" integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ== +is-async-function@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-async-function/-/is-async-function-2.0.0.tgz#8e4418efd3e5d3a6ebb0164c05ef5afb69aa9646" + integrity sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA== + dependencies: + has-tostringtag "^1.0.0" + +is-bigint@^1.0.1: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3" + integrity sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg== + dependencies: + has-bigints "^1.0.1" + is-binary-path@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898" @@ -2804,11 +3610,31 @@ is-binary-path@~2.1.0: dependencies: binary-extensions "^2.0.0" +is-boolean-object@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.2.tgz#5c6dc200246dd9321ae4b885a114bb1f75f63719" + integrity sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + is-buffer@^1.1.5: version "1.1.6" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== +is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.7: + version "1.2.7" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" + integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== + +is-core-module@^2.13.0, is-core-module@^2.9.0: + version "2.13.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.0.tgz#bb52aa6e2cbd49a30c2ba68c42bf3435ba6072db" + integrity sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ== + dependencies: + has "^1.0.3" + is-data-descriptor@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" @@ -2823,6 +3649,13 @@ is-data-descriptor@^1.0.0: dependencies: kind-of "^6.0.0" +is-date-object@^1.0.1, is-date-object@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" + integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== + dependencies: + has-tostringtag "^1.0.0" + is-descriptor@^0.1.0: version "0.1.6" resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" @@ -2863,6 +3696,13 @@ is-extglob@^2.1.0, is-extglob@^2.1.1: resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= +is-finalizationregistry@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-finalizationregistry/-/is-finalizationregistry-1.0.2.tgz#c8749b65f17c133313e661b1289b95ad3dbd62e6" + integrity sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw== + dependencies: + call-bind "^1.0.2" + is-fullwidth-code-point@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" @@ -2880,6 +3720,13 @@ is-fullwidth-code-point@^3.0.0: resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== +is-generator-function@^1.0.10: + version "1.0.10" + resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.10.tgz#f1558baf1ac17e0deea7c0415c438351ff2b3c72" + integrity sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A== + dependencies: + has-tostringtag "^1.0.0" + is-glob@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a" @@ -2894,6 +3741,30 @@ is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1: dependencies: is-extglob "^2.1.1" +is-glob@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + +is-map@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.2.tgz#00922db8c9bf73e81b7a335827bc2a43f2b91127" + integrity sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg== + +is-negative-zero@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150" + integrity sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA== + +is-number-object@^1.0.4: + version "1.0.7" + resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.7.tgz#59d50ada4c45251784e9904f5246c742f07a42fc" + integrity sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ== + dependencies: + has-tostringtag "^1.0.0" + is-number@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" @@ -2925,6 +3796,11 @@ is-path-inside@^2.1.0: dependencies: path-is-inside "^1.0.2" +is-path-inside@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" + integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== + is-plain-object@^2.0.3, is-plain-object@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" @@ -2932,6 +3808,26 @@ is-plain-object@^2.0.3, is-plain-object@^2.0.4: dependencies: isobject "^3.0.1" +is-regex@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" + integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-set@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.2.tgz#90755fa4c2562dc1c5d4024760d6119b94ca18ec" + integrity sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g== + +is-shared-array-buffer@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz#8f259c573b60b6a32d4058a1a07430c0a7344c79" + integrity sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA== + dependencies: + call-bind "^1.0.2" + is-stream@^1.0.1, is-stream@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" @@ -2942,11 +3838,52 @@ is-stream@^2.0.0: resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.0.tgz#bde9c32680d6fae04129d6ac9d921ce7815f78e3" integrity sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw== +is-string@^1.0.5, is-string@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd" + integrity sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg== + dependencies: + has-tostringtag "^1.0.0" + +is-symbol@^1.0.2, is-symbol@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c" + integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg== + dependencies: + has-symbols "^1.0.2" + +is-typed-array@^1.1.10, is-typed-array@^1.1.12, is-typed-array@^1.1.9: + version "1.1.12" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.12.tgz#d0bab5686ef4a76f7a73097b95470ab199c57d4a" + integrity sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg== + dependencies: + which-typed-array "^1.1.11" + is-typedarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= +is-weakmap@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-weakmap/-/is-weakmap-2.0.1.tgz#5008b59bdc43b698201d18f62b37b2ca243e8cf2" + integrity sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA== + +is-weakref@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2" + integrity sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ== + dependencies: + call-bind "^1.0.2" + +is-weakset@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-weakset/-/is-weakset-2.0.2.tgz#4569d67a747a1ce5a994dfd4ef6dcea76e7c0a1d" + integrity sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.1" + is-windows@^1.0.1, is-windows@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" @@ -2967,13 +3904,18 @@ is-wsl@^2.1.0: isarray@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" - integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8= + integrity sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ== isarray@1.0.0, isarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= +isarray@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" + integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== + isbinaryfile@^4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/isbinaryfile/-/isbinaryfile-4.0.8.tgz#5d34b94865bd4946633ecc78a026fc76c5b11fcf" @@ -3049,6 +3991,17 @@ istanbul-reports@^3.0.2: html-escaper "^2.0.0" istanbul-lib-report "^3.0.0" +iterator.prototype@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/iterator.prototype/-/iterator.prototype-1.1.2.tgz#5e29c8924f01916cb9335f1ff80619dcff22b0c0" + integrity sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w== + dependencies: + define-properties "^1.2.1" + get-intrinsic "^1.2.1" + has-symbols "^1.0.3" + reflect.getprototypeof "^1.0.4" + set-function-name "^2.0.1" + jasmine-core@3.5.0, jasmine-core@^3.5.0: version "3.5.0" resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-3.5.0.tgz#132c23e645af96d85c8bca13c8758b18429fc1e4" @@ -3063,19 +4016,26 @@ jest-worker@^27.4.5: merge-stream "^2.0.0" supports-color "^8.0.0" -js-tokens@^4.0.0: +"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== js-yaml@^3.13.1: - version "3.13.1" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" - integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw== + version "3.14.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" + integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== dependencies: argparse "^1.0.7" esprima "^4.0.0" +js-yaml@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" + jsbn@~0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" @@ -3086,6 +4046,11 @@ jsesc@^2.5.1: resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== +json-buffer@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" + integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== + json-parse-better-errors@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" @@ -3106,6 +4071,11 @@ json-schema@0.2.3: resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== + json-stringify-safe@~5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" @@ -3128,6 +4098,13 @@ json5@^1.0.1: dependencies: minimist "^1.2.0" +json5@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.2.tgz#63d98d60f21b313b77c4d6da18bfa69d80e1d593" + integrity sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA== + dependencies: + minimist "^1.2.0" + json5@^2.1.2: version "2.1.3" resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.3.tgz#c9b0f7fa9233bfe5807fe66fcf3a5617ed597d43" @@ -3166,6 +4143,16 @@ jsprim@^1.2.2: json-schema "0.2.3" verror "1.10.0" +"jsx-ast-utils@^2.4.1 || ^3.0.0": + version "3.3.5" + resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz#4766bd05a8e2a11af222becd19e15575e52a853a" + integrity sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ== + dependencies: + array-includes "^3.1.6" + array.prototype.flat "^1.3.1" + object.assign "^4.1.4" + object.values "^1.1.6" + karma-chrome-launcher@3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/karma-chrome-launcher/-/karma-chrome-launcher-3.1.0.tgz#805a586799a4d05f4e54f72a204979f3f3066738" @@ -3257,6 +4244,13 @@ kew@^0.7.0: resolved "https://registry.yarnpkg.com/kew/-/kew-0.7.0.tgz#79d93d2d33363d6fdd2970b335d9141ad591d79b" integrity sha1-edk9LTM2PW/dKXCzNdkUGtWR15s= +keyv@^4.5.3: + version "4.5.3" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.3.tgz#00873d2b046df737963157bd04f294ca818c9c25" + integrity sha512-QCiSav9WaX1PgETJ+SpNnx2PRRapJ/oRSXM4VO5OGYGSjrxbKPVFVhB3l2OCbLCk329N8qyAtsJjSjvVBWzEug== + dependencies: + json-buffer "3.0.1" + killable@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/killable/-/killable-1.0.1.tgz#4c8ce441187a061c7474fb87ca08e2a638194892" @@ -3300,6 +4294,14 @@ lcid@^2.0.0: dependencies: invert-kv "^2.0.0" +levn@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" + integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== + dependencies: + prelude-ls "^1.2.1" + type-check "~0.4.0" + lines-and-columns@^1.1.6: version "1.1.6" resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00" @@ -3352,6 +4354,18 @@ locate-path@^5.0.0: dependencies: p-locate "^4.1.0" +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + +lodash.merge@^4.6.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" + integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== + lodash@^4.0.1, lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.21: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" @@ -3373,6 +4387,13 @@ loglevel@^1.6.6: resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.6.8.tgz#8a25fb75d092230ecd4457270d80b54e28011171" integrity sha512-bsU7+gc9AJ2SqpzxwU3+1fedl8zAntbtC5XYlt3s2j1hJcn2PsXSmgN8TaLG/J1/2mod4+cE/3vNL70/c1RNCA== +loose-envify@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" + integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== + dependencies: + js-tokens "^3.0.0 || ^4.0.0" + lru-cache@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" @@ -3470,6 +4491,11 @@ merge-stream@^2.0.0: resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== +merge2@^1.3.0, merge2@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" + integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== + methods@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" @@ -3502,6 +4528,14 @@ micromatch@^4.0.0: braces "^3.0.1" picomatch "^2.0.5" +micromatch@^4.0.4: + version "4.0.5" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" + integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== + dependencies: + braces "^3.0.2" + picomatch "^2.3.1" + mime-db@1.40.0, "mime-db@>= 1.40.0 < 2": version "1.40.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.40.0.tgz#a65057e998db090f732a68f6c276d387d4126c32" @@ -3558,7 +4592,7 @@ minimalistic-assert@^1.0.0: resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== -minimatch@^3.0.0, minimatch@^3.0.4: +minimatch@^3.0.0, minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== @@ -3580,6 +4614,11 @@ minimist@^1.2.5: resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== +minimist@^1.2.6: + version "1.2.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== + minipass@^2.6.0, minipass@^2.9.0: version "2.9.0" resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.9.0.tgz#e713762e7d3e32fed803115cf93e04bca9fcc9a6" @@ -3610,13 +4649,20 @@ mkdirp@0.5.1: dependencies: minimist "0.0.8" -mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@^0.5.5: +mkdirp@^0.5.1, mkdirp@^0.5.5: version "0.5.5" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== dependencies: minimist "^1.2.5" +mkdirp@^0.5.3: + version "0.5.6" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" + integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== + dependencies: + minimist "^1.2.6" + mri@^1.1.4: version "1.1.5" resolved "https://registry.yarnpkg.com/mri/-/mri-1.1.5.tgz#ce21dba2c69f74a9b7cf8a1ec62307e089e223e0" @@ -3683,6 +4729,11 @@ nanomatch@^1.2.9: snapdragon "^0.8.1" to-regex "^3.0.1" +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== + needle@^2.2.1: version "2.4.0" resolved "https://registry.yarnpkg.com/needle/-/needle-2.4.0.tgz#6833e74975c444642590e15a750288c5f939b57c" @@ -3805,7 +4856,7 @@ oauth-sign@~0.9.0: resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== -object-assign@^4, object-assign@^4.0.1, object-assign@^4.1.0: +object-assign@^4, object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= @@ -3819,11 +4870,21 @@ object-copy@^0.1.0: define-property "^0.2.5" kind-of "^3.0.3" +object-inspect@^1.12.3: + version "1.12.3" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.3.tgz#ba62dffd67ee256c8c086dfae69e016cd1f198b9" + integrity sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g== + object-inspect@^1.9.0: version "1.12.2" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.2.tgz#c0641f26394532f28ab8d796ab954e43c009a8ea" integrity sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ== +object-keys@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== + object-visit@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" @@ -3831,6 +4892,52 @@ object-visit@^1.0.0: dependencies: isobject "^3.0.0" +object.assign@^4.1.4: + version "4.1.4" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.4.tgz#9673c7c7c351ab8c4d0b516f4343ebf4dfb7799f" + integrity sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + has-symbols "^1.0.3" + object-keys "^1.1.1" + +object.entries@^1.1.6: + version "1.1.7" + resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.7.tgz#2b47760e2a2e3a752f39dd874655c61a7f03c131" + integrity sha512-jCBs/0plmPsOnrKAfFQXRG2NFjlhZgjjcBLSmTnEhU8U6vVTsVe8ANeQJCHTl3gSsI4J+0emOoCgoKlmQPMgmA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + +object.fromentries@^2.0.6: + version "2.0.7" + resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.7.tgz#71e95f441e9a0ea6baf682ecaaf37fa2a8d7e616" + integrity sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + +object.groupby@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/object.groupby/-/object.groupby-1.0.1.tgz#d41d9f3c8d6c778d9cbac86b4ee9f5af103152ee" + integrity sha512-HqaQtqLnp/8Bn4GL16cj+CUYbnpe1bh0TtEaWvybszDG4tgxCJuRpV8VGuvNaI1fAnI4lUJzDG55MXcOH4JZcQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + get-intrinsic "^1.2.1" + +object.hasown@^1.1.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/object.hasown/-/object.hasown-1.1.3.tgz#6a5f2897bb4d3668b8e79364f98ccf971bda55ae" + integrity sha512-fFI4VcYpRHvSLXxP7yiZOMAd331cPfd2p7PFDVbgUsYOfCT3tICVqXWngbjr4m49OvsBwUBQ6O2uQoJvy3RexA== + dependencies: + define-properties "^1.2.0" + es-abstract "^1.22.1" + object.pick@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" @@ -3838,6 +4945,15 @@ object.pick@^1.3.0: dependencies: isobject "^3.0.1" +object.values@^1.1.6: + version "1.1.7" + resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.7.tgz#617ed13272e7e1071b43973aa1655d9291b8442a" + integrity sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + obuf@^1.0.0, obuf@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e" @@ -3895,6 +5011,18 @@ opn@^5.5.0: dependencies: is-wsl "^1.1.0" +optionator@^0.9.3: + version "0.9.3" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.3.tgz#007397d44ed1872fdc6ed31360190f81814e2c64" + integrity sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg== + dependencies: + "@aashutoshrathi/word-wrap" "^1.2.3" + deep-is "^0.1.3" + fast-levenshtein "^2.0.6" + levn "^0.4.1" + prelude-ls "^1.2.1" + type-check "^0.4.0" + original@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/original/-/original-1.0.2.tgz#e442a61cffe1c5fd20a65f3261c26663b303f25f" @@ -3956,6 +5084,13 @@ p-limit@^2.0.0, p-limit@^2.2.0: dependencies: p-try "^2.0.0" +p-limit@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + p-locate@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" @@ -3970,6 +5105,13 @@ p-locate@^4.1.0: dependencies: p-limit "^2.2.0" +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + dependencies: + p-limit "^3.0.2" + p-map@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/p-map/-/p-map-2.1.0.tgz#310928feef9c9ecc65b68b17693018a665cea175" @@ -4059,6 +5201,11 @@ path-parse@^1.0.6: resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== +path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + path-to-regexp@0.1.7: version "0.1.7" resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" @@ -4119,6 +5266,11 @@ picomatch@^2.2.1: resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972" integrity sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw== +picomatch@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + pify@^2.0.0: version "2.3.0" resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" @@ -4236,6 +5388,11 @@ postcss@^7.0.14, postcss@^7.0.16, postcss@^7.0.27, postcss@^7.0.5, postcss@^7.0. picocolors "^0.2.1" source-map "^0.6.1" +prelude-ls@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" + integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== + prettier@2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.0.5.tgz#d6d56282455243f2f92cc1716692c08aa31522d4" @@ -4268,6 +5425,15 @@ progress@^1.1.8: resolved "https://registry.yarnpkg.com/progress/-/progress-1.1.8.tgz#e260c78f6161cdd9b0e56cc3e0a85de17c7a57be" integrity sha1-4mDHj2Fhzdmw5WzD4Khd4Xx6V74= +prop-types@^15.8.1: + version "15.8.1" + resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" + integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== + dependencies: + loose-envify "^1.4.0" + object-assign "^4.1.1" + react-is "^16.13.1" + proxy-addr@~2.0.7: version "2.0.7" resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" @@ -4336,6 +5502,11 @@ querystringify@^2.1.1: resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6" integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ== +queue-microtask@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" + integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== + randombytes@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" @@ -4368,6 +5539,11 @@ rc@^1.2.7: minimist "^1.2.0" strip-json-comments "~2.0.1" +react-is@^16.13.1: + version "16.13.1" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" + integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== + readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.2.2: version "2.3.6" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" @@ -4406,6 +5582,18 @@ readdirp@~3.6.0: dependencies: picomatch "^2.2.1" +reflect.getprototypeof@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/reflect.getprototypeof/-/reflect.getprototypeof-1.0.4.tgz#aaccbf41aca3821b87bb71d9dcbc7ad0ba50a3f3" + integrity sha512-ECkTw8TmJwW60lOTR+ZkODISW6RQ8+2CL3COqtiJKLd6MmB45hN51HprHFziKLGkAuTGQhBb91V8cy+KHlaCjw== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + get-intrinsic "^1.2.1" + globalthis "^1.0.3" + which-builtin-type "^1.1.3" + regenerator-runtime@^0.13.4: version "0.13.5" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz#d878a1d094b4306d10b9096484b33ebd55e26697" @@ -4419,6 +5607,15 @@ regex-not@^1.0.0, regex-not@^1.0.2: extend-shallow "^3.0.2" safe-regex "^1.1.0" +regexp.prototype.flags@^1.5.0, regexp.prototype.flags@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz#90ce989138db209f81492edd734183ce99f9677e" + integrity sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + set-function-name "^2.0.0" + remove-trailing-separator@^1.0.1: version "1.1.0" resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" @@ -4517,6 +5714,15 @@ resolve-url@^0.2.1: resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= +resolve@^1.22.4: + version "1.22.6" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.6.tgz#dd209739eca3aef739c626fea1b4f3c506195362" + integrity sha512-njhxM7mV12JfufShqGy3Rz8j11RPdLy4xi15UurGJeoHLfJpVXKdh3ueuOqbYUcDZnffr6X739JBo5LzyahEsw== + dependencies: + is-core-module "^2.13.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + resolve@^1.3.2: version "1.11.1" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.11.1.tgz#ea10d8110376982fef578df8fc30b9ac30a07a3e" @@ -4524,6 +5730,15 @@ resolve@^1.3.2: dependencies: path-parse "^1.0.6" +resolve@^2.0.0-next.4: + version "2.0.0-next.4" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-2.0.0-next.4.tgz#3d37a113d6429f496ec4752d2a2e58efb1fd4660" + integrity sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ== + dependencies: + is-core-module "^2.9.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + ret@~0.1.10: version "0.1.15" resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" @@ -4534,6 +5749,11 @@ retry@^0.12.0: resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" integrity sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs= +reusify@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" + integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== + rfdc@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.3.0.tgz#d0b7c441ab2720d05dc4cf26e01c89631d9da08b" @@ -4553,6 +5773,23 @@ rimraf@^2.6.1, rimraf@^2.6.3: dependencies: glob "^7.1.3" +run-parallel@^1.1.9: + version "1.2.0" + resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" + integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== + dependencies: + queue-microtask "^1.2.2" + +safe-array-concat@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.0.1.tgz#91686a63ce3adbea14d61b14c99572a8ff84754c" + integrity sha512-6XbUAseYE2KtOuGueyeobCySj9L4+66Tn6KQMOPQJrAJEowYKW/YR/MGJZl7FdydUdaFu4LYyDZjxf4/Nmo23Q== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.2.1" + has-symbols "^1.0.3" + isarray "^2.0.5" + safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" @@ -4563,6 +5800,15 @@ safe-buffer@5.2.1, safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.0, resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== +safe-regex-test@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.0.0.tgz#793b874d524eb3640d1873aad03596db2d4f2295" + integrity sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.3" + is-regex "^1.1.4" + safe-regex@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" @@ -4662,12 +5908,12 @@ semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.6.0: resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== -semver@^6.0.0, semver@^6.3.0: +semver@^6.0.0, semver@^6.3.0, semver@^6.3.1: version "6.3.1" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== -semver@^7.3.4: +semver@^7.3.4, semver@^7.5.4: version "7.5.4" resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== @@ -4728,6 +5974,15 @@ set-blocking@^2.0.0, set-blocking@~2.0.0: resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= +set-function-name@^2.0.0, set-function-name@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/set-function-name/-/set-function-name-2.0.1.tgz#12ce38b7954310b9f61faa12701620a0c882793a" + integrity sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA== + dependencies: + define-data-property "^1.0.1" + functions-have-names "^1.2.3" + has-property-descriptors "^1.0.0" + set-value@^2.0.0, set-value@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b" @@ -4967,7 +6222,7 @@ split-string@^3.0.1, split-string@^3.0.2: sprintf-js@~1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" - integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= + integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== sshpk@^1.7.0: version "1.16.1" @@ -5046,6 +6301,48 @@ string-width@^4.1.0, string-width@^4.2.0: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.0" +string.prototype.matchall@^4.0.8: + version "4.0.10" + resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.10.tgz#a1553eb532221d4180c51581d6072cd65d1ee100" + integrity sha512-rGXbGmOEosIQi6Qva94HUjgPs9vKW+dkG7Y8Q5O2OYkWL6wFaTRZO8zM4mhP94uX55wgyrXzfS2aGtGzUL7EJQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + get-intrinsic "^1.2.1" + has-symbols "^1.0.3" + internal-slot "^1.0.5" + regexp.prototype.flags "^1.5.0" + set-function-name "^2.0.0" + side-channel "^1.0.4" + +string.prototype.trim@^1.2.8: + version "1.2.8" + resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz#f9ac6f8af4bd55ddfa8895e6aea92a96395393bd" + integrity sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + +string.prototype.trimend@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz#1bb3afc5008661d73e2dc015cd4853732d6c471e" + integrity sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + +string.prototype.trimstart@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz#d4cdb44b83a4737ffbac2d406e405d43d0184298" + integrity sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + string_decoder@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.2.0.tgz#fe86e738b19544afe70469243b2a1ee9240eae8d" @@ -5088,6 +6385,18 @@ strip-ansi@^6.0.0: dependencies: ansi-regex "^5.0.0" +strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-bom@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" + integrity sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA== + strip-eof@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" @@ -5098,6 +6407,11 @@ strip-final-newline@^2.0.0: resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== +strip-json-comments@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== + strip-json-comments@~2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" @@ -5131,6 +6445,11 @@ supports-color@^8.0.0: dependencies: has-flag "^4.0.0" +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + tapable@^1.0.0: version "1.1.3" resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2" @@ -5175,6 +6494,11 @@ terser@^5.16.8: commander "^2.20.0" source-map-support "~0.5.20" +text-table@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== + throttleit@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/throttleit/-/throttleit-1.0.0.tgz#9e785836daf46743145a5984b6268d828528ac6c" @@ -5247,6 +6571,11 @@ tough-cookie@~2.4.3: psl "^1.1.24" punycode "^1.4.1" +ts-api-utils@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.0.3.tgz#f12c1c781d04427313dbac808f453f050e54a331" + integrity sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg== + ts-loader@9.4.4: version "9.4.4" resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-9.4.4.tgz#6ceaf4d58dcc6979f84125335904920884b7cee4" @@ -5257,17 +6586,27 @@ ts-loader@9.4.4: micromatch "^4.0.0" semver "^7.3.4" +tsconfig-paths@^3.14.2: + version "3.14.2" + resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz#6e32f1f79412decd261f92d633a9dc1cfa99f088" + integrity sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g== + dependencies: + "@types/json5" "^0.0.29" + json5 "^1.0.2" + minimist "^1.2.6" + strip-bom "^3.0.0" + tslib@1.9.0: version "1.9.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.0.tgz#e37a86fda8cbbaf23a057f473c9f4dc64e5fc2e8" integrity sha512-f/qGG2tUkrISBlQZEjEqoZ3B2+npJjIf04H1wuAv9iA8i04Icp+61KRXxFdha22670NJopsZCIjhC3SnjPRKrQ== -tslib@^1.10.0: - version "1.11.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.11.1.tgz#eb15d128827fbee2841549e171f45ed338ac7e35" - integrity sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA== +tslib@^1.10.0, tslib@^1.8.1: + version "1.14.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" + integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== -tslib@^1.8.1, tslib@^1.9.0: +tslib@^1.9.0: version "1.10.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a" integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ== @@ -5332,9 +6671,9 @@ tsutils@^2.29.0: tslib "^1.8.1" tsutils@^3.0.0: - version "3.14.0" - resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.14.0.tgz#bf8d5a7bae5369331fa0f2b0a5a10bd7f7396c77" - integrity sha512-SmzGbB0l+8I0QwsPgjooFRaRvHLBLNYM8SeQ0k6rtNDru5sCGeLJcZdwilNndN+GysuFjF5EIYgN8GfFG6UeUw== + version "3.21.0" + resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" + integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA== dependencies: tslib "^1.8.1" @@ -5350,6 +6689,18 @@ tweetnacl@^0.14.3, tweetnacl@~0.14.0: resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= +type-check@^0.4.0, type-check@~0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" + integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== + dependencies: + prelude-ls "^1.2.1" + +type-fest@^0.20.2: + version "0.20.2" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" + integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== + type-is@~1.6.18: version "1.6.18" resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" @@ -5358,6 +6709,45 @@ type-is@~1.6.18: media-typer "0.3.0" mime-types "~2.1.24" +typed-array-buffer@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz#18de3e7ed7974b0a729d3feecb94338d1472cd60" + integrity sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.2.1" + is-typed-array "^1.1.10" + +typed-array-byte-length@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz#d787a24a995711611fb2b87a4052799517b230d0" + integrity sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA== + dependencies: + call-bind "^1.0.2" + for-each "^0.3.3" + has-proto "^1.0.1" + is-typed-array "^1.1.10" + +typed-array-byte-offset@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz#cbbe89b51fdef9cd6aaf07ad4707340abbc4ea0b" + integrity sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg== + dependencies: + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + for-each "^0.3.3" + has-proto "^1.0.1" + is-typed-array "^1.1.10" + +typed-array-length@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.4.tgz#89d83785e5c4098bec72e08b319651f0eac9c1bb" + integrity sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng== + dependencies: + call-bind "^1.0.2" + for-each "^0.3.3" + is-typed-array "^1.1.9" + typedarray@^0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" @@ -5408,6 +6798,16 @@ uglify-js@^3.1.4: resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.14.2.tgz#d7dd6a46ca57214f54a2d0a43cad0f35db82ac99" integrity sha512-rtPMlmcO4agTUfz10CbgJ1k6UAoXM2gWb3GoMPPZB/+/Ackf8lNWk11K4rYi2D0apgoFRLtQOZhb+/iGNJq26A== +unbox-primitive@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e" + integrity sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw== + dependencies: + call-bind "^1.0.2" + has-bigints "^1.0.2" + has-symbols "^1.0.3" + which-boxed-primitive "^1.0.2" + union-value@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847" @@ -5686,6 +7086,45 @@ websocket-extensions@>=0.1.1: resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.3.tgz#5d2ff22977003ec687a4b87073dfbbac146ccf29" integrity sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg== +which-boxed-primitive@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" + integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg== + dependencies: + is-bigint "^1.0.1" + is-boolean-object "^1.1.0" + is-number-object "^1.0.4" + is-string "^1.0.5" + is-symbol "^1.0.3" + +which-builtin-type@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/which-builtin-type/-/which-builtin-type-1.1.3.tgz#b1b8443707cc58b6e9bf98d32110ff0c2cbd029b" + integrity sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw== + dependencies: + function.prototype.name "^1.1.5" + has-tostringtag "^1.0.0" + is-async-function "^2.0.0" + is-date-object "^1.0.5" + is-finalizationregistry "^1.0.2" + is-generator-function "^1.0.10" + is-regex "^1.1.4" + is-weakref "^1.0.2" + isarray "^2.0.5" + which-boxed-primitive "^1.0.2" + which-collection "^1.0.1" + which-typed-array "^1.1.9" + +which-collection@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/which-collection/-/which-collection-1.0.1.tgz#70eab71ebbbd2aefaf32f917082fc62cdcb70906" + integrity sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A== + dependencies: + is-map "^2.0.1" + is-set "^2.0.1" + is-weakmap "^2.0.1" + is-weakset "^2.0.1" + which-module@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" @@ -5696,6 +7135,17 @@ which-pm-runs@^1.0.0: resolved "https://registry.yarnpkg.com/which-pm-runs/-/which-pm-runs-1.0.0.tgz#670b3afbc552e0b55df6b7780ca74615f23ad1cb" integrity sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs= +which-typed-array@^1.1.11, which-typed-array@^1.1.9: + version "1.1.11" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.11.tgz#99d691f23c72aab6768680805a271b69761ed61a" + integrity sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew== + dependencies: + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + for-each "^0.3.3" + gopd "^1.0.1" + has-tostringtag "^1.0.0" + which@^1.2.1, which@^1.2.10, which@^1.2.14, which@^1.2.9, which@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" @@ -5867,3 +7317,8 @@ yauzl@2.4.1: integrity sha1-lSj0QtqxsihOWLQ3m7GU4i4MQAU= dependencies: fd-slicer "~1.0.1" + +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==