refactor(docs): move doc state change manager into docs package (#7017)

This commit is contained in:
wpxp123456
2026-06-05 16:09:33 +08:00
committed by GitHub
parent 2b15443097
commit d183f6c7a2
21 changed files with 510 additions and 200 deletions
@@ -16,10 +16,11 @@
/* eslint-disable ts/no-explicit-any */
import type { Ctor, Dependency, DependencyIdentifier, DocumentDataModel, IDocumentData, Nullable } from '@univerjs/core';
import type { DocumentSkeleton, IRender, IRenderContext, IRenderModule } from '@univerjs/engine-render';
import type { Dependency, DocumentDataModel, IDocumentData, Nullable } from '@univerjs/core';
import type { DocumentSkeleton, IRenderContext, IRenderModule } from '@univerjs/engine-render';
import {
BooleanNumber,
DisposableCollection,
DOCS_NORMAL_EDITOR_UNIT_ID_KEY,
ILogService,
Inject,
@@ -32,9 +33,9 @@ import {
Univer,
UniverInstanceType,
} from '@univerjs/core';
import { DocSelectionManagerService, DocSkeletonManagerService, DocStateEmitService } from '@univerjs/docs';
import { DocIMEInputManagerService, DocSelectionRenderService, DocStateChangeManagerService } from '@univerjs/docs-ui';
import { DocumentViewModel, IRenderManagerService } from '@univerjs/engine-render';
import { DocSelectionManagerService, DocSkeletonManagerService, DocStateChangeManagerService, DocStateEmitService } from '@univerjs/docs';
import { DocIMEInputManagerService, DocSelectionRenderService } from '@univerjs/docs-ui';
import { DocumentViewModel, IRenderManagerService, RenderManagerService } from '@univerjs/engine-render';
import { BehaviorSubject, takeUntil } from 'rxjs';
const DEFAULT_DOC_DATA: IDocumentData = {
@@ -82,7 +83,7 @@ export function createDocUiTestBed(docData?: IDocumentData, dependencies?: Depen
override onStarting(): void {
this._injector.get(IUndoRedoService);
if (!dependencies?.some((dependency) => Array.isArray(dependency) && dependency[0] === IRenderManagerService)) {
this._injector.add([IRenderManagerService, { useClass: MockRenderManagerService as unknown as Ctor<IRenderManagerService> }]);
this._injector.add([IRenderManagerService, { useClass: RenderManagerService }]);
}
this._injector.add([DocSelectionManagerService]);
this._injector.add([DocStateEmitService]);
@@ -116,6 +117,23 @@ export function createDocUiTestBed(docData?: IDocumentData, dependencies?: Depen
}, univerInstanceService);
injector.add([DocSkeletonManagerService, { useValue: fakeDocSkeletonManager as unknown as DocSkeletonManagerService }]);
const renderManagerService = get(IRenderManagerService);
if (renderManagerService.getRenderUnitById(doc.getUnitId()) == null) {
renderManagerService.addRender(doc.getUnitId(), {
unitId: doc.getUnitId(),
type: UniverInstanceType.UNIVER_DOC,
engine: new DisposableCollection() as any,
scene: new DisposableCollection() as any,
mainComponent: null as any,
components: new Map(),
isMainScene: true,
activated$: new BehaviorSubject(true),
with: injector.get.bind(injector),
activate: () => {},
deactivate: () => {},
isDisposed: () => false,
});
}
univerInstanceService.focusUnit(doc.getUnitId());
get(ILogService).setLogLevel(LogLevel.SILENT);
@@ -127,17 +145,6 @@ export function createDocUiTestBed(docData?: IDocumentData, dependencies?: Depen
};
}
class MockRenderManagerService implements Pick<IRenderManagerService, 'getRenderById'> {
constructor(@Inject(Injector) private readonly _injector: Injector) {}
getRenderById(): Nullable<IRender> {
return {
with: <T>(identifier: DependencyIdentifier<T>) => this._injector.get(identifier),
isDisposed: () => false,
} as unknown as IRender;
}
}
class MockDocSkeletonManagerService extends RxDisposable implements IRenderModule {
private _docViewModel!: DocumentViewModel;
private readonly _currentSkeleton$ = new BehaviorSubject<Nullable<DocumentSkeleton>>(null);
@@ -14,10 +14,11 @@
* limitations under the License.
*/
import type { DocumentDataModel, ICommand, IDocumentData, Injector } from '@univerjs/core';
import type { DependencyIdentifier, DocumentDataModel, ICommand, IDocumentData } from '@univerjs/core';
import {
awaitTime,
Direction,
DisposableCollection,
ICommandService,
IUniverInstanceService,
ObjectRelativeFromH,
@@ -30,7 +31,8 @@ import { DocContentInsertService, DocSelectionManagerService, DocSkeletonManager
import { DocDrawingController as CoreDocDrawingController, DocDrawingService, IDocDrawingService } from '@univerjs/docs-drawing';
import { DocSelectionRenderService } from '@univerjs/docs-ui';
import { DrawingManagerService, IDrawingManagerService } from '@univerjs/drawing';
import { DocumentEditArea, IRenderManagerService } from '@univerjs/engine-render';
import { DocumentEditArea, IRenderManagerService, RenderManagerService } from '@univerjs/engine-render';
import { BehaviorSubject } from 'rxjs';
import { afterEach, describe, expect, it, vi } from 'vitest';
import { createDocUiTestBed } from '../../../__tests__/create-doc-ui-test-bed';
import { DocDrawingAddRemoveController } from '../../../controllers/doc-drawing-notification.controller';
@@ -101,30 +103,38 @@ function createDrawingDocData(): IDocumentData {
function setupDrawingTestBed(docData: IDocumentData) {
const refreshControls = vi.fn();
let injector!: Injector;
const renderManagerService = {
getRenderById: () => ({
scene: {
getTransformerByCreate: () => ({
refreshControls,
}),
},
with: <T>(token: T) => {
if (token === DocSelectionRenderService) {
return {
getSegment: () => '',
} as T;
}
return injector.get(token as never);
},
}),
};
const testBed = createDocUiTestBed(docData, [
[IRenderManagerService, { useValue: renderManagerService }],
[IRenderManagerService, { useClass: RenderManagerService }],
]);
const { univer, get } = testBed;
injector = testBed.injector;
const injector = testBed.injector;
const scene = new DisposableCollection() as any;
scene.getTransformerByCreate = () => ({
refreshControls,
});
get(IRenderManagerService).addRender('test-doc', {
unitId: 'test-doc',
type: UniverInstanceType.UNIVER_DOC,
engine: new DisposableCollection() as any,
scene,
mainComponent: null as any,
components: new Map(),
isMainScene: true,
activated$: new BehaviorSubject(true),
with: <T>(token: DependencyIdentifier<T>) => {
if (token === DocSelectionRenderService) {
return {
getSegment: () => '',
} as T;
}
return injector.get(token);
},
activate: () => {},
deactivate: () => {},
isDisposed: () => false,
});
injector.add([DocDrawingService]);
injector.add([IDocDrawingService, { useClass: DocDrawingService }]);
@@ -16,10 +16,11 @@
/* eslint-disable ts/no-explicit-any */
import type { Ctor, Dependency, DependencyIdentifier, DocumentDataModel, IDocumentData, Nullable } from '@univerjs/core';
import type { DocumentSkeleton, IRender, IRenderContext, IRenderModule } from '@univerjs/engine-render';
import type { Dependency, DocumentDataModel, IDocumentData, Nullable } from '@univerjs/core';
import type { DocumentSkeleton, IRenderContext, IRenderModule } from '@univerjs/engine-render';
import {
BooleanNumber,
DisposableCollection,
DOCS_NORMAL_EDITOR_UNIT_ID_KEY,
ILogService,
Inject,
@@ -32,9 +33,9 @@ import {
Univer,
UniverInstanceType,
} from '@univerjs/core';
import { DocSelectionManagerService, DocSkeletonManagerService, DocStateEmitService } from '@univerjs/docs';
import { DocIMEInputManagerService, DocSelectionRenderService, DocStateChangeManagerService } from '@univerjs/docs-ui';
import { DocumentViewModel, IRenderManagerService } from '@univerjs/engine-render';
import { DocSelectionManagerService, DocSkeletonManagerService, DocStateChangeManagerService, DocStateEmitService } from '@univerjs/docs';
import { DocIMEInputManagerService, DocSelectionRenderService } from '@univerjs/docs-ui';
import { DocumentViewModel, IRenderManagerService, RenderManagerService } from '@univerjs/engine-render';
import { BehaviorSubject, takeUntil } from 'rxjs';
const DEFAULT_DOC_DATA: IDocumentData = {
@@ -82,7 +83,7 @@ export function createDocUiTestBed(docData?: IDocumentData, dependencies?: Depen
override onStarting(): void {
this._injector.get(IUndoRedoService);
if (!dependencies?.some((dependency) => Array.isArray(dependency) && dependency[0] === IRenderManagerService)) {
this._injector.add([IRenderManagerService, { useClass: MockRenderManagerService as unknown as Ctor<IRenderManagerService> }]);
this._injector.add([IRenderManagerService, { useClass: RenderManagerService }]);
}
this._injector.add([DocSelectionManagerService]);
this._injector.add([DocStateEmitService]);
@@ -116,6 +117,23 @@ export function createDocUiTestBed(docData?: IDocumentData, dependencies?: Depen
}, univerInstanceService);
injector.add([DocSkeletonManagerService, { useValue: fakeDocSkeletonManager as unknown as DocSkeletonManagerService }]);
const renderManagerService = get(IRenderManagerService);
if (renderManagerService.getRenderUnitById(doc.getUnitId()) == null) {
renderManagerService.addRender(doc.getUnitId(), {
unitId: doc.getUnitId(),
type: UniverInstanceType.UNIVER_DOC,
engine: new DisposableCollection() as any,
scene: new DisposableCollection() as any,
mainComponent: null as any,
components: new Map(),
isMainScene: true,
activated$: new BehaviorSubject(true),
with: injector.get.bind(injector),
activate: () => {},
deactivate: () => {},
isDisposed: () => false,
});
}
univerInstanceService.focusUnit(doc.getUnitId());
get(ILogService).setLogLevel(LogLevel.SILENT);
@@ -127,17 +145,6 @@ export function createDocUiTestBed(docData?: IDocumentData, dependencies?: Depen
};
}
class MockRenderManagerService implements Pick<IRenderManagerService, 'getRenderById'> {
constructor(@Inject(Injector) private readonly _injector: Injector) {}
getRenderById(): Nullable<IRender> {
return {
with: <T>(identifier: DependencyIdentifier<T>) => this._injector.get(identifier),
isDisposed: () => false,
} as unknown as IRender;
}
}
class MockDocSkeletonManagerService extends RxDisposable implements IRenderModule {
private _docViewModel!: DocumentViewModel;
private readonly _currentSkeleton$ = new BehaviorSubject<Nullable<DocumentSkeleton>>(null);
@@ -16,10 +16,11 @@
/* eslint-disable ts/no-explicit-any */
import type { Ctor, Dependency, DependencyIdentifier, DocumentDataModel, IDocumentData, Nullable } from '@univerjs/core';
import type { DocumentSkeleton, IRender, IRenderContext, IRenderModule } from '@univerjs/engine-render';
import type { Dependency, DocumentDataModel, IDocumentData, Nullable } from '@univerjs/core';
import type { DocumentSkeleton, IRenderContext, IRenderModule } from '@univerjs/engine-render';
import {
BooleanNumber,
DisposableCollection,
DOCS_NORMAL_EDITOR_UNIT_ID_KEY,
ILogService,
Inject,
@@ -32,9 +33,9 @@ import {
Univer,
UniverInstanceType,
} from '@univerjs/core';
import { DocSelectionManagerService, DocSkeletonManagerService, DocStateEmitService } from '@univerjs/docs';
import { DocIMEInputManagerService, DocSelectionRenderService, DocStateChangeManagerService } from '@univerjs/docs-ui';
import { DocumentViewModel, IRenderManagerService } from '@univerjs/engine-render';
import { DocSelectionManagerService, DocSkeletonManagerService, DocStateChangeManagerService, DocStateEmitService } from '@univerjs/docs';
import { DocIMEInputManagerService, DocSelectionRenderService } from '@univerjs/docs-ui';
import { DocumentViewModel, IRenderManagerService, RenderManagerService } from '@univerjs/engine-render';
import { BehaviorSubject, takeUntil } from 'rxjs';
const DEFAULT_DOC_DATA: IDocumentData = {
@@ -82,7 +83,7 @@ export function createDocUiTestBed(docData?: IDocumentData, dependencies?: Depen
override onStarting(): void {
this._injector.get(IUndoRedoService);
if (!dependencies?.some((dependency) => Array.isArray(dependency) && dependency[0] === IRenderManagerService)) {
this._injector.add([IRenderManagerService, { useClass: MockRenderManagerService as unknown as Ctor<IRenderManagerService> }]);
this._injector.add([IRenderManagerService, { useClass: RenderManagerService }]);
}
this._injector.add([DocSelectionManagerService]);
this._injector.add([DocStateEmitService]);
@@ -116,6 +117,23 @@ export function createDocUiTestBed(docData?: IDocumentData, dependencies?: Depen
}, univerInstanceService);
injector.add([DocSkeletonManagerService, { useValue: fakeDocSkeletonManager as unknown as DocSkeletonManagerService }]);
const renderManagerService = get(IRenderManagerService);
if (renderManagerService.getRenderUnitById(doc.getUnitId()) == null) {
renderManagerService.addRender(doc.getUnitId(), {
unitId: doc.getUnitId(),
type: UniverInstanceType.UNIVER_DOC,
engine: new DisposableCollection() as any,
scene: new DisposableCollection() as any,
mainComponent: null as any,
components: new Map(),
isMainScene: true,
activated$: new BehaviorSubject(true),
with: injector.get.bind(injector),
activate: () => {},
deactivate: () => {},
isDisposed: () => false,
});
}
univerInstanceService.focusUnit(doc.getUnitId());
get(ILogService).setLogLevel(LogLevel.SILENT);
@@ -127,17 +145,6 @@ export function createDocUiTestBed(docData?: IDocumentData, dependencies?: Depen
};
}
class MockRenderManagerService implements Pick<IRenderManagerService, 'getRenderById'> {
constructor(@Inject(Injector) private readonly _injector: Injector) {}
getRenderById(): Nullable<IRender> {
return {
with: <T>(identifier: DependencyIdentifier<T>) => this._injector.get(identifier),
isDisposed: () => false,
} as unknown as IRender;
}
}
class MockDocSkeletonManagerService extends RxDisposable implements IRenderModule {
private _docViewModel!: DocumentViewModel;
private readonly _currentSkeleton$ = new BehaviorSubject<Nullable<DocumentSkeleton>>(null);
@@ -16,10 +16,11 @@
/* eslint-disable ts/no-explicit-any */
import type { Ctor, Dependency, DependencyIdentifier, DocumentDataModel, IDocumentData, Nullable } from '@univerjs/core';
import type { DocumentSkeleton, IRender, IRenderContext, IRenderModule } from '@univerjs/engine-render';
import type { Dependency, DocumentDataModel, IDocumentData, Nullable } from '@univerjs/core';
import type { DocumentSkeleton, IRenderContext, IRenderModule } from '@univerjs/engine-render';
import {
BooleanNumber,
DisposableCollection,
DOCS_NORMAL_EDITOR_UNIT_ID_KEY,
ILogService,
Inject,
@@ -32,12 +33,11 @@ import {
Univer,
UniverInstanceType,
} from '@univerjs/core';
import { DocSelectionManagerService, DocSkeletonManagerService, DocStateEmitService } from '@univerjs/docs';
import { DocumentViewModel, IRenderManagerService } from '@univerjs/engine-render';
import { DocSelectionManagerService, DocSkeletonManagerService, DocStateChangeManagerService, DocStateEmitService } from '@univerjs/docs';
import { DocumentViewModel, IRenderManagerService, RenderManagerService } from '@univerjs/engine-render';
import { BehaviorSubject, takeUntil } from 'rxjs';
import { DocIMEInputManagerService } from '../../../services/doc-ime-input-manager.service';
import { DocMenuStyleService } from '../../../services/doc-menu-style.service';
import { DocStateChangeManagerService } from '../../../services/doc-state-change-manager.service';
import { DocSelectionRenderService } from '../../../services/selection/doc-selection-render.service';
const TEST_DOCUMENT_DATA_EN: IDocumentData = {
@@ -114,7 +114,7 @@ export function createCommandTestBed(docData?: IDocumentData, dependencies?: Dep
const injector = this._injector;
injector.get(IUndoRedoService);
injector.add([IRenderManagerService, { useClass: MockRenderManagerService as unknown as Ctor<IRenderManagerService> }]);
injector.add([IRenderManagerService, { useClass: RenderManagerService }]);
injector.add([DocSelectionManagerService]);
injector.add([DocMenuStyleService]);
@@ -155,6 +155,23 @@ export function createCommandTestBed(docData?: IDocumentData, dependencies?: Dep
}, univerInstanceService);
injector.add([DocSkeletonManagerService, { useValue: fakeDocSkeletonManager as unknown as DocSkeletonManagerService }]);
const renderManagerService = get(IRenderManagerService);
if (renderManagerService.getRenderUnitById(doc.getUnitId()) == null) {
renderManagerService.addRender(doc.getUnitId(), {
unitId: doc.getUnitId(),
type: UniverInstanceType.UNIVER_DOC,
engine: new DisposableCollection() as any,
scene: new DisposableCollection() as any,
mainComponent: null as any,
components: new Map(),
isMainScene: true,
activated$: new BehaviorSubject(true),
with: injector.get.bind(injector),
activate: () => {},
deactivate: () => {},
isDisposed: () => false,
});
}
univerInstanceService.focusUnit('test-doc');
@@ -169,21 +186,6 @@ export function createCommandTestBed(docData?: IDocumentData, dependencies?: Dep
};
}
// These services are for document build and manage doc skeletons.
export class MockRenderManagerService implements Pick<IRenderManagerService, 'getRenderById'> {
constructor(
@Inject(Injector) private readonly _injector: Injector
) { }
getRenderById(_unitId: string): Nullable<IRender> {
return {
with: <T>(identifier: DependencyIdentifier<T>) => this._injector.get(identifier),
isDisposed: () => false,
} as unknown as IRender;
}
}
export class MockDocSkeletonManagerService extends RxDisposable implements IRenderModule {
private _docViewModel: DocumentViewModel;
@@ -27,8 +27,8 @@ import {
UniverInstanceType,
} from '@univerjs/core';
import { FUniver } from '@univerjs/core/facade';
import { DocSelectionManagerService, DocSkeletonManagerService, DocStateEmitService } from '@univerjs/docs';
import { DocIMEInputManagerService, DocsRenderService, DocStateChangeManagerService } from '@univerjs/docs-ui';
import { DocSelectionManagerService, DocSkeletonManagerService, DocStateChangeManagerService, DocStateEmitService } from '@univerjs/docs';
import { DocIMEInputManagerService, DocsRenderService } from '@univerjs/docs-ui';
import enUS from '@univerjs/docs-ui/locale/en-US';
import zhCN from '@univerjs/docs-ui/locale/zh-CN';
import { DumbCanvasColorService, ICanvasColorService, IRenderManagerService, RenderManagerService } from '@univerjs/engine-render';
@@ -71,7 +71,7 @@ describe('Test FDocument', () => {
startOffset: 6,
})).resolves.toBe(true);
expect(document.save().body?.dataStream).toBe('Hello,Line 1\r\nLine 2\r\n\r\n');
expect(document.save().body?.dataStream).toBe('Hello,Line 1\rLine 2\r\r\n');
});
it('throws when appending text to a document without a body', () => {
-5
View File
@@ -77,7 +77,6 @@ export { SetParagraphNamedStyleCommand } from './commands/commands/set-heading.c
export { SwitchDocModeCommand } from './commands/commands/switch-doc-mode.command';
export { CreateDocTableCommand, type ICreateDocTableCommandParams } from './commands/commands/table/doc-table-create.command';
export { DocTableDeleteColumnsCommand, DocTableDeleteRowsCommand, DocTableDeleteTableCommand } from './commands/commands/table/doc-table-delete.command';
export type {
IDocTableDeleteColumnsCommandParams,
IDocTableDeleteRowsCommandParams,
@@ -102,7 +101,6 @@ export {
export type { IDocTableTabCommandParams } from './commands/commands/table/doc-table-tab.command';
export { DocTableTabCommand } from './commands/commands/table/doc-table-tab.command';
export { genTableSource, getEmptyTableCell, getEmptyTableRow, getTableColumn } from './commands/commands/table/table';
export { DocCreateTableOperation } from './commands/operations/doc-create-table.operation';
export { type IMoveCursorOperationParams, MoveSelectionOperation } from './commands/operations/doc-cursor.operation';
export { MoveCursorOperation } from './commands/operations/doc-cursor.operation';
@@ -118,7 +116,6 @@ export { menuSchema as DocsUIMenuSchema } from './menu/schema';
export { UniverDocsUIPlugin } from './plugin';
export * from './services';
export { IDocClipboardService } from './services/clipboard/clipboard.service';
export { convertBodyToHtml } from './services/clipboard/udm-to-html/convertor';
export { DocAutoFormatService } from './services/doc-auto-format.service';
export { DocEventManagerService, type IBulletBound } from './services/doc-event-manager.service';
@@ -126,14 +123,12 @@ export { DocIMEInputManagerService } from './services/doc-ime-input-manager.serv
export { DocParagraphMenuService } from './services/doc-paragraph-menu.service';
export { calcDocRangePositions, DocCanvasPopManagerService } from './services/doc-popup-manager.service';
export { DocPrintInterceptorService, type IDocPrintComponentContext, type IDocPrintContext } from './services/doc-print-interceptor.service';
export { DocStateChangeManagerService } from './services/doc-state-change-manager.service';
export { DocsRenderService } from './services/docs-render.service';
export { Editor } from './services/editor/editor';
export { EditorService, IEditorService } from './services/editor/editor-manager.service';
export { isInSameTableCell, isValidRectRange, NodePositionConvertToRectRange } from './services/selection/convert-rect-range';
export { NodePositionConvertToCursor } from './services/selection/convert-text-range';
export { getOneTextSelectionRange } from './services/selection/convert-text-range';
export type { IEditorInputConfig } from './services/selection/doc-selection-render.service';
export { DocSelectionRenderService } from './services/selection/doc-selection-render.service';
export type { IDocRange } from './services/selection/range-interface';
+3 -5
View File
@@ -30,7 +30,7 @@ import {
touchDependencies,
UniverInstanceType,
} from '@univerjs/core';
import { DocInterceptorService, DocSkeletonManagerService } from '@univerjs/docs';
import { DocInterceptorService, DocSkeletonManagerService, IDocStateChangeInterceptorService } from '@univerjs/docs';
import { IRenderManagerService, UniverRenderEnginePlugin } from '@univerjs/engine-render';
import { IShortcutService } from '@univerjs/ui';
import pkg from '../package.json';
@@ -117,12 +117,12 @@ import { DocClipboardService, IDocClipboardService } from './services/clipboard/
import { DocAutoFormatService } from './services/doc-auto-format.service';
import { DocEventManagerService } from './services/doc-event-manager.service';
import { DocIMEInputManagerService } from './services/doc-ime-input-manager.service';
import { DocIMEStateChangeInterceptorService } from './services/doc-ime-state-change-interceptor.service';
import { DocMenuStyleService } from './services/doc-menu-style.service';
import { DocPageLayoutService } from './services/doc-page-layout.service';
import { DocParagraphMenuService } from './services/doc-paragraph-menu.service';
import { DocCanvasPopManagerService } from './services/doc-popup-manager.service';
import { DocPrintInterceptorService } from './services/doc-print-interceptor.service';
import { DocStateChangeManagerService } from './services/doc-state-change-manager.service';
import { DocsRenderService } from './services/docs-render.service';
import { EditorService, IEditorService } from './services/editor/editor-manager.service';
import { DocFloatMenuService } from './services/float-menu.service';
@@ -179,9 +179,7 @@ export class UniverDocsUIPlugin extends Plugin {
this._markDocAsFocused();
touchDependencies(this._injector, [
[DocStateChangeManagerService],
[DocsRenderService],
]);
}
@@ -329,7 +327,7 @@ export class UniverDocsUIPlugin extends Plugin {
[IDocClipboardService, { useClass: DocClipboardService }],
[DocCanvasPopManagerService],
[DocsRenderService],
[DocStateChangeManagerService],
[IDocStateChangeInterceptorService, { useClass: DocIMEStateChangeInterceptorService }],
[DocAutoFormatService],
[DocMenuStyleService],
@@ -0,0 +1,61 @@
/**
* Copyright 2023-present DreamNum Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type { Nullable } from '@univerjs/core';
import type { IDocStateChangeInfo, IDocStateChangeInterceptorService } from '@univerjs/docs';
import { IRenderManagerService } from '@univerjs/engine-render';
import { DocIMEInputManagerService } from './doc-ime-input-manager.service';
export class DocIMEStateChangeInterceptorService implements IDocStateChangeInterceptorService {
constructor(
@IRenderManagerService private readonly _renderManagerService: IRenderManagerService
) {}
transformChangeStateInfo(changeStateInfo: IDocStateChangeInfo): Nullable<IDocStateChangeInfo> {
const { isCompositionEnd, isSync, syncer } = changeStateInfo;
if (!isCompositionEnd) {
return changeStateInfo;
}
const imeInputManagerService = this._renderManagerService.getRenderUnitById(isSync ? syncer! : changeStateInfo.unitId)?.with(DocIMEInputManagerService);
if (imeInputManagerService == null) {
return null;
}
const historyParams = imeInputManagerService.fetchComposedUndoRedoMutationParams();
if (historyParams == null) {
throw new Error('historyParams is null in RichTextEditingMutation');
}
const { undoMutationParams, redoMutationParams, previousActiveRange } = historyParams;
return {
...changeStateInfo,
redoState: {
...changeStateInfo.redoState,
actions: redoMutationParams.actions,
},
undoState: {
...changeStateInfo.undoState,
actions: undoMutationParams.actions,
textRanges: [previousActiveRange],
},
};
}
}
@@ -19,6 +19,7 @@ import type { IRenderContext } from '@univerjs/engine-render';
import {
awaitTime,
CustomRangeType,
DisposableCollection,
ICommandService,
IUniverInstanceService,
LifecycleService,
@@ -27,7 +28,8 @@ import {
Univer,
UniverInstanceType,
} from '@univerjs/core';
import { IRenderManagerService } from '@univerjs/engine-render';
import { IRenderManagerService, RenderManagerService } from '@univerjs/engine-render';
import { BehaviorSubject } from 'rxjs';
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
import { DocsRenameMutation } from '../commands/mutations/docs-rename.mutation';
import { SetTextSelectionsOperation } from '../commands/operations/text-selection.operation';
@@ -36,6 +38,7 @@ import { DocInterceptorService } from '../services/doc-interceptor/doc-intercept
import { DOC_INTERCEPTOR_POINT } from '../services/doc-interceptor/interceptor-const';
import { DocSelectionManagerService } from '../services/doc-selection-manager.service';
import { DocSkeletonManagerService } from '../services/doc-skeleton-manager.service';
import { DocStateChangeManagerService } from '../services/doc-state-change-manager.service';
import { DocStateEmitService } from '../services/doc-state-emit.service';
import { DocViewModelManagerService } from '../services/doc-view-model-manager.service';
import { addCustomRangeBySelectionFactory, deleteCustomRangeFactory } from '../utils/custom-range-factory';
@@ -57,15 +60,21 @@ function registerRenderManagerForDoc(
univerInstanceService
);
injector.add([IRenderManagerService, {
useValue: {
getRenderById: (unitId: string) => unitId === doc.getUnitId()
? ({
with: () => skeletonManager,
} as never)
: null,
} as never,
}]);
injector.add([IRenderManagerService, { useClass: RenderManagerService }]);
injector.get(IRenderManagerService).addRender(doc.getUnitId(), {
unitId: doc.getUnitId(),
type: UniverInstanceType.UNIVER_DOC,
engine: new DisposableCollection() as never,
scene: new DisposableCollection() as never,
mainComponent: null,
components: new Map(),
isMainScene: true,
activated$: new BehaviorSubject(true),
with: <T>() => skeletonManager as T,
activate: () => {},
deactivate: () => {},
isDisposed: () => false,
});
return skeletonManager;
}
@@ -255,6 +264,64 @@ describe('docs integration', () => {
skeletonManager.dispose();
});
it('emits composed doc state changes from the docs package without docs-ui', async () => {
univer.registerPlugin(UniverDocsPlugin);
const doc = univer.createUnit<IDocumentData, DocumentDataModel>(
UniverInstanceType.UNIVER_DOC,
createTestDocData('doc-state-change')
);
const univerInstanceService = injector.get(IUniverInstanceService);
univerInstanceService.focusUnit(doc.getUnitId());
const skeletonManager = registerRenderManagerForDoc(injector, doc);
const selectionManager = injector.get(DocSelectionManagerService);
selectionManager.__TEST_ONLY_setCurrentSelection({
unitId: doc.getUnitId(),
subUnitId: doc.getUnitId(),
});
selectionManager.__TEST_ONLY_add([{
startOffset: 5,
endOffset: 5,
collapsed: true,
isActive: true,
segmentId: '',
style: null as never,
}]);
const docStateChangeManagerService = injector.get(DocStateChangeManagerService);
const stateChanges: unknown[] = [];
const sub = docStateChangeManagerService.docStateChange$.subscribe((value) => value && stateChanges.push(value));
const mutation = replaceSelectionFactory(injector as never, {
unitId: doc.getUnitId(),
body: {
dataStream: ' from docs',
},
});
expect(mutation).not.toBe(false);
if (mutation === false) {
throw new Error('Expected replaceSelectionFactory to create a mutation.');
}
const commandService = injector.get(ICommandService);
const ok = await commandService.executeCommand(mutation.id, {
...mutation.params,
debounce: false,
trigger: 'doc-state-change-spec' as never,
});
await awaitTime(0);
expect(ok).toBeTruthy();
expect(stateChanges).toHaveLength(1);
expect(doc.getBody()?.dataStream).toBe('Hello from docs\r\n');
sub.unsubscribe();
skeletonManager.dispose();
});
it('adds and removes custom ranges through the same mutation pipeline', async () => {
univer.registerPlugin(UniverDocsPlugin);
const doc = univer.createUnit<IDocumentData, DocumentDataModel>(
@@ -0,0 +1,67 @@
/**
* Copyright 2023-present DreamNum Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { describe, expect, it } from 'vitest';
import {
buildPlainTextInsertBody,
getNormalizedPlainTextCursorOffset,
getRemovedLeadingParagraphBreakLength,
} from '../facade/utils';
describe('facade utils', () => {
it('removes a leading paragraph break when plain text is inserted at the document start', () => {
const body = buildPlainTextInsertBody('\r\nHello', { removeLeadingParagraphBreak: true });
expect(body).toEqual({
dataStream: 'Hello',
customDecorations: [],
customRanges: [],
textRuns: [],
});
});
it('keeps a leading paragraph break when the caller does not remove it', () => {
const body = buildPlainTextInsertBody('\r\nHello');
expect(body.dataStream).toBe('\rHello');
expect(body.paragraphs).toEqual([{ startIndex: 0 }]);
});
it('normalizes plain text line feeds and adds paragraph metadata', () => {
const body = buildPlainTextInsertBody('Hello\nWorld\rAgain');
expect(body.dataStream).toBe('Hello\rWorld\rAgain');
expect(body.paragraphs).toEqual([{ startIndex: 5 }, { startIndex: 11 }]);
});
it('adds paragraph metadata for a trailing paragraph break', () => {
const body = buildPlainTextInsertBody('Hello\n');
expect(body.dataStream).toBe('Hello\r');
expect(body.paragraphs).toEqual([{ startIndex: 5 }]);
});
it('reports removed leading paragraph break length for cursor offsets', () => {
expect(getRemovedLeadingParagraphBreakLength('\nHello', true)).toBe(1);
expect(getRemovedLeadingParagraphBreakLength('\r\nHello', true)).toBe(1);
expect(getRemovedLeadingParagraphBreakLength('\r\nHello')).toBe(0);
});
it('normalizes cursor offsets with collapsed paragraph breaks', () => {
expect(getNormalizedPlainTextCursorOffset('Hello\r\n', 'Hello\r\n'.length)).toBe('Hello\r'.length);
expect(getNormalizedPlainTextCursorOffset('\r\nHello', '\r\nHello'.length, true)).toBe('Hello'.length);
});
});
@@ -14,10 +14,10 @@
* limitations under the License.
*/
import type { IMutation, IMutationCommonParams, JSONXActions, Nullable } from '@univerjs/core';
import type { DocumentDataModel, IExecutionOptions, IMutation, IMutationCommonParams, JSONXActions, Nullable } from '@univerjs/core';
import type { ITextRangeWithStyle } from '@univerjs/engine-render';
import type { IDocStateChangeInfo } from '../../services/doc-state-emit.service';
import { CommandType, IUniverInstanceService, JSONX } from '@univerjs/core';
import { CommandType, IUniverInstanceService, JSONX, UniverInstanceType } from '@univerjs/core';
import { IRenderManagerService } from '@univerjs/engine-render';
import { DocSelectionManagerService } from '../../services/doc-selection-manager.service';
import { DocSkeletonManagerService } from '../../services/doc-skeleton-manager.service';
@@ -53,7 +53,7 @@ export const RichTextEditingMutation: IMutation<IRichTextEditingMutationParams,
type: CommandType.MUTATION,
// eslint-disable-next-line max-lines-per-function
handler: (accessor, params) => {
handler: (accessor, params, options?: IExecutionOptions) => {
const {
unitId,
segmentId = '',
@@ -66,17 +66,18 @@ export const RichTextEditingMutation: IMutation<IRichTextEditingMutationParams,
noNeedSetTextRange,
debounce,
isEditing = true,
isSync,
isSync: paramsIsSync,
syncer,
} = params;
const isSync = paramsIsSync || options?.fromCollab || options?.fromChangeset;
const univerInstanceService = accessor.get(IUniverInstanceService);
const renderManagerService = accessor.get(IRenderManagerService);
const docStateEmitService = accessor.get(DocStateEmitService);
const documentDataModel = univerInstanceService.getUniverDocInstance(unitId);
const documentViewModel = renderManagerService.getRenderById(unitId)?.with(DocSkeletonManagerService).getViewModel();
if (documentDataModel == null || documentViewModel == null) {
throw new Error(`DocumentDataModel or documentViewModel not found for unitId: ${unitId}`);
const documentDataModel = univerInstanceService.getUnit<DocumentDataModel>(unitId, UniverInstanceType.UNIVER_DOC);
const documentViewModel = renderManagerService.getRenderUnitById(unitId)?.with(DocSkeletonManagerService).getViewModel();
if (documentDataModel == null) {
throw new Error(`DocumentDataModel not found for unitId: ${unitId}`);
}
const docSelectionManagerService = accessor.get(DocSelectionManagerService);
@@ -100,7 +101,7 @@ export const RichTextEditingMutation: IMutation<IRichTextEditingMutationParams,
documentDataModel.apply(actions);
// Step 2: Update Doc View Model.
documentViewModel.reset(documentDataModel);
documentViewModel?.reset(documentDataModel);
// Step 3: Update cursor & selection.
// Make sure update cursor & selection after doc skeleton is calculated.
if (!noNeedSetTextRange && textRanges && trigger != null && !isSync) {
+15 -4
View File
@@ -27,6 +27,11 @@ import {
} from '@univerjs/core';
import { FBaseInitialable } from '@univerjs/core/facade';
import { InsertTextCommand } from '@univerjs/docs';
import {
buildPlainTextInsertBody,
getNormalizedPlainTextCursorOffset,
getParagraphStyleAtOffset,
} from './utils';
export interface IDocumentInsertTextFacadeOptions {
startOffset?: number;
@@ -235,15 +240,21 @@ export class FDocument extends FBaseInitialable {
collapsed: startOffset === endOffset,
segmentId,
};
const removeLeadingParagraphBreak = startOffset === 0;
const insertBody = buildPlainTextInsertBody(text, {
paragraphStyle: getParagraphStyleAtOffset(body, startOffset),
removeLeadingParagraphBreak,
});
const cursorOffset = options.cursorOffset == null
? undefined
: getNormalizedPlainTextCursorOffset(text, options.cursorOffset, removeLeadingParagraphBreak);
return this._commandService.executeCommand<IInsertTextCommandParams>(InsertTextCommand.id, {
unitId,
body: {
dataStream: text,
},
body: insertBody,
range: activeRange,
segmentId,
...(options.cursorOffset == null ? {} : { cursorOffset: options.cursorOffset }),
...(cursorOffset == null ? {} : { cursorOffset }),
});
}
+96
View File
@@ -0,0 +1,96 @@
/**
* Copyright 2023-present DreamNum Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type { IDocumentBody } from '@univerjs/core';
type IParagraphStyle = NonNullable<IDocumentBody['paragraphs']>[number]['paragraphStyle'];
export interface IBuildPlainTextInsertBodyOptions {
paragraphStyle?: IParagraphStyle;
removeLeadingParagraphBreak?: boolean;
}
function cloneParagraphStyle(paragraphStyle: IParagraphStyle): IParagraphStyle {
return paragraphStyle == null ? paragraphStyle : JSON.parse(JSON.stringify(paragraphStyle));
}
function normalizePlainTextDataStream(dataStream: string): string {
return dataStream.replace(/\r\n/g, '\r').replace(/\n/g, '\r');
}
export function getRemovedLeadingParagraphBreakLength(
dataStream: string,
removeLeadingParagraphBreak?: boolean
): number {
const normalized = normalizePlainTextDataStream(dataStream);
if (removeLeadingParagraphBreak && normalized.length > 1 && normalized.startsWith('\r')) {
return 1;
}
return 0;
}
export function getNormalizedPlainTextCursorOffset(
dataStream: string,
cursorOffset: number,
removeLeadingParagraphBreak?: boolean
): number {
const normalizedPrefixLength = normalizePlainTextDataStream(dataStream.slice(0, cursorOffset)).length;
return Math.max(
0,
normalizedPrefixLength - getRemovedLeadingParagraphBreakLength(dataStream, removeLeadingParagraphBreak)
);
}
export function getParagraphStyleAtOffset(body: IDocumentBody, offset: number): IParagraphStyle {
const paragraphs = body.paragraphs ?? [];
const paragraph = paragraphs.find((item) => item.startIndex >= offset) ?? paragraphs[paragraphs.length - 1];
return paragraph?.paragraphStyle;
}
export function buildPlainTextInsertBody(
dataStream: string,
options: IBuildPlainTextInsertBodyOptions = {}
): IDocumentBody {
const normalizedDataStream = normalizePlainTextDataStream(dataStream).slice(
getRemovedLeadingParagraphBreakLength(dataStream, options.removeLeadingParagraphBreak)
);
const body: IDocumentBody = {
dataStream: normalizedDataStream,
customDecorations: [],
customRanges: [],
textRuns: [],
};
const paragraphs = [];
for (let index = 0; index < normalizedDataStream.length; index++) {
if (normalizedDataStream[index] === '\r') {
paragraphs.push({
startIndex: index,
...(options.paragraphStyle == null ? {} : { paragraphStyle: cloneParagraphStyle(options.paragraphStyle) }),
});
}
}
if (paragraphs.length > 0) {
body.paragraphs = paragraphs;
}
return body;
}
+1
View File
@@ -31,6 +31,7 @@ export { DocInterceptorService } from './services/doc-interceptor/doc-intercepto
export { DOC_INTERCEPTOR_POINT } from './services/doc-interceptor/interceptor-const';
export { DocSelectionManagerService } from './services/doc-selection-manager.service';
export { DocSkeletonManagerService } from './services/doc-skeleton-manager.service';
export { DocStateChangeManagerService, IDocStateChangeInterceptorService } from './services/doc-state-change-manager.service';
export type { IDocStateChangeInfo, IDocStateChangeParams } from './services/doc-state-emit.service';
export { DocStateEmitService } from './services/doc-state-emit.service';
export { addCustomRangeBySelectionFactory, addCustomRangeFactory, deleteCustomRangeFactory } from './utils/custom-range-factory';
+3
View File
@@ -33,6 +33,7 @@ import { defaultPluginConfig, DOCS_PLUGIN_CONFIG_KEY } from './config/config';
import { DocCustomRangeController } from './controllers/custom-range.controller';
import { DocContentInsertService } from './services/doc-content-insert.service';
import { DocSelectionManagerService } from './services/doc-selection-manager.service';
import { DocStateChangeManagerService } from './services/doc-state-change-manager.service';
import { DocStateEmitService } from './services/doc-state-emit.service';
export class UniverDocsPlugin extends Plugin {
@@ -82,6 +83,7 @@ export class UniverDocsPlugin extends Plugin {
[
[DocSelectionManagerService],
[DocStateEmitService],
[DocStateChangeManagerService],
[DocContentInsertService],
[DocCustomRangeController],
] as Dependency[]
@@ -89,6 +91,7 @@ export class UniverDocsPlugin extends Plugin {
}
override onReady(): void {
this._injector.get(DocStateChangeManagerService);
this._injector.get(DocCustomRangeController);
}
}
@@ -15,12 +15,11 @@
*/
import type { JSONXActions, Nullable } from '@univerjs/core';
import type { IDocStateChangeParams, IRichTextEditingMutationParams } from '@univerjs/docs';
import { ICommandService, Inject, IUndoRedoService, IUniverInstanceService, JSONX, RedoCommandId, RxDisposable, UndoCommandId } from '@univerjs/core';
import { DocStateEmitService } from '@univerjs/docs';
import { IRenderManagerService } from '@univerjs/engine-render';
import type { IRichTextEditingMutationParams } from '../commands/mutations/core-editing.mutation';
import type { IDocStateChangeInfo, IDocStateChangeParams } from './doc-state-emit.service';
import { createIdentifier, ICommandService, Inject, IUndoRedoService, IUniverInstanceService, JSONX, Optional, RedoCommandId, RxDisposable, UndoCommandId } from '@univerjs/core';
import { BehaviorSubject, takeUntil } from 'rxjs';
import { DocIMEInputManagerService } from './doc-ime-input-manager.service';
import { DocStateEmitService } from './doc-state-emit.service';
type ChangeStateCacheType = 'history' | 'collaboration';
@@ -31,9 +30,14 @@ interface IStateCache {
collaboration: IDocStateChangeParams[];
}
// This class sends out state-changing events, what is the state, the data model,
// and the cursor & selection, and this class mainly serves the History(undo/redo) module and
// the collaboration module.
export interface IDocStateChangeInterceptorService {
transformChangeStateInfo(changeStateInfo: IDocStateChangeInfo): Nullable<IDocStateChangeInfo>;
}
export const IDocStateChangeInterceptorService = createIdentifier<IDocStateChangeInterceptorService>('doc.state-change-interceptor-service');
// This class sends out state-changing events, what the state is, the data model,
// and cursor & selection information. It mainly serves history and collaboration.
export class DocStateChangeManagerService extends RxDisposable {
private readonly _docStateChange$ = new BehaviorSubject<Nullable<IDocStateChangeParams>>(null);
readonly docStateChange$ = this._docStateChange$.asObservable();
@@ -45,11 +49,11 @@ export class DocStateChangeManagerService extends RxDisposable {
private _changeStateCacheTimer: Nullable<ReturnType<typeof setTimeout>> = null;
constructor(
@Inject(IUndoRedoService) private _undoRedoService: IUndoRedoService,
@Optional(IUndoRedoService) private _undoRedoService: Nullable<IUndoRedoService>,
@ICommandService private readonly _commandService: ICommandService,
@IUniverInstanceService private readonly _univerInstanceService: IUniverInstanceService,
@Inject(DocStateEmitService) private readonly _docStateEmitService: DocStateEmitService,
@IRenderManagerService private readonly _renderManagerService: IRenderManagerService
@Optional(IDocStateChangeInterceptorService) private readonly _docStateChangeInterceptorService?: IDocStateChangeInterceptorService
) {
super();
@@ -99,27 +103,17 @@ export class DocStateChangeManagerService extends RxDisposable {
return;
}
const { isCompositionEnd, isSync, syncer, ...changeState } = changeStateInfo;
const imeInputManagerService = this._renderManagerService.getRenderById(isSync ? syncer! : changeStateInfo.unitId)?.with(DocIMEInputManagerService);
if (imeInputManagerService == null) {
const interceptedChangeStateInfo = this._docStateChangeInterceptorService?.transformChangeStateInfo(changeStateInfo) ?? changeStateInfo;
if (interceptedChangeStateInfo == null) {
return;
}
// Handle IME input.
if (isCompositionEnd) {
const historyParams = imeInputManagerService.fetchComposedUndoRedoMutationParams();
if (historyParams == null) {
throw new Error('historyParams is null in RichTextEditingMutation');
}
const { undoMutationParams, redoMutationParams, previousActiveRange } = historyParams;
changeState.redoState.actions = redoMutationParams.actions;
changeState.undoState.actions = undoMutationParams.actions;
changeState.undoState.textRanges = [previousActiveRange];
if (interceptedChangeStateInfo.isSync) {
return;
}
const { isCompositionEnd: _isCompositionEnd, isSync: _isSync, syncer: _syncer, ...changeState } = interceptedChangeStateInfo;
this._setChangeState(changeState);
});
}
@@ -127,7 +121,7 @@ export class DocStateChangeManagerService extends RxDisposable {
private _cacheChangeState(changeState: IDocStateChangeParams, type: ChangeStateCacheType = 'history') {
const { trigger, unitId, noHistory, debounce = false } = changeState;
if (noHistory || trigger == null) {
if (noHistory || (type === 'history' && trigger == null)) {
return;
}
@@ -178,7 +172,7 @@ export class DocStateChangeManagerService extends RxDisposable {
const undoRedoService = this._undoRedoService;
const cacheStates = this._historyStateCache.get(unitId);
if (!Array.isArray(cacheStates) || cacheStates.length === 0) {
if (undoRedoService == null || !Array.isArray(cacheStates) || cacheStates.length === 0) {
return;
}
@@ -235,15 +235,3 @@ export function createFrozenCommandTestBed(workbookData?: IWorkbookData) {
sheet,
};
}
// export class MockRenderManagerService implements Pick<IRenderManagerService, 'getRenderById'> {
// constructor(
// @Inject(Injector) private readonly _injector: Injector
// ) { }
// getRenderById(_unitId: string): Nullable<IRender> {
// return {
// with: <T>(identifier: DependencyIdentifier<T>) => this._injector.get(identifier),
// } as unknown as IRender;
// }
// }
@@ -27,7 +27,7 @@ import {
UndoCommand,
UniverInstanceType,
} from '@univerjs/core';
import { IRenderManagerService } from '@univerjs/engine-render';
import { IRenderManagerService, RenderManagerService } from '@univerjs/engine-render';
import {
AddWorksheetMergeMutation,
RemoveWorksheetMergeMutation,
@@ -208,22 +208,13 @@ describe('Test format painter rules in controller', () => {
[IMarkSelectionService, { useClass: MarkSelectionService }],
[IFormatPainterService, { useClass: FormatPainterService }],
[ISheetSelectionRenderService, { useClass: SheetSelectionRenderService }],
[IRenderManagerService, { useClass: RenderManagerService }],
[FormatPainterController],
]);
univer = testBed.univer;
get = testBed.get;
class MockRenderManagerService {
getRenderById() {
return null;
}
}
const injector = univer.__getInjector();
// @ts-ignore
injector.add([IRenderManagerService, { useClass: MockRenderManagerService }]);
commandService = get(ICommandService);
themeService = get(ThemeService);
const theme = themeService.getCurrentTheme();
@@ -18,7 +18,7 @@ import type { Dependency, IDisposable, Injector, IWorkbookData, Workbook } from
import type { IRenderContext, Vector2 } from '@univerjs/engine-render';
import type { Observable } from 'rxjs';
import { ICommandService, IContextService, ILogService, Inject, IUniverInstanceService, LocaleService, LocaleType, LogLevel, Plugin, Tools, Univer, Injector as UniverInjector, UniverInstanceType } from '@univerjs/core';
import { IRenderManagerService, SHEET_VIEWPORT_KEY, Viewport } from '@univerjs/engine-render';
import { IRenderManagerService, RenderManagerService, SHEET_VIEWPORT_KEY, Viewport } from '@univerjs/engine-render';
import { SheetInterceptorService, SheetsSelectionsService } from '@univerjs/sheets';
import { BehaviorSubject, Subject } from 'rxjs';
import { SHEET_VIEW_KEY } from '../../../common/keys';
@@ -500,11 +500,13 @@ export function createRenderTestBed(options?: { workbookData?: IWorkbookData; de
viewportMap.set(SHEET_VIEWPORT_KEY.VIEW_LEFT_TOP, viewportFactory(SHEET_VIEWPORT_KEY.VIEW_LEFT_TOP));
const scene = createFakeScene(viewportMap, { parentClassType: options?.parentClassType, engine });
(scene as any).dispose ??= () => { };
(engine as any).dispose ??= () => { };
const components = new Map<any, any>();
components.set(SHEET_VIEW_KEY.ROW, { onPointerDown$: createTestEvent<any>(), onPointerMove$: createTestEvent<any>(), onPointerLeave$: createTestEvent<any>() });
components.set(SHEET_VIEW_KEY.COLUMN, { onPointerDown$: createTestEvent<any>(), onPointerMove$: createTestEvent<any>(), onPointerLeave$: createTestEvent<any>() });
components.set(SHEET_VIEW_KEY.LEFT_TOP, { onPointerDown$: createTestEvent<any>() });
components.set(SHEET_VIEW_KEY.ROW, { onPointerDown$: createTestEvent<any>(), onPointerMove$: createTestEvent<any>(), onPointerLeave$: createTestEvent<any>(), dispose: () => { } });
components.set(SHEET_VIEW_KEY.COLUMN, { onPointerDown$: createTestEvent<any>(), onPointerMove$: createTestEvent<any>(), onPointerLeave$: createTestEvent<any>(), dispose: () => { } });
components.set(SHEET_VIEW_KEY.LEFT_TOP, { onPointerDown$: createTestEvent<any>(), dispose: () => { } });
const mainComponent = {
zIndex: 1,
@@ -557,20 +559,22 @@ export function createRenderTestBed(options?: { workbookData?: IWorkbookData; de
injector.add([SheetSkeletonManagerService, { useValue: sheetSkeletonManagerService as any }]);
const renderManagerService: IRenderManagerService = {
getRenderById: (unitId: string) => {
if (unitId !== sheet.getUnitId()) return null as any;
return {
unitId,
engine,
scene,
mainComponent,
components,
} as any;
},
} as IRenderManagerService;
injector.add([IRenderManagerService, { useValue: renderManagerService as any }]);
injector.add([IRenderManagerService, { useClass: RenderManagerService }]);
const renderManagerService = injector.get(IRenderManagerService);
renderManagerService.addRender(sheet.getUnitId(), {
unitId: sheet.getUnitId(),
type: UniverInstanceType.UNIVER_SHEET,
engine: engine as any,
scene: scene as any,
mainComponent: mainComponent as any,
components,
isMainScene: true,
activated$: new BehaviorSubject(true),
with: injector.get.bind(injector),
activate: () => { },
deactivate: () => { },
isDisposed: () => false,
});
return {
univer,