refactor: migrate doc to sheet as cell editor and support focus manager & context service (#112)

This commit is contained in:
Wenzhao Hu
2023-09-15 10:36:22 +08:00
committed by GitHub
parent a561a99308
commit b83a9d4045
180 changed files with 3553 additions and 1928 deletions
+7 -7
View File
@@ -93,19 +93,19 @@ module.exports = {
},
],
// eslint-plugin-unused-imports
'@typescript-eslint/no-unused-vars': 'off',
'unused-imports/no-unused-imports': 'error',
'prefer-regex-literals': 'off',
'@typescript-eslint/consistent-type-assertions': 'off',
'@typescript-eslint/default-param-last': 'off',
'grouped-accessor-pairs': 'off',
'@typescript-eslint/member-ordering': 'error',
'@typescript-eslint/no-explicit-any': 'warn',
'no-unsafe-optional-chaining': 'off',
'@typescript-eslint/consistent-type-assertions': 'off',
'@typescript-eslint/no-redeclare': 'off', // dependency interface and dependency token share the same name
'@typescript-eslint/no-unused-vars': 'off',
'grouped-accessor-pairs': 'off',
'no-magic-numbers': ['warn', { ignore: [0, 1, -1, 2] }],
'simple-import-sort/imports': 'error',
'no-unsafe-optional-chaining': 'off',
'prefer-regex-literals': 'off',
'simple-import-sort/exports': 'error',
'simple-import-sort/imports': 'error',
'unused-imports/no-unused-imports': 'error',
},
// https://www.npmjs.com/package/eslint-import-resolver-typescript
settings: {
+1 -1
View File
@@ -5,7 +5,7 @@ const args = require('minimist')(process.argv.slice(2));
bundle: true,
color: true,
loader: { '.svg': 'file' },
sourcemap: false,
sourcemap: true,
plugins: [
require('esbuild-plugin-clean').clean({
patterns: ['./local'],
-22
View File
@@ -1,22 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>UniverDoc</title>
<style>
html,
body {
height: 100%;
margin: 0;
}
</style>
</head>
<body>
<div id="app" style="z-index: 1; position: fixed; font-size: 20px; color: green"></div>
<div id="univerdoc" style="height: 100vh; width: 100%"></div>
<script type="module" src="./src/main.tsx"></script>
</body>
</html>
+30 -33
View File
@@ -1,33 +1,30 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta content="width=device-width, initial-scale=1" name="viewport" />
<title>UniverDoc</title>
<link rel="stylesheet" href="./main.css" />
<style>
html,
body {
height: 100%;
margin: 0;
}
</style>
<script>
new EventSource('/esbuild').addEventListener('change', () => {
location.reload()
})
</script>
</head>
<body>
<div id="app" style="z-index: 1; position: fixed; font-size: 20px; color: green;"></div>
<div id="univerdoc" style="height: 100%;"></div>
<script src="./main.js"></script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta content="width=device-width, initial-scale=1" name="viewport" />
<title>UniverDoc</title>
<link rel="stylesheet" href="./main.css" />
<style>
html,
body {
height: 100%;
margin: 0;
}
</style>
<script>
new EventSource('/esbuild').addEventListener('change', () => {
location.reload();
});
</script>
</head>
<body>
<div id="app" style="z-index: 1; position: fixed; font-size: 20px; color: green"></div>
<div id="univer-container" style="height: 100%"></div>
<script src="./main.js"></script>
</body>
</html>
+13 -3
View File
@@ -1,17 +1,25 @@
import { DocPlugin } from '@univerjs/base-docs';
import { RenderEngine } from '@univerjs/base-render';
import { UIPlugin } from '@univerjs/base-ui';
import { DEFAULT_DOCUMENT_DATA_EN } from '@univerjs/common-plugin-data';
import { LocaleType, Univer } from '@univerjs/core';
import { DocUIPlugin } from '@univerjs/ui-plugin-docs';
// univer
const univer = new Univer({
locale: LocaleType.EN,
});
univer.createUniverDoc(DEFAULT_DOCUMENT_DATA_EN);
// core plugins
univer.registerPlugin(RenderEngine);
univer.registerPlugin(DocPlugin);
univer.registerPlugin(UIPlugin, {
container: 'univer-container',
header: true,
toolbar: true,
});
univer.registerPlugin(DocPlugin, {
standalone: true,
});
univer.registerPlugin(DocUIPlugin, {
container: 'univerdoc',
layout: {
@@ -20,3 +28,5 @@ univer.registerPlugin(DocUIPlugin, {
},
},
});
univer.createUniverDoc(DEFAULT_DOCUMENT_DATA_EN);
+1 -1
View File
@@ -5,7 +5,7 @@ const args = require('minimist')(process.argv.slice(2));
bundle: true,
color: true,
loader: { '.svg': 'file' },
sourcemap: false,
sourcemap: true,
plugins: [
require('esbuild-plugin-clean').clean({
patterns: ['./local'],
-24
View File
@@ -1,24 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>UniverSheet</title>
<style>
html,
body {
height: 100%;
margin: 0;
}
</style>
</head>
<body>
<div id="app" style="z-index: 1; position: fixed; font-size: 20px; color: green;"></div>
<div id="universheet" style="height: 100%;"></div>
<script type="module" src="./src/main.tsx"></script>
</body>
</html>
+4 -2
View File
@@ -29,6 +29,7 @@
"ts-node": "^10.9.1"
},
"dependencies": {
"@univerjs/base-docs": "workspace:^0.0.1",
"@univerjs/base-render": "workspace:^0.0.1",
"@univerjs/base-sheets": "workspace:^0.0.1",
"@univerjs/base-ui": "workspace:^0.0.1",
@@ -40,8 +41,9 @@
"@univerjs/sheets-plugin-import-xlsx": "workspace:^0.0.1",
"@univerjs/sheets-plugin-numfmt": "workspace:^0.0.1",
"@univerjs/sheets-plugin-operation": "workspace:^0.0.1",
"@univerjs/ui-plugin-docs": "workspace:^0.0.1",
"@univerjs/ui-plugin-sheets": "workspace:^0.0.1",
"react": "^18.2.0",
"react-dom": "^18.2.0"
"react-dom": "^18.2.0",
"react": "^18.2.0"
}
}
+25 -29
View File
@@ -1,35 +1,31 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta content="width=device-width, initial-scale=1" name="viewport" />
<title>Univer Sheet</title>
<head>
<meta charset="utf-8" />
<meta content="width=device-width, initial-scale=1" name="viewport" />
<title>Univer Sheet</title>
<link rel="stylesheet" href="./main.css" />
<style>
html,
body {
height: 100%;
margin: 0;
}
</style>
<link rel="stylesheet" href="./main.css" />
<style>
html,
body {
height: 100%;
margin: 0;
}
</style>
<script>
new EventSource('/esbuild').addEventListener('change', () => {
console.info('reload--');
location.reload();
});
</script>
</head>
<body style="overflow: hidden">
<div id="app" style="z-index: 1; position: fixed; font-size: 20px; color: green"></div>
<div id="univer-container" style="height: 100%"></div>
<script>
new EventSource('/esbuild').addEventListener('change', () => {
console.info('reload--')
location.reload()
})
</script>
</head>
<body style="overflow: hidden;">
<div id="app" style="z-index: 1; position: fixed; font-size: 20px; color: green;"></div>
<!-- <div id="universheet" style="height: 225px;width: 400px;"></div> -->
<div id="universheet" style="height: 100%;"></div>
<script src="./main.js"></script>
</body>
</html>
<script src="./main.js"></script>
</body>
</html>
+19 -20
View File
@@ -1,13 +1,15 @@
import { DocPlugin } from '@univerjs/base-docs';
import { RenderEngine } from '@univerjs/base-render';
import { SheetPlugin } from '@univerjs/base-sheets';
import { UIPlugin } from '@univerjs/base-ui';
import { DEFAULT_WORKBOOK_DATA_DEMO } from '@univerjs/common-plugin-data';
import { LocaleType, Univer } from '@univerjs/core';
import { FindPlugin } from '@univerjs/sheets-plugin-find';
import { DEFAULT_FORMULA_DATA_DEMO, FormulaPlugin } from '@univerjs/sheets-plugin-formula';
// import { OperationPlugin } from '@univerjs/sheets-plugin-operation';
import { ImagePlugin } from '@univerjs/sheets-plugin-image';
import { ImportXlsxPlugin } from '@univerjs/sheets-plugin-import-xlsx';
import { NumfmtPlugin } from '@univerjs/sheets-plugin-numfmt';
import { DocUIPlugin } from '@univerjs/ui-plugin-docs';
import { SheetUIPlugin } from '@univerjs/ui-plugin-sheets';
// univer
@@ -15,32 +17,29 @@ const univer = new Univer({
locale: LocaleType.EN,
});
// create univer sheet instance
univer.createUniverSheet(DEFAULT_WORKBOOK_DATA_DEMO);
// register plugins
univer.registerPlugin(RenderEngine);
univer.registerPlugin(SheetPlugin);
univer.registerPlugin(SheetUIPlugin, {
container: 'universheet',
layout: {
sheetContainerConfig: {
infoBar: true,
formulaBar: true,
toolbar: true,
sheetBar: true,
countBar: true,
rightMenu: true,
},
},
// core plugins
univer.registerPlugin(DocPlugin, {
standalone: false,
});
univer.registerPlugin(DocUIPlugin);
univer.registerPlugin(RenderEngine);
univer.registerPlugin(UIPlugin, {
container: 'univer-container',
header: true,
toolbar: true,
});
univer.registerPlugin(SheetPlugin);
univer.registerPlugin(SheetUIPlugin);
// feature plugins
univer.registerPlugin(NumfmtPlugin);
univer.registerPlugin(FindPlugin);
univer.registerPlugin(FormulaPlugin, DEFAULT_FORMULA_DATA_DEMO);
univer.registerPlugin(ImportXlsxPlugin);
univer.registerPlugin(ImagePlugin);
// univer.registerPlugin(OperationPlugin);
// create univer sheet instance
univer.createUniverSheet(DEFAULT_WORKBOOK_DATA_DEMO);
// use for console test
declare global {
+3 -2
View File
@@ -55,6 +55,7 @@
"@wendellhu/redi": "^0.12.7",
"css-vars-ponyfill": "^2.4.6",
"react": "^18.2.0",
"react-dom": "^18.2.0"
"react-dom": "^18.2.0",
"rxjs": "^7.8.1"
}
}
}
@@ -13,31 +13,76 @@ import {
isSameLine,
TextSelection,
} from '@univerjs/base-render';
import { DataStreamTreeTokenType, Direction, DocumentModel, ICommandService, ICurrentUniverService, IParagraph, Nullable, Observable } from '@univerjs/core';
import { Inject } from '@wendellhu/redi';
import {
DataStreamTreeTokenType,
Direction,
Disposable,
DisposableCollection,
DocumentModel,
fromObservable,
ICommandService,
ICurrentUniverService,
IParagraph,
Nullable,
Observable,
toDisposable,
} from '@univerjs/core';
import { IDisposable, Inject } from '@wendellhu/redi';
import { DeleteCommand, IMEInputCommand, InsertCommand, UpdateCommand } from '../commands/commands/core-editing.command';
import { CanvasView } from '../View/Render/CanvasView';
import { DocsViewManagerService } from '../services/docs-view-manager/docs-view-manager.service';
import { DocsView } from '../View/Render/Views';
export class InputController {
/**
* InputController handles inputting events on the currently focused DocsView and apply edits to corresponding UniverDoc.
*/
export class InputController extends Disposable {
private _previousIMEContent: string = '';
private _previousIMEStart: number;
private _currentNodePosition: Nullable<INodePosition>;
private _editorDisposables: DisposableCollection | null;
private _currentDocsView: DocsView | null;
constructor(
@Inject(CanvasView) private readonly _canvasView: CanvasView,
@Inject(DocsViewManagerService) private readonly _docsViewManager: DocsViewManagerService,
@ICurrentUniverService private readonly _currentUniverService: ICurrentUniverService,
@ICommandService private readonly _commandService: ICommandService
) {
this._initialize();
super();
this.disposeWithMe(
fromObservable(
this._docsViewManager.current$.subscribe((docsView) => {
this._currentDocsView?.getDocs().disableEditor();
this._editorDisposables?.dispose();
this._editorDisposables = null;
if (docsView) {
this._currentDocsView = docsView;
this._editorDisposables = this._initialize(docsView);
}
})
)
);
}
override dispose(): void {
super.dispose();
this._currentDocsView = null;
this._editorDisposables?.dispose();
}
// eslint-disable-next-line @typescript-eslint/member-ordering
moveCursor(_docModel: DocumentModel, direction: Direction) {
const documents = (this._canvasView.getDocsView() as DocsView).getDocs();
if (!this._currentDocsView) {
return;
}
const documents = this._currentDocsView.getDocs();
const activeSelection = documents.getActiveSelection();
if (!activeSelection) {
return false;
@@ -115,7 +160,11 @@ export class InputController {
// eslint-disable-next-line max-lines-per-function
deleteLeft() {
const document = (this._canvasView.getDocsView() as DocsView).getDocs();
if (!this._currentDocsView) {
return;
}
const document = this._currentDocsView.getDocs();
const activeSelection = document.getActiveSelection();
if (!activeSelection) {
return;
@@ -232,7 +281,11 @@ export class InputController {
}
breakLine() {
const document = (this._canvasView.getDocsView() as DocsView).getDocs();
if (!this._currentDocsView) {
return;
}
const document = this._currentDocsView.getDocs();
const activeSelection = document.getActiveSelection();
if (!activeSelection) {
return;
@@ -272,8 +325,8 @@ export class InputController {
this._adjustSelection(document as Documents, selectionRemain, span, true);
}
private _initialInput(onInputObservable: Observable<IEditorInputConfig>, docsModel: DocumentModel) {
onInputObservable.add((config) => {
private _initialInput(onInputObservable: Observable<IEditorInputConfig>, docsModel: DocumentModel): IDisposable {
const observer = onInputObservable.add((config) => {
const { event, content = '', document, activeSelection, selectionList } = config;
const e = event as InputEvent;
@@ -319,6 +372,8 @@ export class InputController {
this._adjustSelection(document as Documents, selectionRemain, span);
});
return toDisposable(() => onInputObservable.remove(observer));
}
private _initialComposition(
@@ -326,8 +381,8 @@ export class InputController {
onCompositionupdateObservable: Observable<IEditorInputConfig>,
onCompositionendObservable: Observable<IEditorInputConfig>,
docsModel: DocumentModel
) {
onCompositionstartObservable.add((config) => {
): IDisposable {
const startObserver = onCompositionstartObservable.add((config) => {
const { activeSelection, selectionList } = config;
const activeRange = activeSelection?.getRange();
@@ -346,7 +401,7 @@ export class InputController {
this._previousIMEStart = cursor;
});
onCompositionupdateObservable.add(async (config) => {
const updateObserver = onCompositionupdateObservable.add(async (config) => {
const { event, document, activeSelection } = config;
let cursor = this._previousIMEStart;
@@ -388,9 +443,15 @@ export class InputController {
this._previousIMEContent = content;
});
onCompositionendObservable.add((config) => {
const endObserver = onCompositionendObservable.add((config) => {
this._resetIME();
});
return toDisposable(() => {
onCompositionstartObservable.remove(startObserver);
onCompositionupdateObservable.remove(updateObserver);
onCompositionendObservable.remove(endObserver);
});
}
private _getTopOrBottomPosition(document: Documents, span: Nullable<IDocumentSkeletonSpan>, direction: boolean): Nullable<INodePosition> {
@@ -661,23 +722,26 @@ export class InputController {
document.syncSelection();
}
private _initialize() {
const docsView = this._canvasView.getDocsView() as DocsView;
private _initialize(docsView: DocsView): DisposableCollection {
docsView.getDocs().enableEditor();
const events = docsView.getDocs().getEditorInputEvent();
const docsModel = this._currentUniverService.getCurrentUniverDocInstance().getDocument();
const disposableCollection = new DisposableCollection();
if (!events) {
return;
return disposableCollection;
}
const { onInputObservable, onCompositionstartObservable, onCompositionupdateObservable, onCompositionendObservable, onSelectionStartObservable } = events;
this._initialInput(onInputObservable, docsModel);
disposableCollection.add(this._initialInput(onInputObservable, docsModel));
disposableCollection.add(this._initialComposition(onCompositionstartObservable, onCompositionupdateObservable, onCompositionendObservable, docsModel));
this._initialComposition(onCompositionstartObservable, onCompositionupdateObservable, onCompositionendObservable, docsModel);
onSelectionStartObservable.add((nodePosition) => {
const observer = onSelectionStartObservable.add((nodePosition) => {
this._currentNodePosition = nodePosition;
});
disposableCollection.add(toDisposable(() => onSelectionStartObservable.remove(observer)));
return disposableCollection;
}
}
+48 -33
View File
@@ -1,21 +1,24 @@
import { Engine } from '@univerjs/base-render';
import { DesktopPlatformService, DesktopShortcutService, IPlatformService, IShortcutService } from '@univerjs/base-ui';
import { ICommandService, LocaleService, ObserverManager, Plugin, PLUGIN_NAMES, PluginType, UIObserver } from '@univerjs/core';
import { Dependency, Inject, Injector } from '@wendellhu/redi';
import { ContextService, DesktopPlatformService, IContextService, IPlatformService, IShortcutService } from '@univerjs/base-ui';
import { ICommand, ICommandService, ICurrentUniverService, LocaleService, Plugin, PLUGIN_NAMES, PluginType } from '@univerjs/core';
import { Dependency, Inject, Injector, SkipSelf } from '@wendellhu/redi';
import { DocPluginObserve, install } from './Basics/Observer';
import { BreakLineCommand, DeleteCommand, DeleteLeftCommand, IMEInputCommand, InsertCommand, UpdateCommand } from './commands/commands/core-editing.command';
import { BreakLineCommand, CoverCommand, DeleteCommand, DeleteLeftCommand, IMEInputCommand, InsertCommand, UpdateCommand } from './commands/commands/core-editing.command';
import { RichTextEditingMutation } from './commands/mutations/core-editing.mutation';
import { MoveCursorOperation } from './commands/operations/cursor.operation';
import { DocumentController } from './Controller/DocumentController';
import { en, zh } from './Locale';
import { DocsViewManagerService } from './services/docs-view-manager/docs-view-manager.service';
import { BreakLineShortcut, DeleteLeftShortcut } from './shortcuts/core-editing.shortcut';
import { MoveCursorDownShortcut, MoveCursorLeftShortcut, MoveCursorRightShortcut, MoveCursorUpShortcut } from './shortcuts/cursor.shortcut';
import { CANVAS_VIEW_KEY } from './View/Render';
import { CanvasView } from './View/Render/CanvasView';
import { DocsView } from './View/Render/Views';
export interface IDocPluginConfig {}
export interface IDocPluginConfig {
standalone?: boolean;
}
const DEFAULT_DOCUMENT_PLUGIN_DATA = {};
@@ -32,14 +35,15 @@ export class DocPlugin extends Plugin<DocPluginObserve> {
constructor(
config: Partial<IDocPluginConfig> = {},
@Inject(ObserverManager) private readonly _globalObserverManager: ObserverManager,
@SkipSelf() @Inject(Injector) _univerInjector: Injector,
@Inject(Injector) override _injector: Injector,
@Inject(LocaleService) private readonly _localeService: LocaleService
@Inject(LocaleService) private readonly _localeService: LocaleService,
@ICurrentUniverService private readonly _currentUniverService: ICurrentUniverService
) {
super(PLUGIN_NAMES.DOCUMENT);
this._config = Object.assign(DEFAULT_DOCUMENT_PLUGIN_DATA, config);
this._initializeDependencies(_injector);
this._initializeDependencies(_injector, _univerInjector);
this.initializeCommands();
}
@@ -51,11 +55,28 @@ export class DocPlugin extends Plugin<DocPluginObserve> {
install(this);
this.listenEventManager();
if (this._config.standalone) {
this.initCanvasView();
}
this._initController();
this._markDocAsFocused();
}
initializeCommands(): void {
[MoveCursorOperation, DeleteLeftCommand, BreakLineCommand, InsertCommand, DeleteCommand, UpdateCommand, IMEInputCommand, RichTextEditingMutation].forEach((command) => {
(
[
MoveCursorOperation,
DeleteLeftCommand,
BreakLineCommand,
InsertCommand,
DeleteCommand,
UpdateCommand,
IMEInputCommand,
RichTextEditingMutation,
CoverCommand,
] as ICommand[]
).forEach((command) => {
this._injector.get(ICommandService).registerCommand(command);
});
@@ -64,12 +85,7 @@ export class DocPlugin extends Plugin<DocPluginObserve> {
});
}
initializeAfterUI() {
this.initCanvasView();
this.initController();
}
initController() {
_initController() {
this._documentController = new DocumentController(this._injector);
}
@@ -77,14 +93,6 @@ export class DocPlugin extends Plugin<DocPluginObserve> {
this._canvasView = this._injector.get(CanvasView);
}
listenEventManager() {
// FIXME@wzhudev: this looks strange to be. It should not rely on the event created by a upper layer plugin.
// Instead, upper layer plugin should call it.
this._getCoreObserver<boolean>('onUIDidMountObservable').add(() => {
this.initializeAfterUI();
});
}
getConfig() {
return this._config;
}
@@ -151,16 +159,23 @@ export class DocPlugin extends Plugin<DocPluginObserve> {
override onDestroy(): void {}
/** @deprecated This will be removed. Modules should inject `ObserverManager` instead of getting it from plugin. */
protected _getCoreObserver<T>(type: string) {
return this._globalObserverManager.requiredObserver<UIObserver<T>>(type, 'core');
private _initializeDependencies(docInjector: Injector, univerInjector: Injector) {
(
[
[CanvasView, { useFactory: () => docInjector.createInstance(CanvasView, this._config.standalone ?? true) }], // FIXME: CanvasView shouldn't be a dependency of DocPlugin. Because it maybe created dynamically.
[IPlatformService, { useClass: DesktopPlatformService }],
[IContextService, { useClass: ContextService }],
] as Dependency[]
).forEach((d) => docInjector.add(d));
// add docs view manager to univer-level injector
univerInjector.add([DocsViewManagerService]);
}
private _initializeDependencies(docInjector: Injector) {
const dependencies: Dependency[] = [[CanvasView], [IShortcutService, { useClass: DesktopShortcutService }], [IPlatformService, { useClass: DesktopPlatformService }]];
dependencies.forEach((d) => {
docInjector.add(d);
});
private _markDocAsFocused() {
if (this._config.standalone) {
const c = this._currentUniverService.getCurrentUniverDocInstance();
this._currentUniverService.focusUniverInstance(c.getUnitId());
}
}
}
@@ -28,7 +28,7 @@ export class BaseView {
}
export enum CANVAS_VIEW_KEY {
MAIN_SCENE = 'mainScene',
MAIN_SCENE = 'docMainScene',
DOCS_VIEW = 'docsView',
}
@@ -11,7 +11,13 @@ export class CanvasView {
private _views: BaseView[] = [];
constructor(@IRenderingEngine private readonly _engine: Engine, @Inject(Injector) private readonly _injector: Injector) {
constructor(
// TODO@wzhudev: CanvasView should not be a singleton
/** @deprecated This a temporary solution. CanvasView should not be a singleton. */
private standalone = true,
@IRenderingEngine private readonly _engine: Engine,
@Inject(Injector) private readonly _injector: Injector
) {
this._initialize();
}
@@ -30,12 +36,11 @@ export class CanvasView {
private _initialize() {
const engine = this._engine;
const scene = new Scene(CANVAS_VIEW_KEY.MAIN_SCENE, engine, {
width: 1200,
const scene = (this._scene = new Scene(CANVAS_VIEW_KEY.MAIN_SCENE, engine, {
width: 1024,
height: 2000,
});
}));
this._scene = scene;
const viewMain = new Viewport(CANVAS_VIEW_KEY.DOCS_VIEW, scene, {
left: 0,
top: 0,
@@ -76,13 +81,15 @@ export class CanvasView {
this._viewLoader(scene);
engine.runRenderLoop(() => {
scene.render();
const app = document.getElementById('app');
if (app) {
app.innerText = `fps:${Math.round(engine.getFps()).toString()}`;
}
});
if (this.standalone) {
engine.runRenderLoop(() => {
scene.render();
const app = document.getElementById('app');
if (app) {
app.innerText = `fps:${Math.round(engine.getFps()).toString()}`;
}
});
}
}
private _viewLoader(scene: Scene) {
@@ -2,6 +2,7 @@ import { Documents, DocumentSkeleton, IDocumentSkeletonDrawing, Picture, Scene }
import { DocumentModel, ICurrentUniverService, LocaleService } from '@univerjs/core';
import { Inject, Injector } from '@wendellhu/redi';
import { DocsViewManagerService } from '../../../services/docs-view-manager/docs-view-manager.service';
import { BaseView, CANVAS_VIEW_KEY, CanvasViewRegistry } from '../BaseView';
export enum DOCS_VIEW_KEY {
@@ -17,8 +18,30 @@ export class DocsView extends BaseView {
private _documents: Documents;
constructor(@ICurrentUniverService private readonly _currentUniverService: ICurrentUniverService, @Inject(LocaleService) private readonly _localeService: LocaleService) {
private readonly _useExternalModel: boolean;
private _model: DocumentModel | null = null;
// TODO: wzhudev this is strange that DocsView depends on current univer service
constructor(
externalModel: DocumentModel | undefined,
@ICurrentUniverService private readonly _currentUniverService: ICurrentUniverService,
@Inject(DocsViewManagerService) private _docsViewManagerService: DocsViewManagerService,
@Inject(LocaleService) private readonly _localeService: LocaleService
) {
super();
if (externalModel) {
this._useExternalModel = true;
this._model = externalModel;
} else {
this._useExternalModel = false;
}
const docsModel = this._useExternalModel ? externalModel : this._currentUniverService.getCurrentUniverDocInstance()?.getDocument();
if (docsModel) {
this._docsViewManagerService.registerCanvasViewForUniverInstance(docsModel!.getUnitId(), this);
}
}
getDocumentSkeleton() {
@@ -51,12 +74,16 @@ export class DocsView extends BaseView {
protected override _initialize() {
const scene = this.getScene();
const docsModel = this._currentUniverService.getCurrentUniverDocInstance().getDocument();
const docsModel = this._useExternalModel ? this._model! : this._currentUniverService.getCurrentUniverDocInstance()?.getDocument();
if (!docsModel) {
return;
}
const documentSkeleton = this._buildSkeleton(docsModel);
const documents = new Documents(DOCS_VIEW_KEY.MAIN, documentSkeleton);
const documents = new Documents(DOCS_VIEW_KEY.MAIN, documentSkeleton, {
pageMarginLeft: docsModel.documentStyle.marginLeft,
pageMarginTop: docsModel.documentStyle.marginTop,
});
documents.zIndex = 1000;
this._documentSkeleton = documentSkeleton;
this._documents = documents;
@@ -110,8 +137,6 @@ export class DocsView extends BaseView {
obj?.translate(obj.left + docsLeft - pageMarginLeft, obj.top + docsTop - pageMarginTop);
}
}
documents.enableEditor();
}
private _buildSkeleton(model: DocumentModel) {
@@ -1,5 +1,6 @@
import { Rect, Documents, IPageRenderConfig, Scene } from '@univerjs/base-render';
import { Documents, IPageRenderConfig, Rect, Scene } from '@univerjs/base-render';
import { Injector } from '@wendellhu/redi';
import { BaseView, CanvasViewRegistry } from '../BaseView';
import { DOCS_VIEW_KEY } from './DocsView';
@@ -1,10 +1,13 @@
import {
CommandType,
createEmptyDocSnapshot,
getTextIndexByCursor,
ICommand,
ICommandInfo,
ICommandService,
ICurrentUniverService,
IDocumentBody,
IDocumentData,
ITextSelectionRange,
IUndoRedoService,
UpdateDocsAttributeType,
@@ -91,7 +94,7 @@ export const InsertCommand: ICommand<IInsertCommandParams> = {
const result = await commandService.executeCommand<IRichTextEditingMutationParams, IRichTextEditingMutationParams>(doMutation.id, doMutation.params);
if (result) {
undoRedoService.pushUndoRedo({
URI: 'doc',
URI: unitId,
undo() {
commandService.executeCommand(RichTextEditingMutation.id, result);
return true;
@@ -143,7 +146,7 @@ export const DeleteCommand: ICommand<IDeleteCommandParams> = {
const result = await commandService.executeCommand<IRichTextEditingMutationParams, IRichTextEditingMutationParams>(doMutation.id, doMutation.params);
if (result) {
undoRedoService.pushUndoRedo({
URI: 'doc',
URI: unitId,
undo() {
commandService.executeCommand(RichTextEditingMutation.id, result);
return true;
@@ -212,7 +215,7 @@ export const UpdateCommand: ICommand<IUpdateCommandParams> = {
const result = await commandService.executeCommand<IRichTextEditingMutationParams, IRichTextEditingMutationParams>(doMutation.id, doMutation.params);
if (result) {
undoRedoService.pushUndoRedo({
URI: 'doc',
URI: unitId,
undo() {
commandService.executeCommand(RichTextEditingMutation.id, result);
return true;
@@ -243,7 +246,6 @@ export const IMEInputCommand: ICommand<IIMEInputCommandParams> = {
type: CommandType.COMMAND,
handler: async (accessor, params: IIMEInputCommandParams) => {
const commandService = accessor.get(ICommandService);
const undoRedoService = accessor.get(IUndoRedoService);
const { unitId, newText, oldTextLen, start, segmentId } = params;
const doMutation: ICommandInfo<IRichTextEditingMutationParams> = {
@@ -281,7 +283,6 @@ export const IMEInputCommand: ICommand<IIMEInputCommandParams> = {
const result = await commandService.executeCommand(doMutation.id, doMutation.params);
if (!result) {
// NOTE: maybe we should throw an error here?
return false;
}
@@ -289,6 +290,35 @@ export const IMEInputCommand: ICommand<IIMEInputCommandParams> = {
},
};
export interface ICoverCommandParams {
unitId: string;
snapshot?: IDocumentData;
clearUndoRedoStack?: boolean;
}
export const CoverCommand: ICommand<ICoverCommandParams> = {
id: 'doc.command-cover-content',
type: CommandType.COMMAND,
handler: async (accessor, params: ICoverCommandParams) => {
const currentUniverService = accessor.get(ICurrentUniverService);
const { unitId, snapshot, clearUndoRedoStack } = params;
const doc = currentUniverService.getUniverDocInstance(unitId);
if (!doc) {
return false;
}
doc.getDocument().reset(snapshot || createEmptyDocSnapshot());
if (clearUndoRedoStack) {
const undoRedoService = accessor.get(IUndoRedoService);
undoRedoService.clearUndoRedo(unitId);
}
return true;
},
};
function getRetainAndDeleteFromReplace(range: ITextSelectionRange, segmentId?: string): Array<IRetainMutationParams | IDeleteMutationParams> {
const { cursorStart, cursorEnd, isEndBack, isStartBack, isCollapse } = range;
const dos: Array<IRetainMutationParams | IDeleteMutationParams> = [];
@@ -21,7 +21,7 @@ export function InsertApply(document: DocumentModel, insertBody: IDocumentBody,
updateAttributeByInsert(body, insertBody, textLength, currentIndex);
console.log('插入的model打印', bodyModel, body, textLength);
// console.log('插入的model打印', bodyModel, body, textLength);
}
function updateAttributeByInsert(body: IDocumentBody, insertBody: IDocumentBody, textLength: number, currentIndex: number) {
+14
View File
@@ -1 +1,15 @@
export {
CoverCommand,
DeleteCommand,
ICoverCommandParams,
IDeleteCommandParams,
IIMEInputCommandParams,
IInsertCommandParams,
IMEInputCommand,
InsertCommand,
IUpdateCommandParams,
UpdateCommand,
} from './commands/commands/core-editing.command';
export * from './DocPlugin';
export { DocsViewManagerService } from './services/docs-view-manager/docs-view-manager.service';
export { DocsView, DocsViewFactory } from './View/Render/Views/DocsView';
@@ -1,4 +0,0 @@
/**
* This services provides methods to edit the content of the document.
*/
export class DocEditingService {}
@@ -0,0 +1,59 @@
import { Disposable, fromObservable, ICurrentUniverService, toDisposable } from '@univerjs/core';
import { IDisposable } from '@wendellhu/redi';
import { Observable, Subject } from 'rxjs';
import type { DocsView } from '../../View/Render/Views/DocsView';
/**
* This services manages instances of doc CanvasView and determines which one
* the user is currently interacting with.
*/
export class DocsViewManagerService extends Disposable {
readonly current$: Observable<DocsView | null>;
private readonly _current$: Subject<DocsView | null> = new Subject();
private readonly _viewsMap = new Map<string, DocsView>();
constructor(@ICurrentUniverService private readonly _currentUniverService: ICurrentUniverService) {
super();
this.disposeWithMe(
fromObservable(
this._currentUniverService.focused$.subscribe((id) => {
if (!id) {
this._current$.next(null);
return;
}
// FIXME: if the focused id is a sheet ID, we should focus on the cell editor instead
const view = this._viewsMap.get(id);
if (view) {
this._current$.next(view ?? null);
}
})
)
);
this.current$ = this._current$.asObservable();
}
override dispose(): void {
super.dispose();
this._viewsMap.clear();
this._current$.complete();
}
registerCanvasViewForUniverInstance(unitId: string, docsView: DocsView): IDisposable {
this._viewsMap.set(unitId, docsView);
return toDisposable(() => {
this._viewsMap.delete(unitId);
});
}
getDocsView(unitId: string): DocsView | undefined {
return this._viewsMap.get(unitId);
}
}
@@ -1,7 +0,0 @@
import { createIdentifier } from '@wendellhu/redi';
export interface ISelectionManagerService {}
export const ISelectionManagerService = createIdentifier<ISelectionManagerService>('doc.selection-manager-service');
export class SelectionManagerService implements ISelectionManagerService {}
@@ -1,13 +1,15 @@
import { IShortcutItem, KeyCode } from '@univerjs/base-ui';
import { FOCUSING_DOC, IShortcutItem, KeyCode } from '@univerjs/base-ui';
import { BreakLineCommand, DeleteLeftCommand } from '../commands/commands/core-editing.command';
export const BreakLineShortcut: IShortcutItem = {
id: BreakLineCommand.id,
preconditions: (contextService) => contextService.getContextValue(FOCUSING_DOC),
binding: KeyCode.ENTER,
};
export const DeleteLeftShortcut: IShortcutItem = {
id: DeleteLeftCommand.id,
preconditions: (contextService) => contextService.getContextValue(FOCUSING_DOC),
binding: KeyCode.BACKSPACE,
};
@@ -1,4 +1,4 @@
import { IShortcutItem, KeyCode } from '@univerjs/base-ui';
import { FOCUSING_DOC, IShortcutItem, KeyCode } from '@univerjs/base-ui';
import { Direction } from '@univerjs/core';
import { MoveCursorOperation } from '../commands/operations/cursor.operation';
@@ -6,6 +6,7 @@ import { MoveCursorOperation } from '../commands/operations/cursor.operation';
export const MoveCursorUpShortcut: IShortcutItem = {
id: MoveCursorOperation.id,
binding: KeyCode.ARROW_UP,
preconditions: (contextService) => contextService.getContextValue(FOCUSING_DOC),
staticParameters: {
direction: Direction.UP,
},
@@ -14,6 +15,7 @@ export const MoveCursorUpShortcut: IShortcutItem = {
export const MoveCursorDownShortcut: IShortcutItem = {
id: MoveCursorOperation.id,
binding: KeyCode.ARROW_DOWN,
preconditions: (contextService) => contextService.getContextValue(FOCUSING_DOC),
staticParameters: {
direction: Direction.DOWN,
},
@@ -22,6 +24,7 @@ export const MoveCursorDownShortcut: IShortcutItem = {
export const MoveCursorLeftShortcut: IShortcutItem = {
id: MoveCursorOperation.id,
binding: KeyCode.ARROW_LEFT,
preconditions: (contextService) => contextService.getContextValue(FOCUSING_DOC),
staticParameters: {
direction: Direction.LEFT,
},
@@ -30,6 +33,7 @@ export const MoveCursorLeftShortcut: IShortcutItem = {
export const MoveCursorRightShortcut: IShortcutItem = {
id: MoveCursorOperation.id,
binding: KeyCode.ARROW_RIGHT,
preconditions: (contextService) => contextService.getContextValue(FOCUSING_DOC),
staticParameters: {
direction: Direction.RIGHT,
},
@@ -1,5 +1,6 @@
import {
BooleanNumber,
DocumentBodyModel,
GridType,
IDocStyleBase,
IDocumentLayout,
@@ -11,12 +12,12 @@ import {
ISize,
ITextStyle,
ITransformState,
DocumentBodyModel,
} from '@univerjs/core';
import {
IDocumentSkeletonDrawingAnchor,
IDocumentSkeletonBullet,
IDocumentSkeletonDrawing,
IDocumentSkeletonDrawingAnchor,
IDocumentSkeletonFontStyle,
IDocumentSkeletonFooter,
IDocumentSkeletonHeader,
@@ -1,6 +1,7 @@
import {
ColumnSeparatorType,
DocumentModelOrSimple,
fromObservable,
GridType,
HorizontalAlign,
ISectionBreak,
@@ -47,7 +48,15 @@ export class DocumentSkeleton extends Skeleton {
constructor(docModel: DocumentModelOrSimple, localeService: LocaleService) {
super(localeService);
this._docModel = docModel;
this.disposeWithMe(
fromObservable(
this._docModel.bodyModel.modelChange$.subscribe(() => {
this.calculate();
})
)
);
}
static create(docModel: DocumentModelOrSimple, localeService: LocaleService) {
@@ -126,9 +126,51 @@ export class DocsEditor {
this._scrollToSelection();
}
activate(toLastPosition = false): void {
// const documents = this._documents;
// if (!documents) {
// return;
// }
// setTimeout(() => {
// documents.makeDirty();
// });
// FIXME: I don't know if this is correct. Just trying it.
// const lastCharIndex = documents.getSkeleton()!.getModel().bodyModel.body.dataStream.length;
// const span = documents.findNodeByCharIndex(lastCharIndex - 1);
// const position = documents.findPositionBySpan(span!);
// const remain = this.remain();
// if (position && remain) {
// remain.startNodePosition = { ...position, isBack: true };
// remain.endNodePosition = undefined;
// }
// if (!this._documents) {
// return;
// }
// TODO
// this.activeViewport = this._documents.getActiveViewportByCoord(evtOffsetX, evtOffsetY);
// const startNode = this._documents.findNodeByCoord(evtOffsetX, evtOffsetY);
// console.log('startNode', startNode, position, evtOffsetX, evtOffsetY);
// if (position == null) {
// this._deleteAllTextSelection();
// return;
// }
// if (startNode?.node.streamType === DataStreamTreeTokenType.PARAGRAPH) {
// position.isBack = true;
// }
// if (evt.ctrlKey || this._isEmptyTextSelection()) {
// const newTextSelection = new TextSelection(this._documents.getScene(), position);
// this._addTextSelection(newTextSelection);
// } else {
// this._updateTextSelection(position);
// }
// this._activeSelectionRefresh();
// this._syncDomToSelection();
}
active(x: number, y: number) {
this._container.style.left = `${x}px`;
this._container.style.top = `${y}px`;
this._container.style.zIndex = `1000`;
this._cursor.style.animation = 'univer_cursor_blinkStyle 1s steps(1) infinite';
this._cursor.style.display = 'revert';
@@ -138,14 +180,21 @@ export class DocsEditor {
}, 0);
}
deactivate() {
focus(): void {
this._input.focus();
}
// FIXME: for editor cell editor we don't need to blur the input element
deactivate(withoutBlur = true) {
this._container.style.left = `0px`;
this._container.style.top = `0px`;
this._cursor.style.animation = '';
this._cursor.style.display = 'none';
this._input.blur();
if (!withoutBlur) {
this._input.blur();
}
}
changeDocuments(documents: DocComponent) {
@@ -169,23 +218,16 @@ export class DocsEditor {
private _initialDom() {
const container = document.createElement('div');
container.style.position = 'absolute';
container.style.position = 'absolute';
container.style.left = `0px`;
container.style.top = `0px`;
const inputParent = document.createElement('div');
const inputDom = document.createElement('div');
const cursorDom = document.createElement('div');
inputParent.appendChild(inputDom);
container.appendChild(inputParent);
container.appendChild(cursorDom);
this._container = container;
@@ -675,12 +717,11 @@ export class DocsEditor {
const scene = documents.getScene();
this._downObserver = documents.onPointerDownObserver.add((evt: IPointerEvent | IMouseEvent, state) => {
const { offsetX: evtOffsetX, offsetY: evtOffsetY } = evt;
if (!this._documents) {
return;
}
const { offsetX: evtOffsetX, offsetY: evtOffsetY } = evt;
this.activeViewport = this._documents.getActiveViewportByCoord(evtOffsetX, evtOffsetY);
const startNode = this._documents.findNodeByCoord(evtOffsetX, evtOffsetY);
@@ -90,6 +90,10 @@ export class Documents extends DocComponent {
return new Documents(oKey, documentSkeleton, config);
}
getEditor(): DocsEditor {
return this._editor;
}
getActiveSelection() {
return this._editor.getActiveTextSelection();
}
@@ -157,6 +161,7 @@ export class Documents extends DocComponent {
if (this._hasEditor) {
return;
}
this._editor = DocsEditor.create(this);
this._hasEditor = true;
}
@@ -212,6 +217,11 @@ export class Documents extends DocComponent {
return;
}
const isDrawingCellEditor = documentSkeleton.getModel().snapshot.id === 'sheet.model.cell-editor';
if (isDrawingCellEditor) {
console.log('drawing', documentSkeleton, documentSkeleton.getModel().snapshot);
}
// if (this.isCalculateSkeleton) {
// documentSkeleton.calculate(bounds);
// }
@@ -1,15 +1,17 @@
import { LocaleService } from '@univerjs/core';
import { Disposable, LocaleService } from '@univerjs/core';
import { Inject } from '@wendellhu/redi';
import { IFontLocale } from '../Basics/Interfaces';
import { en, zh } from '../Locale';
export class Skeleton {
export class Skeleton extends Disposable {
private _fontLocale: IFontLocale;
private _dirty = true;
constructor(@Inject(LocaleService) protected readonly _localService: LocaleService) {
super();
this._localeInitial();
}
+3 -1
View File
@@ -6,6 +6,7 @@ import { TRANSFORM_CHANGE_OBSERVABLE_TYPE } from './Basics/Interfaces';
import { PerformanceMonitor } from './Basics/PerformanceMonitor';
import { getPointerPrefix, getSizeForDom, IsSafari, requestNewFrame } from './Basics/Tools';
import { Canvas } from './Canvas';
import { fromWindowEvent } from './common/lifecycle';
import { Scene } from './Scene';
import { ThinEngine } from './ThinEngine';
@@ -111,7 +112,9 @@ export class Engine extends ThinEngine<Scene> {
setContainer(elem: HTMLElement) {
this._container = elem;
this._container.appendChild(this._canvasEle);
this.resize();
this.disposeWithMe(fromWindowEvent('resize', () => this.resize()));
}
resize() {
@@ -119,7 +122,6 @@ export class Engine extends ThinEngine<Scene> {
return;
}
// const { offsetWidth, offsetHeight } = this._container;
const { width, height } = getSizeForDom(this._container);
this.resizeBySize(width, height);
}
+6 -12
View File
@@ -1,24 +1,18 @@
import { Plugin, PLUGIN_NAMES, PluginType } from '@univerjs/core';
import { createIdentifier, Injector, Optional } from '@wendellhu/redi';
import { createIdentifier, Inject, Injector } from '@wendellhu/redi';
import { Engine } from './Engine';
/**
* The global rendering engine.
*/
export const IRenderingEngine = createIdentifier<Engine>('univer.render-engine');
export class RenderEngine extends Plugin {
static override type = PluginType.Univer;
private _engine: Engine;
constructor(_config: undefined, @Optional(Injector) override readonly _injector: Injector) {
constructor(_config: undefined, @Inject(Injector) override readonly _injector: Injector) {
super(PLUGIN_NAMES.BASE_RENDER);
this._engine = new Engine();
this._injector?.add([IRenderingEngine, { useFactory: () => this._engine }]);
}
getEngine(): Engine {
return this._engine;
this._injector.add([IRenderingEngine, { useFactory: () => new Engine() }]);
}
}
@@ -1,9 +1,10 @@
import { Observer, Nullable } from '@univerjs/core';
import { IKeyboardEvent, IMouseEvent, IPointerEvent, IEvent, DeviceType, PointerInput, IWheelEvent } from './Basics/IEvents';
import { Vector2 } from './Basics/Vector2';
import { Nullable, Observer } from '@univerjs/core';
import { BaseObject } from './BaseObject';
import { ThinScene } from './ThinScene';
import { RENDER_CLASS_TYPE } from './Basics/Const';
import { DeviceType, IEvent, IKeyboardEvent, IMouseEvent, IPointerEvent, IWheelEvent, PointerInput } from './Basics/IEvents';
import { Vector2 } from './Basics/Vector2';
import { ThinScene } from './ThinScene';
import { Viewport } from './Viewport';
export class InputManager {
+6 -4
View File
@@ -1,10 +1,11 @@
import { IKeyValue, Nullable } from '@univerjs/core';
import { IBoundRect, Vector2 } from './Basics/Vector2';
import { BaseObject } from './BaseObject';
import { ThinScene } from './ThinScene';
import { IObjectFullState } from './Basics/Interfaces';
import { RENDER_CLASS_TYPE } from './Basics/Const';
import { IObjectFullState } from './Basics/Interfaces';
import { transformBoundingCoord } from './Basics/Position';
import { IBoundRect, Vector2 } from './Basics/Vector2';
import { ThinScene } from './ThinScene';
export class SceneViewer extends BaseObject {
private _subScenes = new Map<string, ThinScene>();
@@ -14,6 +15,7 @@ export class SceneViewer extends BaseObject {
private _allowSelectedClipElement = false;
constructor(key?: string, props?: IObjectFullState) {
// WTF: the name of `props`'s interface ends with State?
super(key);
this._initialProps(props);
}
@@ -56,7 +58,7 @@ export class SceneViewer extends BaseObject {
}
getSubScene(sceneKey: string) {
for (let [key, scene] of this._subScenes) {
for (const [key, scene] of this._subScenes) {
if (key === sceneKey) {
return scene;
}
+16 -10
View File
@@ -1,9 +1,13 @@
import { Observable } from '@univerjs/core';
import { CURSOR_TYPE, RENDER_CLASS_TYPE } from './Basics/Const';
import { ITransformChangeState } from './Basics/Interfaces';
import { IEvent } from './Basics/IEvents';
import { Disposable, Observable } from '@univerjs/core';
import { IDisposable } from '@wendellhu/redi';
export abstract class ThinEngine<T> {
import { CURSOR_TYPE, RENDER_CLASS_TYPE } from './Basics/Const';
import { IEvent } from './Basics/IEvents';
import { ITransformChangeState } from './Basics/Interfaces';
import { Canvas } from './Canvas';
// FIXME: T should extends something that is disposable
export abstract class ThinEngine<T extends IDisposable> extends Disposable {
onInputChangedObservable = new Observable<IEvent>();
onTransformChangeObservable = new Observable<ITransformChangeState>();
@@ -62,7 +66,9 @@ export abstract class ThinEngine<T> {
return this._activeScene != null;
}
dispose() {
override dispose() {
super.dispose();
const scenes = this.getScenes();
const sceneKeys = Object.keys(scenes);
sceneKeys.forEach((key) => {
@@ -79,11 +85,11 @@ export abstract class ThinEngine<T> {
}
}
clearCanvas() {}
abstract clearCanvas(): void;
getCanvas() {}
abstract getCanvas(): Canvas;
getCanvasElement() {}
abstract getCanvasElement(): HTMLCanvasElement;
setCanvasCursor(val: CURSOR_TYPE) {}
abstract setCanvasCursor(val: CURSOR_TYPE): void;
}
+4 -7
View File
@@ -1,14 +1,11 @@
import { EventState, IKeyValue, Nullable, Observable, Observer } from '@univerjs/core';
import { EVENT_TYPE, RENDER_CLASS_TYPE } from './Basics/Const';
import { ITransformChangeState } from './Basics/Interfaces';
import { Transform } from './Basics/Transform';
import { IBoundRect, Vector2 } from './Basics/Vector2';
import { BaseObject } from './BaseObject';
import { EVENT_TYPE, RENDER_CLASS_TYPE } from './Basics/Const';
import { IKeyboardEvent, IMouseEvent, IPointerEvent, IWheelEvent } from './Basics/IEvents';
import { ITransformChangeState } from './Basics/Interfaces';
import { Transform } from './Basics/Transform';
import { IBoundRect, Vector2 } from './Basics/Vector2';
export abstract class ThinScene {
onTransformChangeObservable = new Observable<ITransformChangeState>();
@@ -0,0 +1,7 @@
import { toDisposable } from '@univerjs/core';
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function fromWindowEvent<K extends keyof WindowEventMap>(type: K, listener: (this: Window, ev: WindowEventMap[K]) => any, options?: boolean | AddEventListenerOptions) {
window.addEventListener(type, listener, options);
return toDisposable(() => window.removeEventListener(type, listener, options));
}
+8 -8
View File
@@ -1,15 +1,15 @@
export * from './Locale';
export * from './BaseObject';
export * from './Basics';
export * from './Canvas';
export * from './Component';
export * from './Custom';
export * from './Engine';
export * from './BaseObject';
export * from './Group';
export * from './Layer';
export * from './Locale';
export { IRenderingEngine, RenderEngine } from './RenderEngine';
export * from './Scene';
export * from './SceneViewer';
export * from './Viewport';
export * from './Canvas';
export * from './Group';
export * from './Shape';
export * from './Component';
export { RenderEngine, IRenderingEngine } from './RenderEngine';
export * from './ScrollTimer';
export * from './Shape';
export * from './Viewport';
@@ -76,7 +76,7 @@ export const AddWorksheetMergeCommand: ICommand = {
if (result && removeResult) {
undoRedoService.pushUndoRedo({
URI: 'sheet',
URI: workbookId,
undo() {
return (commandService.executeCommand(RemoveWorksheetMergeMutation.id, undoMutationParams) as Promise<boolean>).then((res) => {
if (res) return commandService.executeCommand(AddWorksheetMergeMutation.id, undoRemoveMergeMutationParams);
@@ -45,7 +45,7 @@ export const ClearSelectionContentCommand: ICommand = {
undoRedoService.pushUndoRedo({
// 如果有多个 mutation 构成一个封装项目,那么要封装在同一个 undo redo element 里面
// 通过勾子可以 hook 外部 controller 的代码来增加新的 action
URI: 'sheet', // TODO: this URI is fake
URI: workbookId,
undo() {
return (commandService.executeCommand(SetRangeValuesMutation.id, undoClearMutationParams) as Promise<boolean>).then((res) => {
if (res) return commandService.executeCommand(SetRangeStyleMutation.id, setStyleMutationParams);
@@ -62,7 +62,7 @@ export const CopyFormatToRangeCommand: ICommand = {
const result = commandService.executeCommand(SetRangeStyleMutation.id, setRangeStyleMutationParams);
if (result) {
undoRedoService.pushUndoRedo({
URI: 'sheet',
URI: workbookId,
undo() {
return commandService.executeCommand(SetRangeStyleMutation.id, undoMutationParams);
},
@@ -62,7 +62,7 @@ export const CopyRangeToCommand: ICommand = {
const result = commandService.executeCommand(SetRangeValuesMutation.id, setRangeValuesMutationParams);
if (result) {
undoRedoService.pushUndoRedo({
URI: 'sheet',
URI: workbookId,
undo() {
return commandService.executeCommand(SetRangeValuesMutation.id, undoMutationParams);
},
@@ -43,7 +43,7 @@ export const CopySheetToCommand: ICommand = {
const result = commandService.executeCommand(SetWorksheetConfigMutation.id, setWorksheetConfigMutationParams);
if (result) {
undoRedoService.pushUndoRedo({
URI: 'sheet',
URI: workbookId,
undo() {
return commandService.executeCommand(SetWorksheetConfigMutation.id, undoMutationParams);
},
@@ -59,7 +59,7 @@ export const CopyValuesToRangeCommand: ICommand = {
const result = commandService.executeCommand(SetRangeFormattedValueMutation.id, setRangeFormattedValueMutationParams);
if (result) {
undoRedoService.pushUndoRedo({
URI: 'sheet',
URI: workbookId,
undo() {
return commandService.executeCommand(SetRangeFormattedValueMutation.id, undoMutationParams);
},
@@ -54,7 +54,7 @@ export const CopySheetCommand: ICommand = {
if (insertResult && result) {
undoRedoService.pushUndoRedo({
URI: 'sheet',
URI: workbookId,
undo() {
return (commandService.executeCommand(SetWorksheetActivateMutation.id, undoMutationParams) as Promise<boolean>).then((res) => {
if (res) return commandService.executeCommand(RemoveSheetMutation.id, removeSheetMutationParams);
@@ -45,7 +45,7 @@ export const DeleteRangeMoveLeftCommand: ICommand = {
undoRedoService.pushUndoRedo({
// 如果有多个 mutation 构成一个封装项目,那么要封装在同一个 undo redo element 里面
// 通过勾子可以 hook 外部 controller 的代码来增加新的 action
URI: 'sheet', // TODO: this URI is fake
URI: workbookId,
undo() {
return commandService.executeCommand(InsertRangeMutation.id, insertRangeMutationParams);
},
@@ -45,7 +45,7 @@ export const DeleteRangeMoveUpCommand: ICommand = {
undoRedoService.pushUndoRedo({
// 如果有多个 mutation 构成一个封装项目,那么要封装在同一个 undo redo element 里面
// 通过勾子可以 hook 外部 controller 的代码来增加新的 action
URI: 'sheet', // TODO: this URI is fake
URI: workbookId,
undo() {
return commandService.executeCommand(InsertRangeMutation.id, insertRangeMutationParams);
},
@@ -56,7 +56,7 @@ export const InsertRangeMoveDownCommand: ICommand = {
undoRedoService.pushUndoRedo({
// 如果有多个 mutation 构成一个封装项目,那么要封装在同一个 undo redo element 里面
// 通过勾子可以 hook 外部 controller 的代码来增加新的 action
URI: 'sheet', // TODO: this URI is fake
URI: workbookId,
undo() {
return commandService.executeCommand(DeleteRangeMutation.id, deleteRangeMutationParams);
},
@@ -56,7 +56,7 @@ export const InsertRangeMoveRightCommand: ICommand = {
undoRedoService.pushUndoRedo({
// 如果有多个 mutation 构成一个封装项目,那么要封装在同一个 undo redo element 里面
// 通过勾子可以 hook 外部 controller 的代码来增加新的 action
URI: 'sheet', // TODO: this URI is fake
URI: workbookId,
undo() {
return commandService.executeCommand(DeleteRangeMutation.id, deleteRangeMutationParams);
},
@@ -103,7 +103,7 @@ export const InsertRowCommand: ICommand = {
if (result && deleteResult && removeResult && mergeResult) {
undoRedoService.pushUndoRedo({
URI: 'sheet',
URI: workbookId,
undo() {
return (commandService.executeCommand(DeleteRangeMutation.id, deleteRangeMutationParams) as Promise<boolean>)
.then((res) => {
@@ -312,7 +312,7 @@ export const InsertColCommand: ICommand<InsertColCommandBaseParams> = {
if (result && deleteResult && removeResult && mergeResult) {
undoRedoService.pushUndoRedo({
URI: 'sheet',
URI: workbookId,
undo() {
return (commandService.executeCommand(DeleteRangeMutation.id, deleteRangeMutationParams) as Promise<boolean>)
.then((res) => {
@@ -66,7 +66,7 @@ export const InsertSheetCommand: ICommand = {
if (result && activateResult) {
undoRedoService.pushUndoRedo({
URI: 'sheet',
URI: workbookId,
undo() {
return (commandService.executeCommand(SetWorksheetActivateMutation.id, undoMutationParams) as Promise<boolean>).then((res) => {
if (res) return commandService.executeCommand(RemoveSheetMutation.id, removeSheetMutationParams);
@@ -69,7 +69,7 @@ export const MoveColumnsToCommand: ICommand = {
undoRedoService.pushUndoRedo({
// 如果有多个 mutation 构成一个封装项目,那么要封装在同一个 undo redo element 里面
// 通过勾子可以 hook 外部 controller 的代码来增加新的 action
URI: 'sheet', // TODO: this URI is fake
URI: workbookId,
undo() {
return (commandService.executeCommand(InsertColMutation.id, undoRemoveColumnMutationParams) as Promise<boolean>).then((res) => {
if (res) return commandService.executeCommand(RemoveColMutation.id, undoMutationParams);
@@ -64,7 +64,7 @@ export const MoveRangeToCommand: ICommand = {
undoRedoService.pushUndoRedo({
// 如果有多个 mutation 构成一个封装项目,那么要封装在同一个 undo redo element 里面
// 通过勾子可以 hook 外部 controller 的代码来增加新的 action
URI: 'sheet', // TODO: this URI is fake
URI: workbookId,
undo() {
return (commandService.executeCommand(SetRangeValuesMutation.id, undoClearMutationParams) as Promise<boolean>).then((res) => {
if (res) return commandService.executeCommand(SetRangeValuesMutation.id, undoMutationParams);
@@ -69,7 +69,7 @@ export const MoveRowsToCommand: ICommand = {
undoRedoService.pushUndoRedo({
// 如果有多个 mutation 构成一个封装项目,那么要封装在同一个 undo redo element 里面
// 通过勾子可以 hook 外部 controller 的代码来增加新的 action
URI: 'sheet', // TODO: this URI is fake
URI: workbookId,
undo() {
return (commandService.executeCommand(InsertRowMutation.id, undoRemoveRowMutationParams) as Promise<boolean>).then((res) => {
if (res) return commandService.executeCommand(RemoveRowMutation.id, undoMutationParams);
@@ -107,7 +107,7 @@ export const RemoveRowCommand: ICommand = {
if (result && deleteResult && removeResult && mergeResult) {
undoRedoService.pushUndoRedo({
URI: 'sheet',
URI: workbookId,
undo() {
return (commandService.executeCommand(InsertRowMutation.id, undoMutationParams) as Promise<boolean>)
.then((res) => {
@@ -233,7 +233,7 @@ export const RemoveColCommand: ICommand = {
if (result && deleteResult && removeResult && mergeResult) {
undoRedoService.pushUndoRedo({
URI: 'sheet',
URI: workbookId,
undo() {
return (commandService.executeCommand(InsertColMutation.id, undoMutationParams) as Promise<boolean>)
.then((res) => {
@@ -56,7 +56,7 @@ export const RemoveSheetCommand: ICommand = {
if (result && activeResult) {
undoRedoService.pushUndoRedo({
URI: 'sheet',
URI: workbookId,
undo() {
return (commandService.executeCommand(InsertSheetMutation.id, InsertSheetMutationParams) as Promise<boolean>).then((res) => {
if (res) return commandService.executeCommand(SetWorksheetActivateMutation.id, activeMutationParams);
@@ -36,7 +36,7 @@ export const RemoveWorksheetMergeCommand: ICommand = {
if (result) {
undoRedoService.pushUndoRedo({
URI: 'sheet',
URI: workbookId,
undo() {
return commandService.executeCommand(AddWorksheetMergeMutation.id, undoMutationParams);
},
@@ -330,7 +330,7 @@ export const SetBorderCommand: ICommand<ISetBorderCommandParams> = {
undoRedoService.pushUndoRedo({
// 如果有多个 mutation 构成一个封装项目,那么要封装在同一个 undo redo element 里面
// 通过勾子可以 hook 外部 controller 的代码来增加新的 action
URI: 'sheet', // TODO: this URI is fake
URI: workbookId,
undo() {
return commandService.executeCommand(SetBorderStylesMutation.id, undoSetBorderStylesMutationParams);
},
@@ -317,7 +317,7 @@ export const SetBorderCommand: ICommand = {
undoRedoService.pushUndoRedo({
// 如果有多个 mutation 构成一个封装项目,那么要封装在同一个 undo redo element 里面
// 通过勾子可以 hook 外部 controller 的代码来增加新的 action
URI: 'sheet', // TODO: this URI is fake
URI: workbookId,
undo() {
return commandService.executeCommand(SetBorderStylesMutation.id, undoSetBorderStylesMutationParams);
},
@@ -40,7 +40,7 @@ export const SetFrozenCancelCommand: ICommand = {
if (result && frozenResult) {
undoRedoService.pushUndoRedo({
URI: 'sheet',
URI: workbookId,
undo() {
return (commandService.executeCommand(SetFrozenColumnsMutation.id, frozenColumnMutationParams) as Promise<boolean>).then((res) => {
if (res) return commandService.executeCommand(SetFrozenRowsMutation.id, undoMutationParams);
@@ -34,7 +34,7 @@ export const SetFrozenColumnsCommand: ICommand = {
if (result) {
undoRedoService.pushUndoRedo({
URI: 'sheet',
URI: workbookId,
undo() {
return commandService.executeCommand(SetFrozenColumnsMutation.id, undoMutationParams);
},
@@ -34,7 +34,7 @@ export const SetFrozenRowsCommand: ICommand = {
if (result) {
undoRedoService.pushUndoRedo({
URI: 'sheet',
URI: workbookId,
undo() {
return commandService.executeCommand(SetFrozenRowsMutation.id, undoMutationParams);
},
@@ -43,7 +43,7 @@ export const SetHideGridlinesCommand: ICommand = {
if (result) {
undoRedoService.pushUndoRedo({
URI: 'sheet',
URI: workbookId,
undo() {
return commandService.executeCommand(SetHideGridlinesMutation.id, undoMutationParams);
},
@@ -74,7 +74,7 @@ export const SetRangeFormattedValueCommand: ICommand = {
undoRedoService.pushUndoRedo({
// 如果有多个 mutation 构成一个封装项目,那么要封装在同一个 undo redo element 里面
// 通过勾子可以 hook 外部 controller 的代码来增加新的 action
URI: 'sheet', // TODO: this URI is fake
URI: workbookId,
undo() {
return commandService.executeCommand(SetRangeFormattedValueMutation.id, undoSetRangeFormattedValueMutationParams);
},
@@ -1,4 +1,16 @@
import { CommandType, ICellData, ICommand, ICommandService, ICurrentUniverService, IUndoRedoService, ObjectMatrix, ObjectMatrixPrimitiveType, Tools } from '@univerjs/core';
import {
CommandType,
ICellData,
ICommand,
ICommandService,
ICurrentUniverService,
IRangeData,
isICellData,
IUndoRedoService,
ObjectMatrix,
ObjectMatrixPrimitiveType,
Tools,
} from '@univerjs/core';
import { IAccessor } from '@wendellhu/redi';
import { SelectionManagerService } from '../../Services/selection-manager.service';
@@ -7,6 +19,7 @@ import { ISetRangeValuesMutationParams, SetRangeValuesMutation, SetRangeValuesUn
export interface ISetRangeValuesCommandParams {
worksheetId?: string;
workbookId?: string;
range?: IRangeData;
value: ICellData | ICellData[][] | ObjectMatrixPrimitiveType<ICellData>;
}
@@ -22,47 +35,49 @@ export const SetRangeValuesCommand: ICommand = {
const currentUniverService = accessor.get(ICurrentUniverService);
const selectionManagerService = accessor.get(SelectionManagerService);
const range = selectionManagerService.getRangeDatas();
if (!range?.length) {
const {
value,
range,
workbookId = currentUniverService.getCurrentUniverSheetInstance().getUnitId(),
worksheetId = currentUniverService.getCurrentUniverSheetInstance().getWorkBook().getActiveSheet().getSheetId(),
} = params;
const currentSelections = range ? [range] : selectionManagerService.getRangeDatas();
if (!currentSelections || !currentSelections.length) {
return false;
}
const workbookId = params.workbookId || currentUniverService.getCurrentUniverSheetInstance().getUnitId();
const worksheetId = params.worksheetId || currentUniverService.getCurrentUniverSheetInstance().getWorkBook().getActiveSheet().getSheetId();
const cellValue = new ObjectMatrix<ICellData>();
let realCellValue: ObjectMatrixPrimitiveType<ICellData> | undefined;
const { value } = params;
let cellValue = new ObjectMatrix<ICellData>();
for (let i = 0; i < range.length; i++) {
const { startRow, startColumn, endRow, endColumn } = range[i];
// FIXME: this is actually not ideal
for (let i = 0; i < currentSelections.length; i++) {
const { startRow, startColumn, endRow, endColumn } = currentSelections[i];
if (Tools.isArray(value)) {
for (let r = 0; r <= endRow - startRow; r++) {
for (let c = 0; c <= endColumn - startColumn; c++) {
cellValue.setValue(r + startRow, c + startColumn, value[r][c]);
}
}
} else if (Tools.isObject(value)) {
cellValue = value as ObjectMatrix<ICellData>;
} else {
} else if (isICellData(value)) {
cellValue.setValue(startRow, startColumn, value);
} else {
realCellValue = value as ObjectMatrixPrimitiveType<ICellData>;
}
}
const setRangeValuesMutationParams: ISetRangeValuesMutationParams = {
rangeData: range,
rangeData: currentSelections,
worksheetId,
workbookId,
cellValue: cellValue as any,
cellValue: realCellValue ?? cellValue.getMatrix(),
};
const undoSetRangeValuesMutationParams: ISetRangeValuesMutationParams = SetRangeValuesUndoMutationFactory(accessor, setRangeValuesMutationParams);
// execute do mutations and add undo mutations to undo stack if completed
const result = commandService.executeCommand(SetRangeValuesMutation.id, setRangeValuesMutationParams);
if (result) {
undoRedoService.pushUndoRedo({
// 如果有多个 mutation 构成一个封装项目,那么要封装在同一个 undo redo element 里面
// 通过勾子可以 hook 外部 controller 的代码来增加新的 action
URI: 'sheet', // TODO: this URI is fake
URI: workbookId,
undo() {
return commandService.executeCommand(SetRangeValuesMutation.id, undoSetRangeValuesMutationParams);
},
@@ -94,7 +94,7 @@ export const SetStyleCommand: ICommand<ISetStyleParams<unknown>> = {
const result = commandService.executeCommand(SetRangeStyleMutation.id, setRangeStyleMutationParams);
if (result) {
undoRedoService.pushUndoRedo({
URI: 'sheet', // TODO: this URI is fake: would be replace with Univer business instance id
URI: workbookId,
undo() {
return commandService.executeCommand(SetRangeStyleMutation.id, undoSetRangeStyleMutationParams);
},
@@ -35,7 +35,7 @@ export const SetTabColorCommand: ICommand = {
if (result) {
undoRedoService.pushUndoRedo({
URI: 'sheet',
URI: workbookId,
undo() {
return commandService.executeCommand(SetTabColorMutation.id, undoMutationParams);
},
@@ -35,7 +35,7 @@ export const SetWorksheetActivateCommand: ICommand = {
// no need
// if (result) {
// undoRedoService.pushUndoRedo({
// URI: 'sheet',
// URI: workbookId,
// undo() {
// return commandService.executeCommand(SetWorksheetActivateMutation.id, undoMutationParams);
// },
@@ -36,7 +36,7 @@ export const SetWorksheetColWidthCommand: ICommand = {
const result = commandService.executeCommand(SetWorksheetColWidthMutation.id, redoMutationParams);
if (result) {
undoRedoService.pushUndoRedo({
URI: 'sheet',
URI: workbookId,
undo() {
return commandService.executeCommand(SetWorksheetColWidthMutation.id, undoMutationParams);
},
@@ -36,7 +36,7 @@ export const SetWorksheetColumnHideCommand: ICommand = {
const result = commandService.executeCommand(SetWorksheetColumnHideMutation.id, redoMutationParams);
if (result) {
undoRedoService.pushUndoRedo({
URI: 'sheet',
URI: workbookId,
undo() {
return commandService.executeCommand(SetWorksheetColumnShowMutation.id, undoMutationParams);
},
@@ -37,7 +37,7 @@ export const SetWorksheetColumnShowCommand: ICommand = {
if (result) {
undoRedoService.pushUndoRedo({
URI: 'sheet',
URI: workbookId,
undo() {
return commandService.executeCommand(SetWorksheetColumnHideMutation.id, undoMutationParams);
},
@@ -54,7 +54,7 @@ export const SetWorksheetHideCommand: ICommand = {
if (result && activeResult) {
undoRedoService.pushUndoRedo({
URI: 'sheet',
URI: workbookId,
undo() {
return (commandService.executeCommand(SetWorksheetActivateMutation.id, unActiveMutationParams) as Promise<boolean>).then((res) => {
if (res) return commandService.executeCommand(SetWorksheetHideMutation.id, undoMutationParams);
@@ -34,7 +34,7 @@ export const SetWorksheetNameCommand: ICommand = {
if (result) {
undoRedoService.pushUndoRedo({
URI: 'sheet',
URI: workbookId,
undo() {
return commandService.executeCommand(SetWorksheetNameMutation.id, undoMutationParams);
},
@@ -36,7 +36,7 @@ export const SetWorksheetOrderCommand: ICommand = {
if (result) {
undoRedoService.pushUndoRedo({
URI: 'sheet',
URI: workbookId,
undo() {
return commandService.executeCommand(SetWorksheetOrderMutation.id, undoMutationParams);
},
@@ -47,7 +47,7 @@ export const SetWorksheetRightToLeftCommand: ICommand = {
if (result) {
undoRedoService.pushUndoRedo({
URI: 'sheet',
URI: workbookId,
undo() {
return commandService.executeCommand(SetWorksheetRightToLeftMutation.id, undoMutationParams);
},
@@ -37,7 +37,7 @@ export const SetWorksheetRowHeightCommand: ICommand = {
const result = commandService.executeCommand(SetWorksheetRowHeightMutation.id, redoMutationParams);
if (result) {
undoRedoService.pushUndoRedo({
URI: 'sheet',
URI: workbookId,
undo() {
return commandService.executeCommand(SetWorksheetRowHeightMutation.id, undoMutationParams);
},
@@ -36,7 +36,7 @@ export const SetWorksheetRowHideCommand: ICommand = {
const result = commandService.executeCommand(SetWorksheetRowHideMutation.id, redoMutationParams);
if (result) {
undoRedoService.pushUndoRedo({
URI: 'sheet',
URI: workbookId,
undo() {
return commandService.executeCommand(SetWorksheetRowShowMutation.id, undoMutationParams);
},
@@ -37,7 +37,7 @@ export const SetWorksheetRowShowCommand: ICommand = {
if (result) {
undoRedoService.pushUndoRedo({
URI: 'sheet',
URI: workbookId,
undo() {
return commandService.executeCommand(SetWorksheetRowHideMutation.id, undoMutationParams);
},
@@ -50,7 +50,7 @@ export const SetWorksheetShowCommand: ICommand = {
if (result && activeResult) {
undoRedoService.pushUndoRedo({
URI: 'sheet',
URI: workbookId,
undo() {
return (commandService.executeCommand(SetWorksheetActivateMutation.id, unActiveMutationParams) as Promise<boolean>).then((res) => {
if (res) return commandService.executeCommand(SetWorksheetHideMutation.id, undoMutationParams);
@@ -43,7 +43,7 @@ export const SetZoomRatioCommand: ICommand = {
if (result) {
undoRedoService.pushUndoRedo({
URI: 'sheet',
URI: workbookId,
undo() {
return commandService.executeCommand(SetZoomRatioMutation.id, undoMutationParams);
},
@@ -57,7 +57,7 @@ export const TrimWhitespaceCommand: ICommand = {
undoRedoService.pushUndoRedo({
// 如果有多个 mutation 构成一个封装项目,那么要封装在同一个 undo redo element 里面
// 通过勾子可以 hook 外部 controller 的代码来增加新的 action
URI: 'sheet', // TODO: this URI is fake
URI: workbookId,
undo() {
return commandService.executeCommand(SetRangeFormattedValueMutation.id, undoSetRangeFormattedValueMutationParams);
},
@@ -1,15 +1,4 @@
import {
CommandType,
createRowColIter,
ICellData,
ICopyToOptionsData,
ICurrentUniverService,
IMutation,
IRangeData,
ObjectMatrix,
ObjectMatrixPrimitiveType,
Tools,
} from '@univerjs/core';
import { CommandType, ICellData, ICopyToOptionsData, ICurrentUniverService, IMutation, IRangeData, ObjectMatrix, ObjectMatrixPrimitiveType, Tools } from '@univerjs/core';
import { IAccessor } from '@wendellhu/redi';
/** Params of `SetRangeValuesMutation` */
@@ -57,6 +46,7 @@ export const SetRangeValuesUndoMutationFactory = (accessor: IAccessor, params: I
} as ISetRangeValuesMutationParams;
};
// TODO@Dushusir: this would cover style as well. Which is not expected.
export const SetRangeValuesMutation: IMutation<ISetRangeValuesMutationParams, boolean> = {
id: 'sheet.mutation.set-range-values',
type: CommandType.MUTATION,
@@ -76,11 +66,11 @@ export const SetRangeValuesMutation: IMutation<ISetRangeValuesMutationParams, bo
const { startRow, startColumn, endColumn, endRow } = rangeData[i];
// clear selection content
createRowColIter(startRow, endRow, startColumn, endColumn).forEach((r, c) => {
if (cellMatrix.getValue(r, c)) {
cellMatrix.setValue(r, c, { v: null });
}
});
// createRowColIter(startRow, endRow, startColumn, endColumn).forEach((r, c) => {
// if (cellMatrix.getValue(r, c)) {
// cellMatrix.setValue(r, c, { v: null });
// }
// });
newValues.forValue((row, col, newVal) => {
const oldVal = cellMatrix.getValue(row, col) || {};
@@ -91,23 +81,29 @@ export const SetRangeValuesMutation: IMutation<ISetRangeValuesMutationParams, bo
v: null,
});
} else {
// TODO: @wzhudev: add config to change value or format only
let dirty = false;
if (newVal.p != null) {
oldVal.p = newVal.p;
dirty = true;
}
if (newVal.v != null) {
oldVal.v = newVal.v;
dirty = true;
}
if (newVal.m != null) {
oldVal.m = newVal.m;
dirty = true;
} else {
oldVal.m = String(oldVal.v);
dirty = true;
}
if (newVal.t != null) {
oldVal.t = newVal.t;
dirty = true;
}
cellMatrix.setValue(row, col, oldVal);
@@ -1,117 +0,0 @@
import { Direction, handleJsonToDom, ICellData, ICommandService, ICurrentUniverService, IDocumentData, IRangeData, IStyleData, Nullable } from '@univerjs/core';
import { Inject } from '@wendellhu/redi';
import { SetSelectionsOperation } from '../Commands/Operations/selection.operation';
import { SelectionManagerService } from '../Services/selection-manager.service';
/**
* Cell Editor
*/
export class CellEditorController {
isEditMode: boolean;
// current edit cell
currentEditRangeData: IRangeData;
constructor(
@ICurrentUniverService private readonly _currentUniverService: ICurrentUniverService,
@Inject(SelectionManagerService) private readonly _selectionManagerService: SelectionManagerService,
@ICommandService private readonly _commandService: ICommandService
) {
this._commandService.registerCommand(SetSelectionsOperation);
}
setEditMode(value: boolean) {
this.isEditMode = value;
}
getEditMode() {
return this.isEditMode;
}
setCurrentEditRangeData() {
const currentCell = this._selectionManagerService.getLast()?.cellRange;
if (!currentCell) return;
let row;
let column;
if (currentCell.isMerged) {
row = currentCell.startRow;
column = currentCell.startColumn;
} else {
row = currentCell.row;
column = currentCell.column;
}
this.currentEditRangeData = {
startRow: row,
startColumn: column,
endRow: row,
endColumn: column,
};
}
getCurrentEditRangeData() {
return this.currentEditRangeData;
}
setCurrentEditRangeValue(cell: ICellData) {
// only one selection
if (this.currentEditRangeData == null) {
return;
}
const { startRow, startColumn, endRow, endColumn } = this.currentEditRangeData;
const range = this._currentUniverService.getCurrentUniverSheetInstance().getWorkBook().getActiveSheet().getRange(startRow, startColumn, endRow, endColumn);
// range.setRangeData(cell);
}
getSelectionValue(): string {
const range = this.getActiveRange();
const value = range && range.getDisplayValue();
if (typeof value === 'string') {
return value;
}
if (typeof value === 'object' && value !== null) {
return handleJsonToDom(value);
}
return '';
}
getSelectionStyle(): Nullable<IStyleData> {
return this.getActiveRange()?.getTextStyle();
}
setSelectionValue(value: IDocumentData | string) {
const range = this.getActiveRange();
if (!range) return;
if (typeof value === 'string') {
// range.setValue(value);
}
if (typeof value === 'object') {
// range.setRangeData({ p: value });
}
}
getActiveRange() {
const cellRange = this._selectionManagerService.getLast()?.cellRange;
if (!cellRange) return;
let { row, column } = cellRange;
if (cellRange.isMerged) {
row = cellRange.startRow;
column = cellRange.startColumn;
}
return this._currentUniverService.getCurrentUniverSheetInstance().getWorkBook().getActiveSheet().getRange(row, column);
}
handleBackSpace() {}
handleDirection(direction: Direction) {
// todo
}
}
@@ -24,13 +24,11 @@ export class SheetContainerController extends Disposable {
private _initialize() {
this.disposeWithMe(
this._commandService.onCommandExecuted((command: ICommandInfo) => {
// refresh selection and worksheet canvas view
if (updateCommandList.includes(command.id)) {
const workbook = this._currentUniverService.getCurrentUniverSheetInstance().getWorkBook();
const unitId = workbook.getUnitId();
const worksheet = this._currentUniverService.getCurrentUniverSheetInstance().getWorkBook().getActiveSheet();
const worksheet = workbook.getActiveSheet();
const sheetId = worksheet.getSheetId();
// this._selectionManager.updateToSheet(worksheet);
this._selectionManagerService.setCurrentSelection({
pluginName: NORMAL_SELECTION_PLUGIN_NAME,
@@ -1,9 +1,5 @@
// @index(['./*.ts','./**/index.ts'], f => `export * from '${f.path}'`)
export * from './BasicWorkbook.controller';
export * from './BasicWorksheet.controller';
export * from './CellEditorController';
export * from './CountBarController';
// export * from './EditTooltipsController';
export * from './SheetBarController';
export * from './SheetContainerController';
// @endindex
+19 -49
View File
@@ -1,10 +1,11 @@
import { Engine, IRenderingEngine, ISelectionTransformerShapeManager, SelectionTransformerShapeManager } from '@univerjs/base-render';
import { DEFAULT_SELECTION, ICurrentUniverService, LocaleService, Plugin, PLUGIN_NAMES, PluginType } from '@univerjs/core';
import { ISelectionTransformerShapeManager, SelectionTransformerShapeManager } from '@univerjs/base-render';
import { DEFAULT_SELECTION, ICommandService, ICurrentUniverService, LocaleService, Plugin, PLUGIN_NAMES, PluginType } from '@univerjs/core';
import { Dependency, Inject, Injector } from '@wendellhu/redi';
import { DEFAULT_SPREADSHEET_PLUGIN_DATA, install, ISheetPluginConfig } from './Basics';
import { SheetPluginObserve, uninstall } from './Basics/Observer';
import { BasicWorkbookController, CellEditorController, CountBarController, SheetBarController, SheetContainerController } from './Controller';
import { SetSelectionsOperation } from './Commands/Operations/selection.operation';
import { BasicWorkbookController, CountBarController, SheetBarController, SheetContainerController } from './Controller';
import { BasicWorksheetController } from './Controller/BasicWorksheet.controller';
import { FormulaBarController } from './Controller/FormulaBarController';
import { en, zh } from './Locale';
@@ -20,25 +21,18 @@ export class SheetPlugin extends Plugin<SheetPluginObserve> {
private _config: ISheetPluginConfig;
private _canvasEngine: Engine;
// private _editTooltipsController: EditTooltipsController;
private _formulaBarController: FormulaBarController;
private _sheetBarController: SheetBarController;
// private _cellEditorController: CellEditorController;
private _countBarController: CountBarController;
// private _hideColumnController: HideColumnController;
private _sheetContainerController: SheetContainerController;
constructor(
config: Partial<ISheetPluginConfig>,
@ICurrentUniverService private readonly _currentUniverService: ICurrentUniverService,
@ICommandService private readonly _commandService: ICommandService,
@Inject(LocaleService) private readonly _localeService: LocaleService,
@Inject(Injector) override readonly _injector: Injector
) {
@@ -47,6 +41,7 @@ export class SheetPlugin extends Plugin<SheetPluginObserve> {
this._config = Object.assign(DEFAULT_SPREADSHEET_PLUGIN_DATA, config);
this._initializeDependencies(_injector);
this._initializeCommands();
}
initialize(): void {
@@ -59,7 +54,6 @@ export class SheetPlugin extends Plugin<SheetPluginObserve> {
this.initConfig();
this.initController();
this.initCanvasView();
this.listenEventManager();
}
@@ -81,23 +75,16 @@ export class SheetPlugin extends Plugin<SheetPluginObserve> {
}
}
// TODO@huwenzhao: We don't need to init controllers manually
initController() {
this._sheetContainerController = this._injector.get(SheetContainerController);
this._formulaBarController = this._injector.get(FormulaBarController);
// this._editTooltipsController = this._injector.get(EditTooltipsController);
this._sheetBarController = this._injector.get(SheetBarController);
this._countBarController = this._injector.get(CountBarController);
// this._hideColumnController = this._injector.get(HideColumnController);
this._injector.get(BasicWorksheetController);
this._injector.get(BasicWorkbookController);
}
initCanvasView() {
this._canvasEngine = this._injector.get(IRenderingEngine);
}
override onMounted(): void {
this.initialize();
}
@@ -114,52 +101,35 @@ export class SheetPlugin extends Plugin<SheetPluginObserve> {
this._sheetBarController.listenEventManager();
}
/** @deprecated move to DI system */
// getSelectionManager() {
// return this._injector.get(ISelectionManager);
// }
private _initializeDependencies(sheetInjector: Injector) {
const dependencies: Dependency[] = [
[CanvasView],
[CellEditorController],
[SheetContainerController],
[FormulaBarController],
// [EditTooltipsController],
[SheetBarController],
[CountBarController],
// TODO@huwenzhao: this is a temporary solution
// [
// ISelectionManager,
// {
// useFactory: (injector: Injector, canvasView: CanvasView) => injector.createInstance(SelectionManager, canvasView.getSheetView(), this._config),
// deps: [Injector, CanvasView],
// },
// ],
// [RowTitleController],
// [ColumnTitleController],
// [DragLineController],
// [HideColumnController],
[BasicWorksheetController],
[BasicWorkbookController],
// services
[BorderStyleManagerService],
[SelectionManagerService],
[
ISelectionTransformerShapeManager,
{
useClass: SelectionTransformerShapeManager,
},
],
// controllers
[SheetContainerController],
[FormulaBarController],
[SheetBarController],
[CountBarController],
[BasicWorksheetController],
[BasicWorkbookController],
];
dependencies.forEach((d) => {
sheetInjector.add(d);
});
}
private _initializeCommands(): void {
this._commandService.registerCommand(SetSelectionsOperation);
}
}
@@ -36,6 +36,7 @@ export enum CANVAS_VIEW_KEY {
VIEW_LEFT_TOP = 'viewLeftTop',
SHEET_VIEW = 'sheetView',
DRAG_LINE_VIEW = 'dragLineView',
CELL_EDITOR = 'cellEditor',
SELECTION_VIEW = 'selectionView',
}
+5 -5
View File
@@ -53,11 +53,14 @@ export class CanvasView {
const rowTitle = config.rowTitle;
const columnTitle = config.columnTitle;
// How do we know if a business should claim itself as main scene?
const scene = new Scene(CANVAS_VIEW_KEY.MAIN_SCENE, engine, {
width: 1500,
height: 1000,
});
scene.openTransformer();
this._scene = scene;
const viewMain = new Viewport(CANVAS_VIEW_KEY.VIEW_MAIN, scene, {
left: rowTitle.width,
@@ -89,6 +92,7 @@ export class CanvasView {
});
// viewMain.linkToViewport(viewLeft, LINK_VIEW_PORT_TYPE.Y);
// viewMain.linkToViewport(viewTop, LINK_VIEW_PORT_TYPE.X);
// syncing scroll on the main area to headerbars
viewMain.onScrollAfterObserver.add((param: IScrollObserverParam) => {
const { scrollX, scrollY, actualScrollX, actualScrollY } = param;
@@ -129,6 +133,7 @@ export class CanvasView {
const currentRatio = sheet.getZoomRatio();
let nextRatio = +parseFloat(`${currentRatio + ratioDelta}`).toFixed(1);
nextRatio = nextRatio >= 4 ? 4 : nextRatio <= 0.1 ? 0.1 : nextRatio;
// sheet.setZoomRatio(nextRatio);
e.preventDefault();
@@ -145,11 +150,6 @@ export class CanvasView {
engine.runRenderLoop(() => {
scene.render();
const app = document.getElementById('app');
if (app) {
app.innerText = `fps:${Math.round(engine.getFps()).toString()}`;
}
});
}
@@ -9,9 +9,10 @@ import {
SpreadsheetColumnTitle,
SpreadsheetRowTitle,
} from '@univerjs/base-render';
import { ICurrentUniverService, ISelectionRange, LocaleService, ObserverManager, Worksheet } from '@univerjs/core';
import { ICommandService, ICurrentUniverService, ISelectionRange, LocaleService, ObserverManager, Worksheet } from '@univerjs/core';
import { Inject, Injector } from '@wendellhu/redi';
import { SetSelectionsOperation } from '../../Commands/Operations/selection.operation';
import { NORMAL_SELECTION_PLUGIN_NAME, SelectionManagerService } from '../../Services/selection-manager.service';
import { BaseView, CANVAS_VIEW_KEY, CanvasViewRegistry, SHEET_VIEW_KEY } from '../BaseView';
@@ -21,6 +22,7 @@ export class SelectionView extends BaseView {
constructor(
@ICurrentUniverService private readonly _currentUniverService: ICurrentUniverService,
@ISelectionTransformerShapeManager private readonly _selectionTransformerShapeManager: ISelectionTransformerShapeManager,
@ICommandService private readonly _commandService: ICommandService,
@Inject(SelectionManagerService) private readonly _selectionManagerService: SelectionManagerService,
@Inject(ObserverManager) private readonly _observerManager: ObserverManager,
@Inject(LocaleService) private readonly _localeService: LocaleService
@@ -69,7 +71,16 @@ export class SelectionView extends BaseView {
spreadsheetLeftTopPlaceholder?.onPointerDownObserver.add((evt: IPointerEvent | IMouseEvent) => {});
this._selectionTransformerShapeManager.selectionRangeWithStyle$.subscribe((selectionDataWithStyleList) => {
this._selectionManagerService.replaceWithNoRefresh(selectionDataWithStyleList.map((selectionDataWithStyle) => convertSelectionDataToRange(selectionDataWithStyle)));
const unitId = workbook.getUnitId();
const sheetId = worksheet.getSheetId();
this._commandService.executeCommand(SetSelectionsOperation.id, {
unitId,
sheetId,
pluginName: NORMAL_SELECTION_PLUGIN_NAME,
selections: selectionDataWithStyleList.map((selectionDataWithStyle) => convertSelectionDataToRange(selectionDataWithStyle)),
});
const current = selectionDataWithStyleList[selectionDataWithStyleList.length - 1];
if (current == null) {
return;
+1
View File
@@ -81,6 +81,7 @@ export {
export { CopySheetCommand } from './Commands/Commands/copy-worksheet.command';
export { RemoveSheetCommand } from './Commands/Commands/remove-sheet.command';
export { RemoveWorksheetMergeCommand } from './Commands/Commands/remove-worksheet-merge.command';
export { ISetRangeValuesCommandParams, SetRangeValuesCommand } from './Commands/Commands/set-range-values.command';
export { SetTabColorCommand } from './Commands/Commands/set-tab-color.command';
export { SetWorksheetHideCommand } from './Commands/Commands/set-worksheet-hide.command';
export { SetWorksheetNameCommand } from './Commands/Commands/set-worksheet-name.command';
@@ -15,7 +15,8 @@ import {
} from '@univerjs/base-render';
import { DocumentModel, EventState, IPageElement, LocaleService, PageElementType } from '@univerjs/core';
import { Inject, Injector } from '@wendellhu/redi';
import { ObjectAdaptor, CanvasObjectProviderRegistry } from '../Adaptor';
import { CanvasObjectProviderRegistry, ObjectAdaptor } from '../Adaptor';
export enum DOCS_VIEW_KEY {
MAIN = '__DocsRender__',
@@ -42,6 +43,7 @@ export class DocsAdaptor extends ObjectAdaptor {
return this;
}
// eslint-disable-next-line max-lines-per-function
override convert(pageElement: IPageElement, mainScene: Scene) {
const { id, zIndex, left = 0, top = 0, width, height, angle, scaleX, scaleY, skewX, skewY, flipX, flipY, title, description, document: documentData } = pageElement;
if (documentData == null) {
@@ -1,8 +1,8 @@
import { IRichTextProps, RichText, Scene } from '@univerjs/base-render';
import { IPageElement, LocaleService, PageElementType } from '@univerjs/core';
import { Inject, Injector } from '@wendellhu/redi';
import { ObjectAdaptor, CanvasObjectProviderRegistry } from '../Adaptor';
import { CanvasObjectProviderRegistry, ObjectAdaptor } from '../Adaptor';
export class RichTextAdaptor extends ObjectAdaptor {
override zIndex = 2;
@@ -1,8 +1,8 @@
import { Engine, Rect, Scene, Slide, Viewport } from '@univerjs/base-render';
import { SlideModel, getColorStyle, IColorStyle, IPageElement, ISlidePage, PageElementType } from '@univerjs/core';
import { getColorStyle, IColorStyle, IPageElement, ISlidePage, PageElementType, SlideModel } from '@univerjs/core';
import { Inject, Injector } from '@wendellhu/redi';
import { ObjectAdaptor, CanvasObjectProviderRegistry } from '../Adaptor';
import { CanvasObjectProviderRegistry, ObjectAdaptor } from '../Adaptor';
import { ObjectProvider } from '../ObjectProvider';
// import { DocsAdaptor, ImageAdaptor, RichTextAdaptor, ShapeAdaptor } from './';
@@ -13,9 +13,10 @@ import {
SpreadsheetSkeleton,
Viewport,
} from '@univerjs/base-render';
import { EventState, ICellData, IPageElement, ObjectMatrix, PageElementType, Styles, LocaleService } from '@univerjs/core';
import { EventState, ICellData, IPageElement, LocaleService, ObjectMatrix, PageElementType, Styles } from '@univerjs/core';
import { Inject, Injector } from '@wendellhu/redi';
import { ObjectAdaptor, CanvasObjectProviderRegistry } from '../Adaptor';
import { CanvasObjectProviderRegistry, ObjectAdaptor } from '../Adaptor';
enum SHEET_VIEW_KEY {
MAIN = 'spreadInSlide',
@@ -289,12 +289,11 @@ export class CanvasView {
const { pageElements, pageBackgroundFill } = page;
// SceneViewers
const objects = this._ObjectProvider.convertToRenderObjects(pageElements, this._scene);
scene.openTransformer();
this._addBackgroundRect(scene, pageBackgroundFill);
// So finally SceneViewers are added to the scene as objects. How can we do optimizations on this?
scene.addObjects(objects);
const transformer = scene.getTransformer();
+7 -3
View File
@@ -45,17 +45,21 @@
"@storybook/addon-interactions": "^7.4.1",
"@storybook/addon-links": "^7.4.1",
"@storybook/blocks": "^7.4.1",
"@storybook/react": "^7.4.1",
"@storybook/react-vite": "^7.4.1",
"@storybook/react": "^7.4.1",
"@storybook/testing-library": "^0.2.0",
"@testing-library/react": "^14.0.0",
"@types/jest": "^29.5.4",
"@types/node": "^18.14.0",
"@types/react": "^18.2.21",
"@types/react-dom": "^18.2.7",
"jest": "29.6.4",
"@types/react": "^18.2.21",
"debug": "^4.3.3",
"enzyme": "^3.11.0",
"identity-obj-proxy": "^3.0.0",
"jest-css-modules-transform": "^4.4.2",
"jest-dev-server": "^6.1.1",
"jest-environment-jsdom": "^29.6.4",
"jest": "29.6.4",
"less": "^4.1.1",
"storybook": "^7.4.1",
"ts-jest": "29.1.1",
-7
View File
@@ -1,11 +1,4 @@
import { Context } from '@univerjs/core/src/Basics/Context';
// TODO Button const enum; BaseComponentSheet => BaseComponentXXX
export interface BaseComponentProps {
getComponent?: (ref: any) => void; //获取自身组件
id?: string; // 组件id
/** @deprecated */
context?: Context;
}
@@ -1,57 +1,58 @@
import { Nullable } from '@univerjs/core';
import { ICell } from '../../Interfaces/Cell';
import { BaseCellEditExtension, BaseCellEditExtensionFactory } from './CellEditExtensionFactory';
import { CellEditExtensionRegister } from './CellEditExtensionRegister';
export class CellEditExtensionManager {
static register: CellEditExtensionRegister;
private _cellEditExtensionFactoryList: BaseCellEditExtensionFactory[];
static create(): CellEditExtensionRegister {
if (!this.register) {
this.register = new CellEditExtensionRegister();
this.register.initialize();
}
return this.register;
}
/**
* inject cell position
* @param row
* @param column
* @param value
*/
handle(cell: ICell): Nullable<ICell> {
const cellEditExtensionFactoryList = CellEditExtensionManager?.register?.cellEditExtensionFactoryList;
if (!cellEditExtensionFactoryList) return;
// get the sorted list
// get the dynamically added list
this._cellEditExtensionFactoryList = cellEditExtensionFactoryList;
const extension = this._checkExtension(cell);
if (extension) {
extension.execute();
return extension.getCell();
}
}
/**
* Execute when the action is matched
* @param command
* @returns
*/
private _checkExtension(cell: ICell): BaseCellEditExtension | false {
let extension: BaseCellEditExtension | false = false;
if (!this._cellEditExtensionFactoryList) return false;
for (let index = 0; index < this._cellEditExtensionFactoryList.length; index++) {
const extensionFactory = this._cellEditExtensionFactoryList[index];
extension = extensionFactory.check(cell);
if (extension !== false) {
break;
}
}
return extension;
}
}
import { Nullable } from '@univerjs/core';
import { ICell } from '../../Interfaces/Cell';
import { BaseCellEditExtension, BaseCellEditExtensionFactory } from './CellEditExtensionFactory';
import { CellEditExtensionRegister } from './CellEditExtensionRegister';
export class CellEditExtensionManager {
static register: CellEditExtensionRegister;
private _cellEditExtensionFactoryList: BaseCellEditExtensionFactory[];
static create(): CellEditExtensionRegister {
if (!this.register) {
this.register = new CellEditExtensionRegister();
this.register.initialize();
}
return this.register;
}
/**
* inject cell position
* @param row
* @param column
* @param value
*/
handle(cell: ICell): Nullable<ICell> {
const cellEditExtensionFactoryList = CellEditExtensionManager?.register?.cellEditExtensionFactoryList;
if (!cellEditExtensionFactoryList) return;
// get the sorted list
// get the dynamically added list
this._cellEditExtensionFactoryList = cellEditExtensionFactoryList;
const extension = this._checkExtension(cell);
if (extension) {
extension.execute();
return extension.getCell();
}
}
/**
* Execute when the action is matched
* @param command
* @returns
*/
private _checkExtension(cell: ICell): BaseCellEditExtension | false {
let extension: BaseCellEditExtension | false = false;
if (!this._cellEditExtensionFactoryList) return false;
for (let index = 0; index < this._cellEditExtensionFactoryList.length; index++) {
const extensionFactory = this._cellEditExtensionFactoryList[index];
extension = extensionFactory.check(cell);
if (extension !== false) {
break;
}
}
return extension;
}
}
+2 -4
View File
@@ -2,8 +2,8 @@
* Use createContext to provide global language and skin settings
*/
import { LocaleService, ObserverManager } from '@univerjs/core';
import { createContext } from 'react';
import { Injector } from '@wendellhu/redi';
import { createContext } from 'react';
import { LocaleType } from '../Enum';
import { ComponentManager } from './ComponentManager';
@@ -18,6 +18,4 @@ export type AppContextValues = {
observerManager: ObserverManager;
};
const AppContext = createContext<Partial<AppContextValues>>({});
export { AppContext };
export default AppContext;
export const AppContext = createContext<Partial<AppContextValues>>({});
@@ -26,6 +26,7 @@ import {
FillColorIcon,
ForwardIcon,
FullBorderIcon,
FxIcon,
InnerBorderIcon,
ItalicIcon,
LeftAlignIcon,
@@ -48,7 +49,6 @@ import {
TopVerticalIcon,
UnderLineIcon,
VerticalBorderIcon,
FxIcon,
} from '../Components/Icon';
import { Item } from '../Components/Item/Item';

Some files were not shown because too many files have changed in this diff Show More