From 72e43d6ad7a2fa8cf78edcb9ae203ba5ecccaba7 Mon Sep 17 00:00:00 2001 From: Ran Luo Date: Sun, 29 Sep 2024 15:55:51 +0800 Subject: [PATCH] refactor(docs): refactor zen editor to use base-editor apis (#3551) Co-authored-by: zhangw --- packages/core/src/shared/tools.ts | 6 +- .../commands/replace-content.command.ts | 189 ++++++++++-- .../src/components/editor/TextEditor.tsx | 13 +- .../docs-ui/src/components/editor/utils.ts | 60 ++++ .../back-scroll.render-controller.ts | 4 +- .../doc-clipboard.controller.ts | 10 +- .../doc-editor-bridge.controller.ts | 9 +- .../doc-resize.render-controller.ts | 1 + .../doc.render-controller.ts | 9 +- packages/docs-ui/src/docs-ui-plugin.ts | 3 +- packages/docs-ui/src/index.ts | 2 +- .../services/clipboard/clipboard.service.ts | 10 +- .../services/editor/editor-manager.service.ts | 33 +- .../docs-ui/src/services/editor/editor.ts | 107 ++++--- packages/engine-render/src/thin-engine.ts | 2 +- .../src/controllers/prompt.controller.ts | 2 + .../src/views/CellLinkEdit/index.tsx | 15 +- .../src/views/CellLinkPopup/index.tsx | 30 +- .../__tests__/end-edit.controller.spec.ts | 2 +- .../editor/editing.render-controller.ts | 55 ++-- packages/sheets-ui/src/index.ts | 1 + .../commands/commands/zen-editor.command.ts | 122 +++++--- .../operations/zen-editor.operation.ts | 28 -- .../src/controllers/menu.schema.ts | 6 +- .../shortcuts/zen-editor.shortcut.ts | 2 +- .../controllers/zen-editor-ui.controller.ts | 7 +- .../src/controllers/zen-editor.controller.ts | 285 ++---------------- packages/sheets-zen-editor/src/index.ts | 5 +- packages/sheets-zen-editor/src/plugin.ts | 2 +- packages/sheets-zen-editor/src/views/menu.ts | 4 +- .../src/views/zen-editor/ZenEditor.tsx | 80 +++-- .../message/desktop-message.service.ts | 4 +- .../components/zen-zone/index.module.less | 6 +- 33 files changed, 621 insertions(+), 493 deletions(-) create mode 100644 packages/docs-ui/src/components/editor/utils.ts delete mode 100644 packages/sheets-zen-editor/src/commands/operations/zen-editor.operation.ts diff --git a/packages/core/src/shared/tools.ts b/packages/core/src/shared/tools.ts index 3e80072646e..dd605827748 100644 --- a/packages/core/src/shared/tools.ts +++ b/packages/core/src/shared/tools.ts @@ -14,11 +14,11 @@ * limitations under the License. */ -import type { IKeyValue } from './types'; - import { customAlphabet, nanoid } from 'nanoid'; import { isLegalUrl, normalizeUrl } from '../common/url'; +import type { IKeyValue } from './types'; + const rmsPrefix = /^-ms-/; const rDashAlpha = /-([a-z])/g; @@ -269,7 +269,7 @@ export class Tools { } function diffArrays(oneArray: any[], towArray: any[]) { - if (one.length !== tow.length) { + if (oneArray.length !== towArray.length) { return false; } for (let i = 0, len = oneArray.length; i < len; i++) { diff --git a/packages/docs-ui/src/commands/commands/replace-content.command.ts b/packages/docs-ui/src/commands/commands/replace-content.command.ts index 7228da1bb58..c14ee9e35f5 100644 --- a/packages/docs-ui/src/commands/commands/replace-content.command.ts +++ b/packages/docs-ui/src/commands/commands/replace-content.command.ts @@ -14,13 +14,143 @@ * limitations under the License. */ -import { BuildTextUtils, CommandType, ICommandService, IUndoRedoService, IUniverInstanceService, JSONX, TextX, TextXActionType } from '@univerjs/core'; +import { BuildTextUtils, CommandType, ICommandService, IUndoRedoService, IUniverInstanceService, JSONX, TextX, TextXActionType, Tools, UniverInstanceType } from '@univerjs/core'; import { DocSelectionManagerService, RichTextEditingMutation } from '@univerjs/docs'; -import type { DocumentDataModel, ICommand, IDocumentBody, IMutationInfo, ITextRange } from '@univerjs/core'; +import type { DocumentDataModel, ICommand, IDocumentBody, IDocumentData, IMutationInfo, ITextRange, JSONXActions } from '@univerjs/core'; import type { IRichTextEditingMutationParams } from '@univerjs/docs'; import type { ITextRangeWithStyle } from '@univerjs/engine-render'; import { getRichTextEditPath } from '../util'; +interface IReplaceSnapshotCommandParams { + unitId: string; + snapshot: IDocumentData; + textRanges: ITextRangeWithStyle[]; + segmentId?: string; + options: { [key: string]: boolean }; +} + +export const ReplaceSnapshotCommand: ICommand = { + id: 'doc.command-replace-snapshot', + type: CommandType.COMMAND, + // eslint-disable-next-line max-lines-per-function, complexity + handler: async (accessor, params: IReplaceSnapshotCommandParams) => { + const { unitId, snapshot, textRanges, segmentId = '', options } = params; + const univerInstanceService = accessor.get(IUniverInstanceService); + const commandService = accessor.get(ICommandService); + const docSelectionManagerService = accessor.get(DocSelectionManagerService); + + const docDataModel = univerInstanceService.getUnit(unitId, UniverInstanceType.UNIVER_DOC); + const prevSnapshot = docDataModel?.getSelfOrHeaderFooterModel(segmentId).getSnapshot(); + + if (docDataModel == null || prevSnapshot == null) { + return false; + } + + const { body, tableSource, footers, headers, lists, drawings, drawingsOrder } = snapshot; + const { + body: prevBody, + tableSource: prevTableSource, + footers: prevFooters, + headers: prevHeaders, + lists: prevLists, + drawings: prevDrawings, + drawingsOrder: prevDrawingsOrder, + } = prevSnapshot; + + if (body == null || prevBody == null) { + return false; + } + + // Handle body is equal to previous prevBody, only set the text ranges. + if (Tools.diffValue(body, prevBody) && textRanges) { + docSelectionManagerService.replaceDocRanges(textRanges, { + unitId, + subUnitId: unitId, + }, false); + + return true; + } + + const doMutation: IMutationInfo = { + id: RichTextEditingMutation.id, + params: { + unitId, + actions: [], + textRanges, + }, + }; + + if (options) { + doMutation.params.options = options; + } + + const rawActions: JSONXActions = []; + + const jsonX = JSONX.getInstance(); + + if (!Tools.diffValue(body, prevBody)) { + const actions = jsonX.replaceOp(['body'], prevBody, body); + if (actions != null) { + rawActions.push(actions); + } + } + + if (!Tools.diffValue(tableSource, prevTableSource)) { + const actions = jsonX.replaceOp(['tableSource'], prevTableSource, tableSource); + if (actions != null) { + rawActions.push(actions); + } + } + + if (!Tools.diffValue(footers, prevFooters)) { + const actions = jsonX.replaceOp(['footers'], prevFooters, footers); + if (actions != null) { + rawActions.push(actions); + } + } + + if (!Tools.diffValue(headers, prevHeaders)) { + const actions = jsonX.replaceOp(['headers'], prevHeaders, headers); + if (actions != null) { + rawActions.push(actions); + } + } + + if (!Tools.diffValue(lists, prevLists)) { + const actions = jsonX.replaceOp(['lists'], prevLists, lists); + if (actions != null) { + rawActions.push(actions); + } + } + + if (!Tools.diffValue(drawings, prevDrawings)) { + const actions = jsonX.replaceOp(['drawings'], prevDrawings, drawings); + if (actions != null) { + rawActions.push(actions); + } + } + + if (!Tools.diffValue(drawingsOrder, prevDrawingsOrder)) { + const actions = jsonX.replaceOp(['drawingsOrder'], prevDrawingsOrder, drawingsOrder); + if (actions != null) { + rawActions.push(actions); + } + } + + doMutation.params.actions = rawActions.reduce((acc, cur) => { + return JSONX.compose(acc, cur as JSONXActions); + }, null as JSONXActions); + + const result = commandService.syncExecuteCommand< + IRichTextEditingMutationParams, + IRichTextEditingMutationParams + >(doMutation.id, doMutation.params); + + return Boolean(result); + }, + +}; + interface IReplaceContentCommandParams { unitId: string; body: IDocumentBody; // Do not contain `\r\n` at the end. @@ -30,6 +160,9 @@ interface IReplaceContentCommandParams { } // Replace all content with new body, and reserve undo/redo stack. +/** + * @deprecated please use ReplaceSnapshotCommand instead. + */ export const ReplaceContentCommand: ICommand = { id: 'doc.command-replace-content', @@ -41,22 +174,29 @@ export const ReplaceContentCommand: ICommand = { const commandService = accessor.get(ICommandService); const docSelectionManagerService = accessor.get(DocSelectionManagerService); - const docDataModel = univerInstanceService.getUniverDocInstance(unitId); - const prevBody = docDataModel?.getSnapshot().body; - const selections = docSelectionManagerService.getTextRanges(); + const docDataModel = univerInstanceService.getUnit(unitId, UniverInstanceType.UNIVER_DOC); + const prevBody = docDataModel?.getSelfOrHeaderFooterModel(segmentId).getSnapshot().body; if (docDataModel == null || prevBody == null) { return false; } - if (!Array.isArray(selections) || selections.length === 0) { - return false; - } - const doMutation = getMutationParams(unitId, segmentId, docDataModel, prevBody, body); doMutation.params.textRanges = textRanges; - options && (doMutation.params.options = options); + if (options) { + doMutation.params.options = options; + } + + // Handle body is equal to prevBody. + if (doMutation.params.actions == null && textRanges) { + docSelectionManagerService.replaceDocRanges(textRanges, { + unitId, + subUnitId: unitId, + }, false); + + return true; + } const result = commandService.syncExecuteCommand< IRichTextEditingMutationParams, @@ -110,16 +250,7 @@ export const CoverContentCommand: ICommand = { }, }; -function getMutationParams(unitId: string, segmentId: string, docDatModel: DocumentDataModel, prevBody: IDocumentBody, body: IDocumentBody) { - const doMutation: IMutationInfo = { - id: RichTextEditingMutation.id, - params: { - unitId, - actions: [], - textRanges: [], - }, - }; - +function getMutationActions(segmentId: string, docDatModel: DocumentDataModel, prevBody: IDocumentBody, body: IDocumentBody) { const textX = new TextX(); const jsonX = JSONX.getInstance(); @@ -144,7 +275,23 @@ function getMutationParams(unitId: string, segmentId: string, docDatModel: Docum } const path = getRichTextEditPath(docDatModel, segmentId); - doMutation.params.actions = jsonX.editOp(textX.serialize(), path); + + return jsonX.editOp(textX.serialize(), path); +} + +function getMutationParams(unitId: string, segmentId: string, docDatModel: DocumentDataModel, prevBody: IDocumentBody, body: IDocumentBody) { + const doMutation: IMutationInfo = { + id: RichTextEditingMutation.id, + params: { + unitId, + actions: [], + textRanges: [], + }, + }; + + const actions = getMutationActions(segmentId, docDatModel, prevBody, body); + + doMutation.params.actions = actions; return doMutation; } diff --git a/packages/docs-ui/src/components/editor/TextEditor.tsx b/packages/docs-ui/src/components/editor/TextEditor.tsx index 9a527b73b69..8a171849bc2 100644 --- a/packages/docs-ui/src/components/editor/TextEditor.tsx +++ b/packages/docs-ui/src/components/editor/TextEditor.tsx @@ -20,6 +20,7 @@ import type { IDocumentData, Nullable } from '@univerjs/core'; import { isElementVisible } from '../../basics/editor'; import { IEditorService } from '../../services/editor/editor-manager.service'; import styles from './index.module.less'; +import { genSnapShotByValue } from './utils'; import type { Editor, IEditorCanvasStyle } from '../../services/editor/editor'; type MyComponentProps = React.DetailedHTMLProps, HTMLDivElement>; @@ -82,7 +83,7 @@ export interface ITextEditorProps { placeholder?: string; // Placeholder text. isValueValid?: boolean; // Whether the value is valid. - disbaled?: boolean; + disabled?: boolean; } /** @@ -151,9 +152,15 @@ export function TextEditor(props: ITextEditorProps & Omit) => { - const unitId = editor.editorUnitId; + const unitId = editor.getEditorId(); const isLegality = editorService.checkValueLegality(unitId); setTimeout(() => { diff --git a/packages/docs-ui/src/components/editor/utils.ts b/packages/docs-ui/src/components/editor/utils.ts new file mode 100644 index 00000000000..40f4c4aa5e7 --- /dev/null +++ b/packages/docs-ui/src/components/editor/utils.ts @@ -0,0 +1,60 @@ +/** + * Copyright 2023-present DreamNum Inc. + * + * 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 { DEFAULT_EMPTY_DOCUMENT_VALUE, DocumentFlavor } from '@univerjs/core'; +import type { IDocumentData } from '@univerjs/core'; + +/** + * + * @param value The initial value of the editor. + * @returns snapshot generated by value. + */ +export function genSnapShotByValue(id = '', value = '') { + const dataStream = `${value}${DEFAULT_EMPTY_DOCUMENT_VALUE}`; + const paragraphs = []; + const sectionBreaks = []; + + for (let i = 0; i < dataStream.length; i++) { + if (dataStream[i] === '\r') { + paragraphs.push({ + startIndex: i, + }); + } + + if (dataStream[i] === '\n') { + sectionBreaks.push({ + startIndex: i, + }); + } + } + + const snapshot: IDocumentData = { + id, + body: { + dataStream, + tables: [], + textRuns: [], + paragraphs, + sectionBreaks, + }, + tableSource: {}, + documentStyle: { + documentFlavor: DocumentFlavor.MODERN, + }, + }; + + return snapshot; +} diff --git a/packages/docs-ui/src/controllers/render-controllers/back-scroll.render-controller.ts b/packages/docs-ui/src/controllers/render-controllers/back-scroll.render-controller.ts index 4e5115dced3..7420461a1fa 100644 --- a/packages/docs-ui/src/controllers/render-controllers/back-scroll.render-controller.ts +++ b/packages/docs-ui/src/controllers/render-controllers/back-scroll.render-controller.ts @@ -88,7 +88,7 @@ export class DocBackScrollRenderController extends RxDisposable implements IRend const viewportMain = scene.getViewport(VIEWPORT_KEY.VIEW_MAIN); - const isEditor = !!this._editorService.getEditor(unitId); + const editor = this._editorService.getEditor(unitId); if (viewportMain == null) { return; @@ -104,7 +104,7 @@ export class DocBackScrollRenderController extends RxDisposable implements IRend let offsetY = 0; let offsetX = 0; - const delta = isEditor ? 0 : 100; + const delta = editor ? editor.params.backScrollOffset ?? 0 : 100; if (top < boundTop) { offsetY = top - boundTop - delta; diff --git a/packages/docs-ui/src/controllers/render-controllers/doc-clipboard.controller.ts b/packages/docs-ui/src/controllers/render-controllers/doc-clipboard.controller.ts index d44b93d93a8..4e0999a15dd 100644 --- a/packages/docs-ui/src/controllers/render-controllers/doc-clipboard.controller.ts +++ b/packages/docs-ui/src/controllers/render-controllers/doc-clipboard.controller.ts @@ -24,8 +24,9 @@ import { takeUntil } from 'rxjs'; import type { DocumentDataModel } from '@univerjs/core'; import type { IRenderContext, IRenderModule } from '@univerjs/engine-render'; -import { whenDocOrEditor, whenFocusEditor } from '../../commands/commands/clipboard.command'; +import { whenDocOrEditor } from '../../commands/commands/clipboard.command'; import { IDocClipboardService } from '../../services/clipboard/clipboard.service'; +import { IEditorService } from '../../services/editor/editor-manager.service'; import { DocSelectionRenderService } from '../../services/selection/doc-selection-render.service'; export class DocClipboardController extends RxDisposable implements IRenderModule { @@ -34,7 +35,8 @@ export class DocClipboardController extends RxDisposable implements IRenderModul @ICommandService private readonly _commandService: ICommandService, @IDocClipboardService private readonly _docClipboardService: IDocClipboardService, @Inject(DocSelectionRenderService) private readonly _docSelectionRenderService: DocSelectionRenderService, - @IContextService private readonly _contextService: IContextService + @IContextService private readonly _contextService: IContextService, + @IEditorService private readonly _editorService: IEditorService ) { super(); @@ -59,7 +61,9 @@ export class DocClipboardController extends RxDisposable implements IRenderModul // TODO: @JOCS, work around to fix https://github.com/dream-num/univer-pro/issues/2006. and then when you paste it, // you need to distinguish between different editors, // because different editors have different pasting effects. For example, when editing a state, you can't paste a table - if (whenFocusEditor(this._contextService) && (htmlContent ?? '').indexOf('') > -1) { + const editor = this._editorService.getEditor(this._context.unitId); + + if (!!editor && (htmlContent ?? '').indexOf('') > -1) { htmlContent = ''; } diff --git a/packages/docs-ui/src/controllers/render-controllers/doc-editor-bridge.controller.ts b/packages/docs-ui/src/controllers/render-controllers/doc-editor-bridge.controller.ts index 86fe176baa1..696685a032e 100644 --- a/packages/docs-ui/src/controllers/render-controllers/doc-editor-bridge.controller.ts +++ b/packages/docs-ui/src/controllers/render-controllers/doc-editor-bridge.controller.ts @@ -296,9 +296,14 @@ export class DocEditorBridgeController extends Disposable implements IRenderModu return; } - this._resize(unitId); + const editor = this._editorService.getEditor(unitId); - this._valueChange(); + // Only for Text editor? + if (editor && !editor.params.scrollBar) { + this._resize(unitId); + + this._valueChange(); + } } }) ); diff --git a/packages/docs-ui/src/controllers/render-controllers/doc-resize.render-controller.ts b/packages/docs-ui/src/controllers/render-controllers/doc-resize.render-controller.ts index 9a9ce1063ac..80cab95d3c4 100644 --- a/packages/docs-ui/src/controllers/render-controllers/doc-resize.render-controller.ts +++ b/packages/docs-ui/src/controllers/render-controllers/doc-resize.render-controller.ts @@ -21,6 +21,7 @@ import { animationFrameScheduler, filter, throttleTime } from 'rxjs'; import type { IRenderContext, IRenderModule } from '@univerjs/engine-render'; import { DocPageLayoutService } from '../../services/doc-page-layout.service'; +// REFACTOR: @JOCS, move to new-docs package. export class DocResizeRenderController extends Disposable implements IRenderModule { constructor( private _context: IRenderContext, diff --git a/packages/docs-ui/src/controllers/render-controllers/doc.render-controller.ts b/packages/docs-ui/src/controllers/render-controllers/doc.render-controller.ts index d099bee2598..1ea369f3991 100644 --- a/packages/docs-ui/src/controllers/render-controllers/doc.render-controller.ts +++ b/packages/docs-ui/src/controllers/render-controllers/doc.render-controller.ts @@ -58,7 +58,9 @@ export class DocRenderController extends RxDisposable implements IRenderModule { skeleton.calculate(); - if (this._editorService.isEditor(unitId)) { + // REFACTOR: @Jocs, should not use scroll bar to indicate a Zen Editor. refactor after support modern doc. + const editor = this._editorService.getEditor(unitId); + if (this._editorService.isEditor(unitId) && !editor?.params.scrollBar) { this._context.mainComponent?.makeDirty(); return; @@ -236,7 +238,10 @@ export class DocRenderController extends RxDisposable implements IRenderModule { docsComponent.resize(width, height); docBackground.resize(width, height); - if (!this._editorService.isEditor(unitId)) { + const editor = this._editorService.getEditor(unitId); + + // REFACTOR: @JOCS show not use scrollBar to indicate it's a Zen Editor. + if (!this._editorService.isEditor(unitId) || editor?.params.scrollBar) { scene.resize(width, height); } } diff --git a/packages/docs-ui/src/docs-ui-plugin.ts b/packages/docs-ui/src/docs-ui-plugin.ts index 54353189daa..4c645fb61e3 100644 --- a/packages/docs-ui/src/docs-ui-plugin.ts +++ b/packages/docs-ui/src/docs-ui-plugin.ts @@ -39,7 +39,7 @@ import { IMEInputCommand } from './commands/commands/ime-input.command'; import { ResetInlineFormatTextBackgroundColorCommand, SetInlineFormatBoldCommand, SetInlineFormatCommand, SetInlineFormatFontFamilyCommand, SetInlineFormatFontSizeCommand, SetInlineFormatItalicCommand, SetInlineFormatStrikethroughCommand, SetInlineFormatSubscriptCommand, SetInlineFormatSuperscriptCommand, SetInlineFormatTextBackgroundColorCommand, SetInlineFormatTextColorCommand, SetInlineFormatUnderlineCommand } from './commands/commands/inline-format.command'; import { BulletListCommand, ChangeListNestingLevelCommand, ChangeListTypeCommand, CheckListCommand, ListOperationCommand, OrderListCommand, QuickListCommand, ToggleCheckListCommand } from './commands/commands/list.command'; import { AlignCenterCommand, AlignJustifyCommand, AlignLeftCommand, AlignOperationCommand, AlignRightCommand } from './commands/commands/paragraph-align.command'; -import { CoverContentCommand, ReplaceContentCommand } from './commands/commands/replace-content.command'; +import { CoverContentCommand, ReplaceContentCommand, ReplaceSnapshotCommand } from './commands/commands/replace-content.command'; import { SetDocZoomRatioCommand } from './commands/commands/set-doc-zoom-ratio.command'; import { CreateDocTableCommand } from './commands/commands/table/doc-table-create.command'; import { DocTableDeleteColumnsCommand, DocTableDeleteRowsCommand, DocTableDeleteTableCommand } from './commands/commands/table/doc-table-delete.command'; @@ -185,6 +185,7 @@ export class UniverDocsUIPlugin extends Plugin { InnerPasteCommand, CutContentCommand, ReplaceContentCommand, + ReplaceSnapshotCommand, CoverContentCommand, SetDocZoomRatioCommand, SelectAllOperation, diff --git a/packages/docs-ui/src/index.ts b/packages/docs-ui/src/index.ts index b1e54b71f54..0568e12cd72 100644 --- a/packages/docs-ui/src/index.ts +++ b/packages/docs-ui/src/index.ts @@ -114,7 +114,7 @@ export { AlignOperationCommand, AlignRightCommand, } from './commands/commands/paragraph-align.command'; -export { CoverContentCommand, ReplaceContentCommand } from './commands/commands/replace-content.command'; +export { CoverContentCommand, ReplaceContentCommand, ReplaceSnapshotCommand } from './commands/commands/replace-content.command'; export { SetDocZoomRatioCommand } from './commands/commands/set-doc-zoom-ratio.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'; diff --git a/packages/docs-ui/src/services/clipboard/clipboard.service.ts b/packages/docs-ui/src/services/clipboard/clipboard.service.ts index 10c2880a7b1..38fade307c6 100644 --- a/packages/docs-ui/src/services/clipboard/clipboard.service.ts +++ b/packages/docs-ui/src/services/clipboard/clipboard.service.ts @@ -280,7 +280,15 @@ export class DocClipboardService extends Disposable implements IDocClipboardServ ? documentBodyList.map((body) => body.dataStream).join('\n') : documentBodyList[0].dataStream) .replaceAll(DataStreamTreeTokenType.CUSTOM_RANGE_START, '') - .replaceAll(DataStreamTreeTokenType.CUSTOM_RANGE_END, ''); + .replaceAll(DataStreamTreeTokenType.CUSTOM_RANGE_END, '') + .replaceAll(DataStreamTreeTokenType.TABLE_START, '') + .replaceAll(DataStreamTreeTokenType.TABLE_END, '') + .replaceAll(DataStreamTreeTokenType.TABLE_ROW_START, '') + .replaceAll(DataStreamTreeTokenType.TABLE_ROW_END, '') + .replaceAll(DataStreamTreeTokenType.TABLE_CELL_START, '') + .replaceAll(DataStreamTreeTokenType.TABLE_CELL_END, '') + // Replace `\r\n` in table cell to white space. + .replaceAll('\r\n', ' '); let html = this._umdToHtml.convert(documentBodyList); diff --git a/packages/docs-ui/src/services/editor/editor-manager.service.ts b/packages/docs-ui/src/services/editor/editor-manager.service.ts index 39193964441..7069a17efb5 100644 --- a/packages/docs-ui/src/services/editor/editor-manager.service.ts +++ b/packages/docs-ui/src/services/editor/editor-manager.service.ts @@ -192,6 +192,7 @@ export class EditorService extends Disposable implements IEditorService, IDispos ); } + /** @deprecated */ private _blurSheetEditor(target: HTMLElement) { if (editorFocusInElements.some((item) => target.classList.contains(item))) { return; @@ -225,11 +226,13 @@ export class EditorService extends Disposable implements IEditorService, IDispos return this._editors.has(editorUnitId); } + /** @deprecated */ isSheetEditor(editorUnitId: string) { const editor = this._editors.get(editorUnitId); return !!(editor && editor.isSheetEditor()); } + /** @deprecated */ closeRangePrompt() { const documentDataModel = this._univerInstanceService.getCurrentUniverDocInstance(); if (!documentDataModel) { @@ -260,6 +263,7 @@ export class EditorService extends Disposable implements IEditorService, IDispos return this._spreadsheetFocusState; } + /** @deprecated */ focusStyle(editorUnitId: string) { const editor = this.getEditor(editorUnitId); if (!editor) { @@ -281,10 +285,12 @@ export class EditorService extends Disposable implements IEditorService, IDispos this.setFocusId(editorUnitId); } + /** @deprecated */ singleSelection(state: boolean) { this._singleSelection$.next(state); } + /** @deprecated */ selectionChangingState() { // const documentDataModel = this._univerInstanceService.getCurrentUniverDocInstance(); const editorUnitId = this.getFocusId(); @@ -419,6 +425,7 @@ export class EditorService extends Disposable implements IEditorService, IDispos return [...this.getAllEditor().values()][0]; } + /** @deprecated */ resize(unitId: string) { const editor = this.getEditor(unitId); if (editor == null) { @@ -430,6 +437,7 @@ export class EditorService extends Disposable implements IEditorService, IDispos this._resize$.next(unitId); } + /** @deprecated */ isVisible(id: string) { return this.getEditor(id)?.isVisible(); } @@ -455,14 +463,18 @@ export class EditorService extends Disposable implements IEditorService, IDispos } register(config: IEditorConfigParams, container: HTMLDivElement): IDisposable { - const { initialSnapshot, editorUnitId, canvasStyle = {} } = config; + const { initialSnapshot, canvasStyle = {} } = config; + const editorUnitId = initialSnapshot.id; + + const documentDataModel = this._univerInstanceService.getUnit(editorUnitId, UniverInstanceType.UNIVER_DOC); - const documentDataModel = this._univerInstanceService.getUnit(editorUnitId, UniverInstanceType.UNIVER_DOC) - ?? this._univerInstanceService.createUnit( + if (documentDataModel == null) { + this._univerInstanceService.createUnit( UniverInstanceType.UNIVER_DOC, initialSnapshot || this._getBlank(editorUnitId), { makeCurrent: false } ); + } let render = this._renderManagerService.getRenderById(editorUnitId); if (render == null) { @@ -474,7 +486,7 @@ export class EditorService extends Disposable implements IEditorService, IDispos render.engine.setContainer(container); const editor = new Editor( - { ...config, render, documentDataModel, editorDom: container, canvasStyle }, + { ...config, render, editorDom: container, canvasStyle }, this._univerInstanceService, this._docSelectionManagerService, this._commandService, @@ -484,10 +496,12 @@ export class EditorService extends Disposable implements IEditorService, IDispos this._editors.set(editorUnitId, editor); // Delete scroll bar - // FIXME@Jocs: should add a configuration when creating a renderer, not delete it. - (render.mainComponent?.getScene() as Scene)?.getViewports()?.[0].getScrollBar()?.dispose(); + if (!config.scrollBar) { + (render.mainComponent?.getScene() as Scene)?.getViewports()?.[0].getScrollBar()?.dispose(); + } - if (!editor.isSheetEditor()) { + // @ggg, Move this to Text Editor? + if (!editor.isSheetEditor() && !config.noNeedVerticalAlign) { editor.verticalAlign(); editor.updateCanvasStyle(); } @@ -504,7 +518,7 @@ export class EditorService extends Disposable implements IEditorService, IDispos } this._renderManagerService.removeRender(editorUnitId); - editor.documentDataModel.dispose(); + editor.dispose(); this._editors.delete(editorUnitId); this._univerInstanceService.disposeUnit(editorUnitId); this._contextService.setContextValue(FOCUSING_UNIVER_EDITOR_STANDALONE_SINGLE_MODE, false); @@ -516,6 +530,7 @@ export class EditorService extends Disposable implements IEditorService, IDispos * Compatible with the editor in the sheet scenario, * it is necessary to refocus back to the current sheet when unloading. */ + // REFACTOR: @zw, move to sheet cell editor. const sheets = this._univerInstanceService.getAllUnitsForType(UniverInstanceType.UNIVER_SHEET); if (sheets.length > 0) { const current = this._univerInstanceService.getCurrentUnitForType(UniverInstanceType.UNIVER_SHEET); @@ -526,10 +541,12 @@ export class EditorService extends Disposable implements IEditorService, IDispos } } + /** @deprecated */ refreshValueChange(editorUnitId: string) { this._refreshValueChange(editorUnitId); } + /** @deprecated */ checkValueLegality(editorUnitId: string) { const editor = this._editors.get(editorUnitId); diff --git a/packages/docs-ui/src/services/editor/editor.ts b/packages/docs-ui/src/services/editor/editor.ts index 98f3f412e96..1056dd56fe0 100644 --- a/packages/docs-ui/src/services/editor/editor.ts +++ b/packages/docs-ui/src/services/editor/editor.ts @@ -19,7 +19,7 @@ import { merge, type Observable, Subject } from 'rxjs'; import type { DocumentDataModel, ICommandService, IDocumentData, IDocumentStyle, IPosition, IUndoRedoService, IUniverInstanceService, Nullable } from '@univerjs/core'; import type { DocSelectionManagerService } from '@univerjs/docs'; import type { IDocSelectionInnerParam, IRender, ISuccinctDocRangeParam, ITextRangeWithStyle } from '@univerjs/engine-render'; -import { ReplaceContentCommand } from '../../commands/commands/replace-content.command'; +import { ReplaceSnapshotCommand } from '../../commands/commands/replace-content.command'; import { DocSelectionRenderService, type IEditorInputConfig } from '../selection/doc-selection-render.service'; interface IEditorEvent { @@ -81,7 +81,7 @@ export interface IEditorCanvasStyle { } export interface IEditorConfigParams { - initialSnapshot?: IDocumentData; + initialSnapshot: IDocumentData; cancelDefaultResizeListener?: boolean; canvasStyle?: IEditorCanvasStyle; // A Boolean attribute which, if present, indicates that the editor should automatically have focus. @@ -91,50 +91,57 @@ export interface IEditorConfigParams { // Boolean. The value is not editable readonly?: boolean; + backScrollOffset?: number; // The unique id of editor. - editorUnitId: string; + editorUnitId?: string; + + // show scrollBar + scrollBar?: boolean; + + // need vertical align and update canvas style. TODO: remove this latter. + /** @deprecated */ + noNeedVerticalAlign?: boolean; /** * @deprecated The implementer makes its own judgment. */ - isSheetEditor: boolean; + isSheetEditor?: boolean; /** * If the editor is for formula editing. * @deprecated this is a temp fix before refactoring editor. */ - isFormulaEditor: boolean; + isFormulaEditor?: boolean; /** * @deprecated The implementer makes its own judgment. */ - isSingle: boolean; + isSingle?: boolean; /** * @deprecated The implementer makes its own judgment. */ - onlyInputFormula: boolean; + onlyInputFormula?: boolean; /** * @deprecated The implementer makes its own judgment. */ - onlyInputRange: boolean; + onlyInputRange?: boolean; /** * @deprecated The implementer makes its own judgment. */ - onlyInputContent: boolean; + onlyInputContent?: boolean; /** * @deprecated The implementer makes its own judgment. */ - isSingleChoice: boolean; + isSingleChoice?: boolean; /** * @deprecated The implementer makes its own judgment. */ - openForSheetUnitId: Nullable; + openForSheetUnitId?: Nullable; /** * @deprecated The implementer makes its own judgment. */ - openForSheetSubUnitId: Nullable; + openForSheetSubUnitId?: Nullable; } export interface IEditorOptions extends IEditorConfigParams, IEditorStateParams { render: IRender; - documentDataModel: DocumentDataModel; editorDom: HTMLDivElement; } @@ -285,7 +292,6 @@ export class Editor extends Disposable implements IEditor { const docSelectionRenderService = this._param.render.with(DocSelectionRenderService); docSelectionRenderService.blur(); - this._focus = false; } @@ -324,29 +330,23 @@ export class Editor extends Disposable implements IEditor { // get editor id. getEditorId(): string { - return this._param.editorUnitId; + return this._getEditorId(); } // get document data. getDocumentData(): IDocumentData { - const editorUnitId = this.getEditorId(); - const docDataModel = this._univerInstanceService.getUnit(editorUnitId, UniverInstanceType.UNIVER_DOC)!; + const docDataModel = this._getDocDataModel(); return docDataModel.getSnapshot(); } // Set the new document data. setDocumentData(data: IDocumentData, textRanges: Nullable) { - const { id, body } = data; + const { id } = data; - this._commandService.executeCommand(ReplaceContentCommand.id, { + this._commandService.executeCommand(ReplaceSnapshotCommand.id, { unitId: id, - body: { - ...body, - dataStream: body?.dataStream.endsWith('\r\n') - ? body.dataStream.substring(0, body.dataStream.length - 2) - : body!.dataStream, - }, + snapshot: data, textRanges, }); } @@ -358,11 +358,10 @@ export class Editor extends Disposable implements IEditor { return this._undoRedoService.clearUndoRedo(editorUnitId); } - /** - * @deprecated use getDocumentData. - */ - get documentDataModel() { - return this._param.documentDataModel; + override dispose(): void { + const docDataModel = this._getDocDataModel(); + + docDataModel.dispose(); } /** @@ -372,6 +371,13 @@ export class Editor extends Disposable implements IEditor { return this._param.editorUnitId; } + /** + * @deprecated @TODO: @JOCS remove this in the future. + */ + get params() { + return this._param; + } + get cancelDefaultResizeListener() { return this._param.cancelDefaultResizeListener; } @@ -381,7 +387,7 @@ export class Editor extends Disposable implements IEditor { } isSingleChoice() { - return this._param.isSingleChoice; + return this._param.isSingleChoice ?? false; } /** @deprecated */ @@ -469,7 +475,8 @@ export class Editor extends Disposable implements IEditor { * @deprecated use getDocumentData. */ getValue() { - const value = this._param.documentDataModel.getBody()?.dataStream || ''; + const docDataModel = this._getDocDataModel(); + const value = docDataModel.getBody()?.dataStream || ''; return value.replace(/\r\n/g, '').replace(/\n/g, '').replace(/\n/g, ''); } @@ -477,7 +484,8 @@ export class Editor extends Disposable implements IEditor { * @deprecated use getDocumentData. */ getBody() { - return this._param.documentDataModel.getBody(); + const docDataModel = this._getDocDataModel(); + return docDataModel.getBody(); } /** @@ -490,10 +498,13 @@ export class Editor extends Disposable implements IEditor { }; } + /** + * @deprecated. + */ verticalAlign() { - const documentDataModel = this._param?.documentDataModel; + const docDataModel = this._getDocDataModel(); - if (documentDataModel == null) { + if (docDataModel == null) { return; } @@ -504,7 +515,7 @@ export class Editor extends Disposable implements IEditor { } if (!this.isSingle()) { - documentDataModel.updateDocumentDataPageSize(width, undefined); + docDataModel.updateDocumentDataPageSize(width, undefined); return; } @@ -516,16 +527,19 @@ export class Editor extends Disposable implements IEditor { const top = (height - (fontSize * 4 / 3)) / 2 - 2; - documentDataModel.updateDocumentDataMargin({ + docDataModel.updateDocumentDataMargin({ t: top < 0 ? 0 : top, }); - documentDataModel.updateDocumentDataPageSize(undefined, undefined); + docDataModel.updateDocumentDataPageSize(undefined, undefined); } + /** + * @deprecated. + */ updateCanvasStyle() { - const documentDataModel = this._param.documentDataModel; - if (documentDataModel == null) { + const docDataModel = this._getDocDataModel(); + if (docDataModel == null) { return; } @@ -539,6 +553,17 @@ export class Editor extends Disposable implements IEditor { documentStyle.textStyle.fs = this._param.canvasStyle.fontSize; } - documentDataModel.updateDocumentStyle(documentStyle); + docDataModel.updateDocumentStyle(documentStyle); + } + + private _getDocDataModel() { + const editorUnitId = this._getEditorId(); + const docDataModel = this._univerInstanceService.getUnit(editorUnitId, UniverInstanceType.UNIVER_DOC)!; + + return docDataModel; + } + + private _getEditorId() { + return this._param.initialSnapshot?.id || this._param.editorUnitId || ''; } } diff --git a/packages/engine-render/src/thin-engine.ts b/packages/engine-render/src/thin-engine.ts index dae242b0837..7bf8c854766 100644 --- a/packages/engine-render/src/thin-engine.ts +++ b/packages/engine-render/src/thin-engine.ts @@ -17,8 +17,8 @@ import { Disposable, EventSubject } from '@univerjs/core'; import type { IDisposable } from '@univerjs/core'; -import type { CURSOR_TYPE } from './basics/const'; import { RENDER_CLASS_TYPE } from './basics/const'; +import type { CURSOR_TYPE } from './basics/const'; import type { IEvent } from './basics/i-events'; import type { ITransformChangeState } from './basics/interfaces'; import type { Canvas } from './canvas'; diff --git a/packages/sheets-formula/src/controllers/prompt.controller.ts b/packages/sheets-formula/src/controllers/prompt.controller.ts index cf360e8b694..9354987981f 100644 --- a/packages/sheets-formula/src/controllers/prompt.controller.ts +++ b/packages/sheets-formula/src/controllers/prompt.controller.ts @@ -263,6 +263,8 @@ export class PromptController extends Disposable { if (!editor || editor.onlyInputContent() || (editor.isSheetEditor() && !this._isFormulaEditorActivated()) + // Remove this latter. + || editor.params.scrollBar ) { return; } diff --git a/packages/sheets-hyper-link-ui/src/views/CellLinkEdit/index.tsx b/packages/sheets-hyper-link-ui/src/views/CellLinkEdit/index.tsx index 79aa3a8ac77..0d60773e13e 100644 --- a/packages/sheets-hyper-link-ui/src/views/CellLinkEdit/index.tsx +++ b/packages/sheets-hyper-link-ui/src/views/CellLinkEdit/index.tsx @@ -19,7 +19,7 @@ import type { ISetSelectionsOperationParams } from '@univerjs/sheets'; import { BuildTextUtils, createInternalEditorID, CustomRangeType, DisposableCollection, DOCS_ZEN_EDITOR_UNIT_ID_KEY, FOCUSING_SHEET, generateRandomId, getOriginCellValue, ICommandService, IContextService, isValidRange, IUniverInstanceService, LocaleService, Tools, UniverInstanceType, useDependency } from '@univerjs/core'; import { Button, FormLayout, Input, Select } from '@univerjs/design'; import { DocSelectionManagerService } from '@univerjs/docs'; -import { DocSelectionRenderService, RangeSelector } from '@univerjs/docs-ui'; +import { DocBackScrollRenderController, DocSelectionRenderService, RangeSelector } from '@univerjs/docs-ui'; import { deserializeRangeWithSheet, IDefinedNamesService, serializeRange, serializeRangeToRefString, serializeRangeWithSheet } from '@univerjs/engine-formula'; import { IRenderManagerService } from '@univerjs/engine-render'; import { SetSelectionsOperation, SetWorksheetActiveOperation } from '@univerjs/sheets'; @@ -59,6 +59,8 @@ export const CellLinkEdit = () => { const markSelectionService = useDependency(IMarkSelectionService); const textSelectionService = useDependency(DocSelectionManagerService); const contextService = useDependency(IContextService); + const docSelectionManagerService = useDependency(DocSelectionManagerService); + const customHyperLinkSidePanel = useMemo(() => { if (sidePanelService.isBuiltInLinkType(type)) { return; @@ -206,7 +208,6 @@ export const CellLinkEdit = () => { useEffect(() => { const render = renderManagerService.getRenderById(editorBridgeService.getCurrentEditorId()); const disposeCollection = new DisposableCollection(); - if (render) { const selectionRenderService = render.with(DocSelectionRenderService); selectionRenderService.setReserveRangesStatus(true); @@ -441,6 +442,16 @@ export const CellLinkEdit = () => { zenZoneService.show(); contextService.setContextValue(FOCUSING_SHEET, false); + const docBackScrollRenderController = renderManagerService.getRenderById(DOCS_ZEN_EDITOR_UNIT_ID_KEY)?.with(DocBackScrollRenderController); + const range = docSelectionManagerService.getTextRanges({ unitId: DOCS_ZEN_EDITOR_UNIT_ID_KEY, subUnitId: DOCS_ZEN_EDITOR_UNIT_ID_KEY })?.[0]; + + if (docBackScrollRenderController && range) { + // TODO: this was hacking + setTimeout(() => { + docBackScrollRenderController.scrollToRange(range); + docSelectionManagerService.refreshSelection({ unitId: DOCS_ZEN_EDITOR_UNIT_ID_KEY, subUnitId: DOCS_ZEN_EDITOR_UNIT_ID_KEY }); + }, 100); + } } editorBridgeService.disableForceKeepVisible(); setHide(false); diff --git a/packages/sheets-hyper-link-ui/src/views/CellLinkPopup/index.tsx b/packages/sheets-hyper-link-ui/src/views/CellLinkPopup/index.tsx index 2b17389f42d..f16ef6cd924 100644 --- a/packages/sheets-hyper-link-ui/src/views/CellLinkPopup/index.tsx +++ b/packages/sheets-hyper-link-ui/src/views/CellLinkPopup/index.tsx @@ -14,21 +14,21 @@ * limitations under the License. */ -import { DOCS_ZEN_EDITOR_UNIT_ID_KEY, ICommandService, LocaleService, useDependency } from '@univerjs/core'; -import React, { useEffect, useState } from 'react'; -import { AllBorderSingle, CopySingle, LinkSingle, UnlinkSingle, WriteSingle, Xlsx } from '@univerjs/icons'; -import cs from 'clsx'; +import { DOCS_ZEN_EDITOR_UNIT_ID_KEY, ICommandService, LocaleService, useDependency, useObservable } from '@univerjs/core'; import { MessageType, Tooltip } from '@univerjs/design'; -import { IMessageService } from '@univerjs/ui'; -import { IEditorBridgeService } from '@univerjs/sheets-ui'; +import { AllBorderSingle, CopySingle, LinkSingle, UnlinkSingle, WriteSingle, Xlsx } from '@univerjs/icons'; import { SheetHyperLinkType } from '@univerjs/sheets-hyper-link'; -import type { IHyperLinkPopup } from '../../services/popup.service'; +import { IEditorBridgeService } from '@univerjs/sheets-ui'; +import { IMessageService, IZenZoneService } from '@univerjs/ui'; +import cs from 'clsx'; +import React, { useEffect, useState } from 'react'; +import { CancelHyperLinkCommand, CancelRichHyperLinkCommand } from '../../commands/commands/remove-hyper-link.command'; +import { OpenHyperLinkEditPanelOperation } from '../../commands/operations/popup.operations'; import { SheetsHyperLinkPopupService } from '../../services/popup.service'; import { SheetsHyperLinkResolverService } from '../../services/resolver.service'; -import { OpenHyperLinkEditPanelOperation } from '../../commands/operations/popup.operations'; -import { CancelHyperLinkCommand, CancelRichHyperLinkCommand } from '../../commands/commands/remove-hyper-link.command'; import { HyperLinkEditSourceType } from '../../types/enums/edit-source'; import styles from './index.module.less'; +import type { IHyperLinkPopup } from '../../services/popup.service'; const iconsMap = { [SheetHyperLinkType.URL]: , @@ -46,6 +46,8 @@ export const CellLinkPopup = () => { const [currentPopup, setCurrentPopup] = useState(null); const resolverService = useDependency(SheetsHyperLinkResolverService); const editorBridgeService = useDependency(IEditorBridgeService); + const zenZoneService = useDependency(IZenZoneService); + const visible = useObservable(zenZoneService.visible$); useEffect(() => { setCurrentPopup(popupService.currentPopup); @@ -70,7 +72,15 @@ export const CellLinkPopup = () => { return (
popupService.hideCurrentPopup()}> -
+
{ + if (zenZoneService.visible) { + return; + } + linkObj.handler(); + }} + >
{iconsMap[linkObj.type]}
diff --git a/packages/sheets-ui/src/controllers/editor/__tests__/end-edit.controller.spec.ts b/packages/sheets-ui/src/controllers/editor/__tests__/end-edit.controller.spec.ts index 1401eaf14c5..2cc356c5001 100644 --- a/packages/sheets-ui/src/controllers/editor/__tests__/end-edit.controller.spec.ts +++ b/packages/sheets-ui/src/controllers/editor/__tests__/end-edit.controller.spec.ts @@ -125,7 +125,7 @@ describe('Test EndEditController', () => { throw new Error('documentLayoutObject is undefined'); } // @ts-ignore - return getCellDataByInput(cell, documentLayoutObject, lexerTreeBuilder, (model) => model.getSnapshot(), localeService, get(IMockFunctionService)); + return getCellDataByInput(cell, documentLayoutObject.documentModel, lexerTreeBuilder, (model) => model.getSnapshot(), localeService, get(IMockFunctionService)); }; normalizeStringByLexer = (str: string) => { diff --git a/packages/sheets-ui/src/controllers/editor/editing.render-controller.ts b/packages/sheets-ui/src/controllers/editor/editing.render-controller.ts index 3fbdd70e8db..2e5c618451b 100644 --- a/packages/sheets-ui/src/controllers/editor/editing.render-controller.ts +++ b/packages/sheets-ui/src/controllers/editor/editing.render-controller.ts @@ -843,7 +843,6 @@ export class EditingRenderController extends Disposable implements IRenderModule return; } - const { unitId, sheetId, row, column, documentLayoutObject } = editCellState; // If neither the formula bar editor nor the cell editor has been edited, // it is considered that the content has not changed and returns directly. const editorIsDirty = this._editorBridgeService.getEditorDirty(); @@ -852,16 +851,10 @@ export class EditingRenderController extends Disposable implements IRenderModule } const workbook = this._context.unit; - let worksheet = workbook.getActiveSheet(); + const worksheet = workbook.getActiveSheet(); const workbookId = this._context.unitId; const worksheetId = worksheet.getSheetId(); - // If the target cell does not exist, there is no need to execute setRangeValue - const setRangeValueTargetSheet = workbook.getSheetBySheetId(sheetId); - if (!setRangeValueTargetSheet) { - return; - } - // Reselect the current selections, when exist cell editor by press ESC.I if (keycode === KeyCode.ESC) { const selections = this._workbookSelections.getCurrentSelections(); @@ -876,6 +869,8 @@ export class EditingRenderController extends Disposable implements IRenderModule return; } + const { unitId, sheetId } = editCellState; + /** * When closing the editor, switch to the current tab of the editor. */ @@ -887,12 +882,43 @@ export class EditingRenderController extends Disposable implements IRenderModule }); } + const documentDataModel = editCellState.documentLayoutObject.documentModel; + + if (documentDataModel) { + await this._submitCellData(documentDataModel); + } + + // moveCursor need to put behind of SetRangeValuesCommand, fix https://github.com/dream-num/univer/issues/1155 + this._moveCursor(keycode); + } + + submitCellData(documentDataModel: DocumentDataModel) { + return this._submitCellData(documentDataModel); + } + + private async _submitCellData(documentDataModel: DocumentDataModel) { + const editCellState = this._editorBridgeService.getEditCellState(); + if (editCellState == null) { + return; + } + + const { unitId, sheetId, row, column } = editCellState; + + const workbook = this._context.unit; + let worksheet = workbook.getActiveSheet(); + + // If the target cell does not exist, there is no need to execute setRangeValue + const setRangeValueTargetSheet = workbook.getSheetBySheetId(sheetId); + if (!setRangeValueTargetSheet) { + return; + } + worksheet = workbook.getActiveSheet(); // If cross-sheet operation, switch current sheet first, then const cellData const cellData: Nullable = getCellDataByInput( worksheet.getCellRaw(row, column) || {}, - documentLayoutObject, + documentDataModel, this._lexerTreeBuilder, (model) => model.getSnapshot(), this._localService, @@ -900,7 +926,6 @@ export class EditingRenderController extends Disposable implements IRenderModule ); if (!cellData) { - this._moveCursor(keycode); return; } @@ -933,9 +958,6 @@ export class EditingRenderController extends Disposable implements IRenderModule }, value: finalCell, }); - - // moveCursor need to put behind of SetRangeValuesCommand, fix https://github.com/dream-num/univer/issues/1155 - this._moveCursor(keycode); } private _exitInput(param: IEditorBridgeServiceVisibleParam) { @@ -1042,7 +1064,7 @@ export class EditingRenderController extends Disposable implements IRenderModule export function getCellDataByInput( cellData: ICellData, - documentLayoutObject: IDocumentLayoutObject, + documentDataModel: Nullable, lexerTreeBuilder: LexerTreeBuilder, getSnapshot: (data: DocumentDataModel) => IDocumentData, localeService: LocaleService, @@ -1050,12 +1072,11 @@ export function getCellDataByInput( ) { cellData = Tools.deepClone(cellData); - const { documentModel } = documentLayoutObject; - if (documentModel == null) { + if (documentDataModel == null) { return null; } - const snapshot = getSnapshot(documentModel); + const snapshot = getSnapshot(documentDataModel); const { body } = snapshot; if (body == null) { diff --git a/packages/sheets-ui/src/index.ts b/packages/sheets-ui/src/index.ts index aa9b6d5826c..4ae2e1c39e9 100644 --- a/packages/sheets-ui/src/index.ts +++ b/packages/sheets-ui/src/index.ts @@ -50,6 +50,7 @@ export { export type { ICellDataWithSpanInfo, ICopyPastePayload, ISheetClipboardHook, ISheetDiscreteRangeLocation } from './services/clipboard/type'; export { COPY_TYPE } from './services/clipboard/type'; export { getRepeatRange } from './services/clipboard/utils'; +export { EditingRenderController } from './controllers/editor/editing.render-controller'; export { CellEditorManagerService, ICellEditorManagerService } from './services/editor/cell-editor-manager.service'; export { IFormulaEditorManagerService } from './services/editor/formula-editor-manager.service'; export { diff --git a/packages/sheets-zen-editor/src/commands/commands/zen-editor.command.ts b/packages/sheets-zen-editor/src/commands/commands/zen-editor.command.ts index df5919aa6cc..873c7be27ab 100644 --- a/packages/sheets-zen-editor/src/commands/commands/zen-editor.command.ts +++ b/packages/sheets-zen-editor/src/commands/commands/zen-editor.command.ts @@ -14,24 +14,71 @@ * limitations under the License. */ -import { CommandType, DOCS_ZEN_EDITOR_UNIT_ID_KEY, IUniverInstanceService, UniverInstanceType } from '@univerjs/core'; -import { DocBackScrollRenderController } from '@univerjs/docs-ui'; -import { DeviceInputEventType, IRenderManagerService } from '@univerjs/engine-render'; -import { IEditorBridgeService } from '@univerjs/sheets-ui'; -import { IZenZoneService, KeyCode } from '@univerjs/ui'; -import type { IAccessor, ICommand, ITextRange, Workbook } from '@univerjs/core'; - -function scrollToTop(accessor: IAccessor) { - const renderManagerService = accessor.get(IRenderManagerService); - const backScrollController = renderManagerService.getRenderById(DOCS_ZEN_EDITOR_UNIT_ID_KEY)?.with(DocBackScrollRenderController); - const textRange = { - startOffset: 0, - endOffset: 0, - }; - if (backScrollController) { - backScrollController.scrollToRange(textRange as ITextRange); - } -} +import { CommandType, DOCS_ZEN_EDITOR_UNIT_ID_KEY, DocumentDataModel, IUniverInstanceService, Tools, UniverInstanceType } from '@univerjs/core'; +import { IEditorService } from '@univerjs/docs-ui'; +import { IRenderManagerService } from '@univerjs/engine-render'; +import { EditingRenderController, IEditorBridgeService } from '@univerjs/sheets-ui'; +import { IZenZoneService } from '@univerjs/ui'; +import type { ICommand, Workbook } from '@univerjs/core'; + +export const OpenZenEditorCommand: ICommand = { + id: 'zen-editor.command.open-zen-editor', + type: CommandType.COMMAND, + handler: async (accessor) => { + const zenZoneService = accessor.get(IZenZoneService); + const editorService = accessor.get(IEditorService); + const editorBridgeService = accessor.get(IEditorBridgeService); + const univerInstanceService = accessor.get(IUniverInstanceService); + + zenZoneService.open(); + + const editor = editorService.getEditor(DOCS_ZEN_EDITOR_UNIT_ID_KEY); + + if (editor == null) { + return false; + } + const editCellState = editorBridgeService.getLatestEditCellState(); + + if (editCellState == null) { + return false; + } + + const snapshot = editCellState.documentLayoutObject.documentModel?.getSnapshot(); + + if (snapshot == null) { + return false; + } + + univerInstanceService.focusUnit(DOCS_ZEN_EDITOR_UNIT_ID_KEY); + + const { body, drawings, drawingsOrder, tableSource, settings } = Tools.deepClone(snapshot); + + const originSnapshot = editor.getDocumentData(); + + const newSnapshot = { + ...originSnapshot, + body, + drawings, + drawingsOrder, + tableSource, + settings, + }; + + const textRanges = [ + { + startOffset: 0, + endOffset: 0, + collapsed: true, + }, + ]; + editor.focus(); + editor.setDocumentData(newSnapshot, textRanges); + // Need to clear undo/redo service when open zen mode. + editor.clearUndoRedoHistory(); + + return true; + }, +}; export const CancelZenEditCommand: ICommand = { id: 'zen-editor.command.cancel-zen-edit', @@ -41,20 +88,8 @@ export const CancelZenEditCommand: ICommand = { const editorBridgeService = accessor.get(IEditorBridgeService); const univerInstanceManager = accessor.get(IUniverInstanceService); - const visibleState = editorBridgeService.isVisible(); - if (visibleState.visible) { - editorBridgeService.changeVisible({ - visible: false, - eventType: DeviceInputEventType.Keyboard, - keycode: KeyCode.ESC, - unitId: '', - }); - } - zenZoneEditorService.close(); - scrollToTop(accessor); - const currentSheetInstance = univerInstanceManager.getCurrentUnitForType(UniverInstanceType.UNIVER_SHEET); if (currentSheetInstance) { univerInstanceManager.focusUnit(currentSheetInstance.getUnitId()); @@ -76,22 +111,29 @@ export const ConfirmZenEditCommand: ICommand = { const zenZoneEditorService = accessor.get(IZenZoneService); const editorBridgeService = accessor.get(IEditorBridgeService); const univerInstanceManager = accessor.get(IUniverInstanceService); - - const visibleState = editorBridgeService.isVisible(); - if (visibleState.visible) { - editorBridgeService.changeVisible({ - visible: false, - eventType: DeviceInputEventType.PointerDown, - unitId: '', - }); - } + const editorService = accessor.get(IEditorService); zenZoneEditorService.close(); - scrollToTop(accessor); + const editor = editorService.getEditor(DOCS_ZEN_EDITOR_UNIT_ID_KEY); + + if (editor == null) { + return false; + } + + const renderManagerService = accessor.get(IRenderManagerService); const currentSheetInstance = univerInstanceManager.getCurrentUnitForType(UniverInstanceType.UNIVER_SHEET); if (currentSheetInstance) { + const currentSheetId = currentSheetInstance.getUnitId(); + + const editingRenderController = renderManagerService.getRenderById(currentSheetId)?.with(EditingRenderController); + + if (editingRenderController) { + const snapshot = Tools.deepClone(editor.getDocumentData()); + editingRenderController.submitCellData(new DocumentDataModel(snapshot)); + } + univerInstanceManager.focusUnit(currentSheetInstance.getUnitId()); editorBridgeService.refreshEditCellState(); return true; diff --git a/packages/sheets-zen-editor/src/commands/operations/zen-editor.operation.ts b/packages/sheets-zen-editor/src/commands/operations/zen-editor.operation.ts deleted file mode 100644 index 28a4ff7a104..00000000000 --- a/packages/sheets-zen-editor/src/commands/operations/zen-editor.operation.ts +++ /dev/null @@ -1,28 +0,0 @@ -/** - * Copyright 2023-present DreamNum Inc. - * - * 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 { CommandType } from '@univerjs/core'; -import type { IOperation } from '@univerjs/core'; - -export const OpenZenEditorOperation: IOperation = { - id: 'zen-editor.operation.open-zen-editor', - - type: CommandType.OPERATION, - - handler: (accessor) => { - return true; - }, -}; diff --git a/packages/sheets-zen-editor/src/controllers/menu.schema.ts b/packages/sheets-zen-editor/src/controllers/menu.schema.ts index 5d0c4d7e958..33446dc18c2 100644 --- a/packages/sheets-zen-editor/src/controllers/menu.schema.ts +++ b/packages/sheets-zen-editor/src/controllers/menu.schema.ts @@ -14,15 +14,15 @@ * limitations under the License. */ -import type { MenuSchemaType } from '@univerjs/ui'; import { ContextMenuGroup, ContextMenuPosition } from '@univerjs/ui'; -import { OpenZenEditorOperation } from '../commands/operations/zen-editor.operation'; +import type { MenuSchemaType } from '@univerjs/ui'; +import { OpenZenEditorCommand } from '../commands/commands/zen-editor.command'; import { ZenEditorMenuItemFactory } from '../views/menu'; export const menuSchema: MenuSchemaType = { [ContextMenuPosition.MAIN_AREA]: { [ContextMenuGroup.OTHERS]: { - [OpenZenEditorOperation.id]: { + [OpenZenEditorCommand.id]: { order: 2, menuItemFactory: ZenEditorMenuItemFactory, }, diff --git a/packages/sheets-zen-editor/src/controllers/shortcuts/zen-editor.shortcut.ts b/packages/sheets-zen-editor/src/controllers/shortcuts/zen-editor.shortcut.ts index 1102a5a249b..c921f20c0c8 100644 --- a/packages/sheets-zen-editor/src/controllers/shortcuts/zen-editor.shortcut.ts +++ b/packages/sheets-zen-editor/src/controllers/shortcuts/zen-editor.shortcut.ts @@ -14,9 +14,9 @@ * limitations under the License. */ -import type { IContextService } from '@univerjs/core'; import { EDITOR_ACTIVATED, FOCUSING_DOC, FOCUSING_EDITOR_STANDALONE, FOCUSING_UNIVER_EDITOR } from '@univerjs/core'; import { type IShortcutItem, KeyCode, MetaKeys } from '@univerjs/ui'; +import type { IContextService } from '@univerjs/core'; import { CancelZenEditCommand, ConfirmZenEditCommand } from '../../commands/commands/zen-editor.command'; diff --git a/packages/sheets-zen-editor/src/controllers/zen-editor-ui.controller.ts b/packages/sheets-zen-editor/src/controllers/zen-editor-ui.controller.ts index dc92aa669d2..f2d4044459b 100644 --- a/packages/sheets-zen-editor/src/controllers/zen-editor-ui.controller.ts +++ b/packages/sheets-zen-editor/src/controllers/zen-editor-ui.controller.ts @@ -17,11 +17,10 @@ import { Disposable, ICommandService, Inject, Injector, LifecycleStages, OnLifecycle } from '@univerjs/core'; import { IMenuManagerService, IShortcutService, IZenZoneService } from '@univerjs/ui'; -import { CancelZenEditCommand, ConfirmZenEditCommand } from '../commands/commands/zen-editor.command'; -import { OpenZenEditorOperation } from '../commands/operations/zen-editor.operation'; +import { CancelZenEditCommand, ConfirmZenEditCommand, OpenZenEditorCommand } from '../commands/commands/zen-editor.command'; import { ZEN_EDITOR_COMPONENT, ZenEditor } from '../views/zen-editor'; -import { ZenEditorCancelShortcut, ZenEditorConfirmShortcut } from './shortcuts/zen-editor.shortcut'; import { menuSchema } from './menu.schema'; +import { ZenEditorCancelShortcut, ZenEditorConfirmShortcut } from './shortcuts/zen-editor.shortcut'; @OnLifecycle(LifecycleStages.Rendered, ZenEditorUIController) export class ZenEditorUIController extends Disposable { @@ -49,7 +48,7 @@ export class ZenEditorUIController extends Disposable { } private _initCommands(): void { - [OpenZenEditorOperation, CancelZenEditCommand, ConfirmZenEditCommand].forEach((c) => { + [OpenZenEditorCommand, CancelZenEditCommand, ConfirmZenEditCommand].forEach((c) => { this.disposeWithMe(this._commandService.registerCommand(c)); }); } diff --git a/packages/sheets-zen-editor/src/controllers/zen-editor.controller.ts b/packages/sheets-zen-editor/src/controllers/zen-editor.controller.ts index 72e97650aaf..33feafa0f64 100644 --- a/packages/sheets-zen-editor/src/controllers/zen-editor.controller.ts +++ b/packages/sheets-zen-editor/src/controllers/zen-editor.controller.ts @@ -15,50 +15,26 @@ */ import { - DEFAULT_EMPTY_DOCUMENT_VALUE, - DOCS_FORMULA_BAR_EDITOR_UNIT_ID_KEY, - DOCS_NORMAL_EDITOR_UNIT_ID_KEY, DOCS_ZEN_EDITOR_UNIT_ID_KEY, - DocumentFlavor, - ICommandService, - Inject, - IUndoRedoService, - IUniverInstanceService, LifecycleStages, OnLifecycle, RxDisposable, - UniverInstanceType, } from '@univerjs/core'; -import { - DocSelectionManagerService, - DocSkeletonManagerService, - RichTextEditingMutation, -} from '@univerjs/docs'; -import { VIEWPORT_KEY as DOC_VIEWPORT_KEY, DocSelectionRenderService, getDocObject } from '@univerjs/docs-ui'; -import { DeviceInputEventType, IRenderManagerService } from '@univerjs/engine-render'; -import { getEditorObject, IEditorBridgeService } from '@univerjs/sheets-ui'; -import { IZenZoneService } from '@univerjs/ui'; +import { VIEWPORT_KEY as DOC_VIEWPORT_KEY, DocBackScrollRenderController } from '@univerjs/docs-ui'; +import { IRenderManagerService } from '@univerjs/engine-render'; +import { getEditorObject } from '@univerjs/sheets-ui'; import { takeUntil } from 'rxjs'; -import type { ICommandInfo, ICustomBlock, ICustomRange, IDocumentData, IDrawings, IParagraph, ITextRun } from '@univerjs/core'; -import type { IRichTextEditingMutationParams } from '@univerjs/docs'; +import type { ITextRange } from '@univerjs/core'; import type { IDocObjectParam } from '@univerjs/docs-ui'; import type { Viewport } from '@univerjs/engine-render'; -import type { IEditorBridgeServiceParam } from '@univerjs/sheets-ui'; -import { OpenZenEditorOperation } from '../commands/operations/zen-editor.operation'; import { IZenEditorManagerService } from '../services/zen-editor.service'; @OnLifecycle(LifecycleStages.Steady, ZenEditorController) export class ZenEditorController extends RxDisposable { constructor( - @IUniverInstanceService private readonly _univerInstanceService: IUniverInstanceService, @IZenEditorManagerService private readonly _zenEditorManagerService: IZenEditorManagerService, - @IRenderManagerService private readonly _renderManagerService: IRenderManagerService, - @ICommandService private readonly _commandService: ICommandService, - @IZenZoneService private readonly _zenZoneService: IZenZoneService, - @IEditorBridgeService private readonly _editorBridgeService: IEditorBridgeService, - @IUndoRedoService private readonly _undoRedoService: IUndoRedoService, - @Inject(DocSelectionManagerService) private readonly _textSelectionManagerService: DocSelectionManagerService + @IRenderManagerService private readonly _renderManagerService: IRenderManagerService ) { super(); @@ -67,48 +43,6 @@ export class ZenEditorController extends RxDisposable { private _initialize() { this._syncZenEditorSize(); - this._commandExecutedListener(); - } - - private _createZenEditorInstance() { - // create univer doc formula bar editor instance - const INITIAL_SNAPSHOT: IDocumentData = { - id: DOCS_ZEN_EDITOR_UNIT_ID_KEY, - body: { - dataStream: `${DEFAULT_EMPTY_DOCUMENT_VALUE}`, - textRuns: [], - tables: [], - customBlocks: [], - paragraphs: [ - { - startIndex: 0, - }, - ], - sectionBreaks: [{ - startIndex: 1, - }], - }, - tableSource: {}, - documentStyle: { - pageSize: { - width: 595, - height: 842, - }, - documentFlavor: DocumentFlavor.MODERN, - marginTop: 50, - marginBottom: 50, - marginRight: 40, - marginLeft: 40, - renderConfig: { - vertexAngle: 0, - centerAngle: 0, - }, - }, - drawings: {}, - drawingsOrder: [], - }; - - return this._univerInstanceService.createUnit(UniverInstanceType.UNIVER_DOC, INITIAL_SNAPSHOT); } // Listen to changes in the size of the zen editor container to set the size of the editor. @@ -118,164 +52,23 @@ export class ZenEditorController extends RxDisposable { return; } + const { width, height } = position; + const editorObject = getEditorObject(DOCS_ZEN_EDITOR_UNIT_ID_KEY, this._renderManagerService); - const zenEditorDataModel = this._univerInstanceService.getUniverDocInstance(DOCS_ZEN_EDITOR_UNIT_ID_KEY); - if (editorObject == null || zenEditorDataModel == null) { + if (editorObject == null) { return; } - const { width, height } = position; - - const { engine } = editorObject; - - const skeleton = this._renderManagerService.getRenderById(DOCS_ZEN_EDITOR_UNIT_ID_KEY)?.with(DocSkeletonManagerService).getSkeleton(); - - // Update page size when container resized. - // zenEditorDataModel.updateDocumentDataPageSize(width); - // resize canvas requestIdleCallback(() => { - engine.resizeBySize(width, height); - + editorObject.engine.resizeBySize(width, height); this._calculatePagePosition(editorObject); - - if (skeleton) { - this._textSelectionManagerService.refreshSelection(); - } + this._scrollToTop(); }); }); } - private _zenEditorInitialState = false; - - private _handleOpenZenEditor() { - if (!this._zenEditorInitialState) { - this._createZenEditorInstance(); - this._zenEditorInitialState = true; - } - - this._zenZoneService.open(); - - const currentSheet = this._univerInstanceService.getCurrentUnitForType(UniverInstanceType.UNIVER_SHEET); - - // Need to clear undo/redo service when open zen mode. - this._undoRedoService.clearUndoRedo(DOCS_ZEN_EDITOR_UNIT_ID_KEY); - - this._univerInstanceService.focusUnit(DOCS_ZEN_EDITOR_UNIT_ID_KEY); - this._univerInstanceService.setCurrentUnitForType(DOCS_ZEN_EDITOR_UNIT_ID_KEY); - const docSelectionRenderService = this._renderManagerService.getRenderById(DOCS_ZEN_EDITOR_UNIT_ID_KEY)?.with(DocSelectionRenderService); - if (docSelectionRenderService) { - docSelectionRenderService.focus(); - } - - const visibleState = this._editorBridgeService.isVisible(); - if (visibleState.visible === false) { - this._editorBridgeService.changeVisible({ - visible: true, - eventType: DeviceInputEventType.PointerDown, - unitId: currentSheet?.getUnitId() ?? '', - }); - } - - const editCellState = this._editorBridgeService.getLatestEditCellState(); - - if (editCellState == null) { - return; - } - - this._editorSyncHandler(editCellState); - - const textRanges = [ - { - startOffset: 0, - endOffset: 0, - }, - ]; - - this._textSelectionManagerService.replaceTextRanges(textRanges, false); - } - - private _editorSyncHandler(param: IEditorBridgeServiceParam) { - const body = param.documentLayoutObject.documentModel?.getBody(); - const dataStream = body?.dataStream; - const paragraphs = body?.paragraphs; - const customBlocks = body?.customBlocks; - const drawings = param.documentLayoutObject.documentModel?.getDrawings(); - const drawingsOrder = param.documentLayoutObject.documentModel?.getDrawingsOrder(); - const customRanges = body?.customRanges; - - let textRuns: ITextRun[] = []; - - if (dataStream == null || (!paragraphs && !customRanges)) { - return; - } - - if (body?.textRuns?.length) { - textRuns = body?.textRuns; - } - - this._syncContentAndRender(DOCS_ZEN_EDITOR_UNIT_ID_KEY, dataStream, paragraphs ?? [], textRuns, customBlocks, drawings, drawingsOrder, customRanges); - - // Also need to resize document and scene after sync content. - // this._autoScroll(); - } - - private _syncContentAndRender( - unitId: string, - dataStream: string, - paragraphs: IParagraph[], - textRuns: ITextRun[] = [], - customBlocks: ICustomBlock[] = [], - drawings: IDrawings = {}, - drawingsOrder: string[] = [], - customRanges: ICustomRange[] = [] - ) { - const INCLUDE_LIST = [ - DOCS_ZEN_EDITOR_UNIT_ID_KEY, - DOCS_NORMAL_EDITOR_UNIT_ID_KEY, - DOCS_FORMULA_BAR_EDITOR_UNIT_ID_KEY, - ]; - - const docSkeletonManagerService = this._renderManagerService.getRenderById(unitId)?.with(DocSkeletonManagerService); - const skeleton = docSkeletonManagerService?.getSkeleton(); - const docDataModel = this._univerInstanceService.getUniverDocInstance(unitId); - const docViewModel = docSkeletonManagerService?.getViewModel(); - - if (docDataModel == null || docViewModel == null || skeleton == null) { - return; - } - - const docBody = docDataModel.getBody()!; - const snapshot = docDataModel.getSnapshot()!; - - docBody.dataStream = dataStream; - docBody.paragraphs = paragraphs; - docBody.customBlocks = customBlocks; - docBody.customRanges = customRanges; - snapshot.drawings = drawings; - snapshot.drawingsOrder = drawingsOrder; - docBody.textRuns = textRuns; - - // Need to empty textRuns(previous formula highlight) every time when sync content(change selection or edit cell or edit formula bar). - if (unitId === DOCS_FORMULA_BAR_EDITOR_UNIT_ID_KEY) { - docBody.textRuns = []; - } - - docViewModel.reset(docDataModel); - - const currentRender = this._getDocObject(); - if (currentRender == null) { - return; - } - - skeleton.calculate(); - - if (INCLUDE_LIST.includes(unitId)) { - currentRender.document.makeDirty(); - } - } - private _calculatePagePosition(currentRender: IDocObjectParam) { const { document: docsComponent, scene, docBackground } = currentRender; @@ -320,7 +113,6 @@ export class ZenEditorController extends RxDisposable { } scene.resize(sceneWidth, sceneHeight + 200); - docsComponent.translate(docsLeft, docsTop); docBackground.translate(docsLeft, docsTop); @@ -335,53 +127,14 @@ export class ZenEditorController extends RxDisposable { return this; } - private _commandExecutedListener() { - const updateCommandList = [OpenZenEditorOperation.id]; - - this.disposeWithMe( - this._commandService.onCommandExecuted((command: ICommandInfo) => { - if (updateCommandList.includes(command.id)) { - this._handleOpenZenEditor(); - } - }) - ); - - const editCommandList = [RichTextEditingMutation.id]; - - this.disposeWithMe( - this._commandService.onCommandExecuted((command: ICommandInfo) => { - if (editCommandList.includes(command.id)) { - const params = command.params as IRichTextEditingMutationParams; - const { unitId } = params; - - if (unitId === DOCS_ZEN_EDITOR_UNIT_ID_KEY) { - // sync cell content to formula editor bar when edit cell editor and vice verse. - const editorDocDataModel = this._univerInstanceService.getUniverDocInstance(unitId); - const docBody = editorDocDataModel?.getBody(); - const dataStream = docBody?.dataStream; - const paragraphs = docBody?.paragraphs; - const textRuns = docBody?.textRuns; - const customBlocks = docBody?.customBlocks; - const drawings = editorDocDataModel?.getDrawings(); - const drawingsOrder = editorDocDataModel?.getDrawingsOrder(); - const customRanges = editorDocDataModel?.getCustomRanges(); - /** - * Fix the issue where content cannot be saved in the doc under Zen mode. - */ - this._editorBridgeService.changeEditorDirty(true); - - if (dataStream == null || paragraphs == null) { - return; - } - - this._syncContentAndRender(DOCS_NORMAL_EDITOR_UNIT_ID_KEY, dataStream, paragraphs, textRuns, customBlocks, drawings, drawingsOrder, customRanges); - } - } - }) - ); - } - - private _getDocObject() { - return getDocObject(this._univerInstanceService, this._renderManagerService); + private _scrollToTop() { + const backScrollController = this._renderManagerService.getRenderById(DOCS_ZEN_EDITOR_UNIT_ID_KEY)?.with(DocBackScrollRenderController); + const textRange = { + startOffset: 0, + endOffset: 0, + }; + if (backScrollController) { + backScrollController.scrollToRange(textRange as ITextRange); + } } } diff --git a/packages/sheets-zen-editor/src/index.ts b/packages/sheets-zen-editor/src/index.ts index 053ff87bbc5..00175ff52d0 100644 --- a/packages/sheets-zen-editor/src/index.ts +++ b/packages/sheets-zen-editor/src/index.ts @@ -14,11 +14,10 @@ * limitations under the License. */ -export { UniverSheetsZenEditorPlugin } from './plugin'; +export { CancelZenEditCommand, ConfirmZenEditCommand, OpenZenEditorCommand } from './commands/commands/zen-editor.command'; // #region - all commands -export { OpenZenEditorOperation } from './commands/operations/zen-editor.operation'; -export { CancelZenEditCommand, ConfirmZenEditCommand } from './commands/commands/zen-editor.command'; +export { UniverSheetsZenEditorPlugin } from './plugin'; // #endregion diff --git a/packages/sheets-zen-editor/src/plugin.ts b/packages/sheets-zen-editor/src/plugin.ts index 164b181a224..256e86ef1b2 100644 --- a/packages/sheets-zen-editor/src/plugin.ts +++ b/packages/sheets-zen-editor/src/plugin.ts @@ -17,11 +17,11 @@ import { IConfigService, Inject, Injector, Plugin, UniverInstanceType } from '@univerjs/core'; import type { Dependency } from '@univerjs/core'; +import { defaultPluginConfig, PLUGIN_CONFIG_KEY } from './controllers/config.schema'; import { ZenEditorController } from './controllers/zen-editor.controller'; import { ZenEditorUIController } from './controllers/zen-editor-ui.controller'; import { IZenEditorManagerService, ZenEditorManagerService } from './services/zen-editor.service'; import type { IUniverSheetsZenEditorConfig } from './controllers/config.schema'; -import { defaultPluginConfig, PLUGIN_CONFIG_KEY } from './controllers/config.schema'; export class UniverSheetsZenEditorPlugin extends Plugin { static override pluginName = 'SHEET_ZEN_EDITOR_PLUGIN'; diff --git a/packages/sheets-zen-editor/src/views/menu.ts b/packages/sheets-zen-editor/src/views/menu.ts index ba581aa64f1..0625101720b 100644 --- a/packages/sheets-zen-editor/src/views/menu.ts +++ b/packages/sheets-zen-editor/src/views/menu.ts @@ -19,11 +19,11 @@ import { RangeProtectionPermissionEditPoint, WorkbookEditablePermission, Workshe import { getCurrentExclusiveRangeInterest$, getCurrentRangeDisable$ } from '@univerjs/sheets-ui'; import { type IMenuButtonItem, MenuItemType } from '@univerjs/ui'; import type { IAccessor } from '@univerjs/core'; -import { OpenZenEditorOperation } from '../commands/operations/zen-editor.operation'; +import { OpenZenEditorCommand } from '../commands/commands/zen-editor.command'; export function ZenEditorMenuItemFactory(accessor: IAccessor): IMenuButtonItem { return { - id: OpenZenEditorOperation.id, + id: OpenZenEditorCommand.id, type: MenuItemType.BUTTON, title: 'rightClick.zenEditor', icon: 'AmplifySingle', diff --git a/packages/sheets-zen-editor/src/views/zen-editor/ZenEditor.tsx b/packages/sheets-zen-editor/src/views/zen-editor/ZenEditor.tsx index 8e9b55696ab..b62cc475516 100644 --- a/packages/sheets-zen-editor/src/views/zen-editor/ZenEditor.tsx +++ b/packages/sheets-zen-editor/src/views/zen-editor/ZenEditor.tsx @@ -14,57 +14,93 @@ * limitations under the License. */ -import { DOCS_ZEN_EDITOR_UNIT_ID_KEY, ICommandService, useDependency } from '@univerjs/core'; -import { IRenderManagerService } from '@univerjs/engine-render'; +import { DEFAULT_EMPTY_DOCUMENT_VALUE, DOCS_ZEN_EDITOR_UNIT_ID_KEY, DocumentFlavor, ICommandService, useDependency } from '@univerjs/core'; +import { IEditorService } from '@univerjs/docs-ui'; import { CheckMarkSingle, CloseSingle } from '@univerjs/icons'; import clsx from 'clsx'; import React, { useEffect, useRef } from 'react'; +import type { IDocumentData } from '@univerjs/core'; import { CancelZenEditCommand, ConfirmZenEditCommand } from '../../commands/commands/zen-editor.command'; + import { IZenEditorManagerService } from '../../services/zen-editor.service'; import styles from './index.module.less'; const COMPONENT_PREFIX = 'ZEN_EDITOR_PLUGIN_'; +// eslint-disable-next-line react-refresh/only-export-components export const ZEN_EDITOR_COMPONENT = `${COMPONENT_PREFIX}ZEN_EDITOR_COMPONENT`; +const INITIAL_SNAPSHOT: IDocumentData = { + id: DOCS_ZEN_EDITOR_UNIT_ID_KEY, + body: { + dataStream: `${DEFAULT_EMPTY_DOCUMENT_VALUE}`, + textRuns: [], + tables: [], + customBlocks: [], + paragraphs: [ + { + startIndex: 0, + }, + ], + sectionBreaks: [{ + startIndex: 1, + }], + }, + tableSource: {}, + documentStyle: { + pageSize: { + width: 595, + height: 842, + }, + documentFlavor: DocumentFlavor.MODERN, + marginTop: 50, + marginBottom: 50, + marginRight: 40, + marginLeft: 40, + renderConfig: { + vertexAngle: 0, + centerAngle: 0, + }, + }, + drawings: {}, + drawingsOrder: [], +}; + export function ZenEditor() { const editorRef = useRef(null); - - const renderManagerService: IRenderManagerService = useDependency(IRenderManagerService); - - const zenEditorManagerService = useDependency(IZenEditorManagerService); + const zenEditorService = useDependency(IZenEditorManagerService); + const editorService = useDependency(IEditorService); const commandService = useDependency(ICommandService); useEffect(() => { - const editor = editorRef.current; + const editorDom = editorRef.current; - if (!editor) { + if (!editorDom) { return; } - const renderSubscription = renderManagerService.currentRender$.subscribe((param) => { - if (param !== DOCS_ZEN_EDITOR_UNIT_ID_KEY) { - return; - } - - const engine = renderManagerService.getRenderById(DOCS_ZEN_EDITOR_UNIT_ID_KEY)?.engine; - engine?.setContainer(editor); - }); + const registerSubscription = editorService.register({ + editorUnitId: DOCS_ZEN_EDITOR_UNIT_ID_KEY, + initialSnapshot: INITIAL_SNAPSHOT, + scrollBar: true, + noNeedVerticalAlign: true, + backScrollOffset: 100, + }, + editorDom); const resizeObserver = new ResizeObserver(() => { - const editorRect = editor.getBoundingClientRect(); - - zenEditorManagerService.setPosition(editorRect); + zenEditorService.setPosition(editorDom.getBoundingClientRect()); }); - resizeObserver.observe(editor); + resizeObserver.observe(editorDom); // Clean up on unmount return () => { - resizeObserver.unobserve(editor); - renderSubscription.unsubscribe(); + registerSubscription.dispose(); + resizeObserver.unobserve(editorDom); }; + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); // Empty dependency array means this effect runs once on mount and clean up on unmount function handleCloseBtnClick() { diff --git a/packages/ui/src/services/message/desktop-message.service.ts b/packages/ui/src/services/message/desktop-message.service.ts index f31a10cdbdf..c29f650967e 100644 --- a/packages/ui/src/services/message/desktop-message.service.ts +++ b/packages/ui/src/services/message/desktop-message.service.ts @@ -14,11 +14,11 @@ * limitations under the License. */ -import type { IMessageOptions, IMessageProps } from '@univerjs/design'; import { Message } from '@univerjs/design'; +import canUseDom from 'rc-util/lib/Dom/canUseDom'; import type { IDisposable } from '@univerjs/core'; -import canUseDom from 'rc-util/lib/Dom/canUseDom'; +import type { IMessageOptions, IMessageProps } from '@univerjs/design'; import type { IMessageService } from './message.service'; export class DesktopMessageService implements IMessageService, IDisposable { diff --git a/packages/ui/src/views/components/zen-zone/index.module.less b/packages/ui/src/views/components/zen-zone/index.module.less index cb5bd3df531..7e4f2bf3417 100644 --- a/packages/ui/src/views/components/zen-zone/index.module.less +++ b/packages/ui/src/views/components/zen-zone/index.module.less @@ -1,6 +1,7 @@ .zen-zone { position: absolute; - display: none; + z-index: -1; + transition: all 0.2s ease-in-out; &-open { z-index: 100; @@ -13,9 +14,10 @@ height: 100%; background: rgb(var(--bg-color)); + opacity: 1; } &-close { - display: none; + opacity: 0; } }