From 9e0d7dcecb422096f80b03dc6e46998055a02a10 Mon Sep 17 00:00:00 2001 From: lumixraku Date: Wed, 27 Nov 2024 12:50:57 +0800 Subject: [PATCH 001/134] fix: SelectionMoveType default to be ONLY_SET --- packages/sheets/src/commands/commands/move-range.command.ts | 5 +++-- packages/sheets/src/services/selections/selection.service.ts | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/sheets/src/commands/commands/move-range.command.ts b/packages/sheets/src/commands/commands/move-range.command.ts index 114aa06d316..d7090a1dbd1 100644 --- a/packages/sheets/src/commands/commands/move-range.command.ts +++ b/packages/sheets/src/commands/commands/move-range.command.ts @@ -32,6 +32,7 @@ import { sequenceExecute, Tools, } from '@univerjs/core'; +import { SelectionMoveType } from '../../services/selections/type'; import { SheetInterceptorService } from '../../services/sheet-interceptor/sheet-interceptor.service'; import { MoveRangeMutation } from '../mutations/move-range.mutation'; import { SetSelectionsOperation } from '../operations/selection.operation'; @@ -88,8 +89,8 @@ export const MoveRangeCommand: ICommand = { params: { unitId, subUnitId, - selections: [{ range: params.toRange, primary: getPrimaryForRange(params.toRange, worksheet) }], + type: SelectionMoveType.MOVE_END, } as ISetSelectionsOperationParams, }, ]; @@ -102,8 +103,8 @@ export const MoveRangeCommand: ICommand = { params: { unitId, subUnitId, - selections: [{ range: params.fromRange, primary: getPrimaryForRange(params.fromRange, worksheet) }], + type: SelectionMoveType.MOVE_END, } as ISetSelectionsOperationParams, }, ]; diff --git a/packages/sheets/src/services/selections/selection.service.ts b/packages/sheets/src/services/selections/selection.service.ts index 4ff65a5fdb7..6b846bd3f12 100644 --- a/packages/sheets/src/services/selections/selection.service.ts +++ b/packages/sheets/src/services/selections/selection.service.ts @@ -110,7 +110,7 @@ export class SheetsSelectionsService extends RxDisposable { type?: SelectionMoveType ): void { if (typeof unitIdOrSelections === 'string' && typeof worksheetIdOrType === 'string') { - this._ensureWorkbookSelection(unitIdOrSelections).setSelections(worksheetIdOrType, selectionDatas!, type ?? SelectionMoveType.MOVE_END); + this._ensureWorkbookSelection(unitIdOrSelections).setSelections(worksheetIdOrType, selectionDatas!, type ?? SelectionMoveType.ONLY_SET); return; } From a03a3475be008ecf227cb38dfba71cf5245c3b9c Mon Sep 17 00:00:00 2001 From: lumixraku Date: Wed, 27 Nov 2024 15:18:15 +0800 Subject: [PATCH 002/134] fix: move range should add SelectionMoveType.move_end fix: move range should add SelectionMoveType.MOVE_END --- .../src/controllers/clipboard/utils.ts | 5 +-- .../src/controllers/move-range.controller.ts | 8 ++--- .../base-selection-render.service.ts | 11 ++----- .../services/selection/selection-control.ts | 6 ++-- .../selection/selection-render.service.ts | 8 +++-- .../selection/selection-shape-extension.ts | 31 ++++++++++--------- .../commands/move-rows-cols.command.ts | 19 +++++++----- .../commands/utils/handle-merge-operation.ts | 5 +-- .../selections/selection-data-model.ts | 11 +++---- 9 files changed, 52 insertions(+), 52 deletions(-) diff --git a/packages/sheets-ui/src/controllers/clipboard/utils.ts b/packages/sheets-ui/src/controllers/clipboard/utils.ts index 5a46292d0c1..4904835d25f 100644 --- a/packages/sheets-ui/src/controllers/clipboard/utils.ts +++ b/packages/sheets-ui/src/controllers/clipboard/utils.ts @@ -34,6 +34,7 @@ import { MoveRangeMutation, RemoveMergeUndoMutationFactory, RemoveWorksheetMergeMutation, + SelectionMoveType, SetRangeValuesMutation, SetRangeValuesUndoMutationFactory, SetSelectionsOperation, @@ -255,8 +256,8 @@ export function getMoveRangeMutations( params: { unitId, subUnitId: toSubUnitId, - selections: [{ range: toRange }], + type: SelectionMoveType.MOVE_END, } as ISetSelectionsOperationParams, }, ]; @@ -269,7 +270,7 @@ export function getMoveRangeMutations( params: { unitId, subUnitId: fromSubUnitId, - + type: SelectionMoveType.MOVE_END, selections: [{ range: fromRange }], }, }, diff --git a/packages/sheets-ui/src/controllers/move-range.controller.ts b/packages/sheets-ui/src/controllers/move-range.controller.ts index 500c64f64e5..97525e6ee8e 100644 --- a/packages/sheets-ui/src/controllers/move-range.controller.ts +++ b/packages/sheets-ui/src/controllers/move-range.controller.ts @@ -15,6 +15,8 @@ */ import type { IRange, Workbook } from '@univerjs/core'; +import type { IRenderContext, IRenderModule } from '@univerjs/engine-render'; +import type { IMoveRangeCommandParams } from '@univerjs/sheets'; import { Disposable, DisposableCollection, @@ -22,10 +24,8 @@ import { Inject, toDisposable, } from '@univerjs/core'; -import type { IMoveRangeCommandParams } from '@univerjs/sheets'; -import { MoveRangeCommand, SheetsSelectionsService } from '@univerjs/sheets'; -import type { IRenderContext, IRenderModule } from '@univerjs/engine-render'; +import { MoveRangeCommand, SheetsSelectionsService } from '@univerjs/sheets'; import { ISheetSelectionRenderService } from '../services/selection/base-selection-render.service'; export class MoveRangeRenderController extends Disposable implements IRenderModule { @@ -51,7 +51,7 @@ export class MoveRangeRenderController extends Disposable implements IRenderModu selectionControls.forEach((controlSelection) => { disposableCollection.add( toDisposable( - controlSelection.selectionMoved$.subscribe((_toRange) => { + controlSelection.selectionMoveEnd$.subscribe((_toRange) => { if (!_toRange) { return; } diff --git a/packages/sheets-ui/src/services/selection/base-selection-render.service.ts b/packages/sheets-ui/src/services/selection/base-selection-render.service.ts index f9770c35726..8052e14bdbc 100644 --- a/packages/sheets-ui/src/services/selection/base-selection-render.service.ts +++ b/packages/sheets-ui/src/services/selection/base-selection-render.service.ts @@ -425,13 +425,6 @@ export class BaseSelectionRenderService extends Disposable implements ISheetSele this._downObserver = null; } - // resetAndEndSelection(): void { - // this.endSelection(); - // this._reset(); - // } - - // TODO: @wzhudev: refactor the method to make it more readable - /** * Init pointer move listener in each pointer down, unbind in each pointer up. * Both cell selections and row-column selections are supported by this method. @@ -442,7 +435,7 @@ export class BaseSelectionRenderService extends Disposable implements ISheetSele * @param moveStartPosX * @param moveStartPosY */ - // eslint-disable-next-line max-lines-per-function + protected _setupPointerMoveListener( viewportMain: Nullable, activeSelectionControl: SelectionControl, @@ -462,7 +455,7 @@ export class BaseSelectionRenderService extends Disposable implements ISheetSele const startViewport = scene.getActiveViewportByCoord(Vector2.FromArray([moveStartPosX, moveStartPosY])); // #region onPointerMove$ - // eslint-disable-next-line max-lines-per-function, complexity + // eslint-disable-next-line complexity this._scenePointerMoveSub = scene.onPointerMove$.subscribeEvent((moveEvt: IPointerEvent | IMouseEvent) => { const { offsetX: moveOffsetX, offsetY: moveOffsetY } = moveEvt; diff --git a/packages/sheets-ui/src/services/selection/selection-control.ts b/packages/sheets-ui/src/services/selection/selection-control.ts index be2e5a7626a..9d6797ff729 100644 --- a/packages/sheets-ui/src/services/selection/selection-control.ts +++ b/packages/sheets-ui/src/services/selection/selection-control.ts @@ -133,7 +133,6 @@ export class SelectionControl extends Disposable { protected _columnHeaderHeight: number = 0; protected _widgetRects: Rect[] = []; - protected _controlExtension: Nullable; private _dispose$ = new BehaviorSubject(this); @@ -144,7 +143,7 @@ export class SelectionControl extends Disposable { * Observer: prompt.controller */ readonly selectionMoving$ = new Subject(); - readonly selectionMoved$ = new Subject(); + readonly selectionMoveEnd$ = new Subject(); readonly selectionScaling$ = new Subject(); readonly selectionScaled$ = new Subject>(); readonly selectionFilling$ = new Subject>(); @@ -173,7 +172,6 @@ export class SelectionControl extends Disposable { this._initialHeader(); } - // eslint-disable-next-line max-lines-per-function private _initializeSheetBody(): void { this._defaultStyle = genNormalSelectionStyle(this._themeService); this._currentStyle = genNormalSelectionStyle(this._themeService); @@ -517,7 +515,7 @@ export class SelectionControl extends Disposable { * Update Control Style And Position of SelectionControl * @param selectionStyle */ - // eslint-disable-next-line max-lines-per-function + protected _updateLayoutOfSelectionControl(selectionStyle?: Nullable>): void { if (selectionStyle) { this.currentStyle = Object.assign({}, this._defaultStyle, selectionStyle); diff --git a/packages/sheets-ui/src/services/selection/selection-render.service.ts b/packages/sheets-ui/src/services/selection/selection-render.service.ts index 10e2443b682..8f808b0f19d 100644 --- a/packages/sheets-ui/src/services/selection/selection-render.service.ts +++ b/packages/sheets-ui/src/services/selection/selection-render.service.ts @@ -23,7 +23,7 @@ import { ICommandService, IContextService, ILogService, Inject, Injector, RANGE_ import { ScrollTimerType, SHEET_VIEWPORT_KEY, Vector2 } from '@univerjs/engine-render'; import { convertSelectionDataToRange, DISABLE_NORMAL_SELECTIONS, SelectionMoveType, SetSelectionsOperation, SheetsSelectionsService } from '@univerjs/sheets'; import { IShortcutService } from '@univerjs/ui'; -import { distinctUntilChanged, startWith } from 'rxjs'; +import { distinctUntilChanged, merge, startWith } from 'rxjs'; import { getCoordByOffset, getSheetObject } from '../../controllers/utils/component-tools'; import { isThisColSelected, isThisRowSelected } from '../../controllers/utils/selections-tools'; import { SheetSkeletonManagerService } from '../sheet-skeleton-manager.service'; @@ -60,6 +60,8 @@ export class SheetSelectionRenderService extends BaseSelectionRenderService impl this._workbookSelections = selectionManagerService.getWorkbookSelections(this._context.unitId); this._init(); + + window.srs = this; } private _init(): void { @@ -146,7 +148,7 @@ export class SheetSelectionRenderService extends BaseSelectionRenderService impl private _initSelectionChangeListener(): void { // normal selection: after dragging selection(move end) - this.disposeWithMe(this._workbookSelections.selectionMoveEnd$.subscribe((selectionWithStyleList) => { + this.disposeWithMe(merge(this._workbookSelections.selectionMoveEnd$, this._workbookSelections.selectionSet$).subscribe((selectionWithStyleList) => { this.resetSelectionsByModelData(selectionWithStyleList); })); } @@ -253,7 +255,7 @@ export class SheetSelectionRenderService extends BaseSelectionRenderService impl * @param viewport * @param scrollTimerType */ - // eslint-disable-next-line max-lines-per-function, complexity + // eslint-disable-next-line complexity protected _onPointerDown( evt: IPointerEvent | IMouseEvent, _zIndex = 0, diff --git a/packages/sheets-ui/src/services/selection/selection-shape-extension.ts b/packages/sheets-ui/src/services/selection/selection-shape-extension.ts index f5b15119390..d7a9fb7bf4d 100644 --- a/packages/sheets-ui/src/services/selection/selection-shape-extension.ts +++ b/packages/sheets-ui/src/services/selection/selection-shape-extension.ts @@ -14,20 +14,20 @@ * limitations under the License. */ -import type { ISelectionWithStyle } from '@univerjs/sheets'; -import { ColorKit, Quantity, UniverInstanceType } from '@univerjs/core'; -import { CURSOR_TYPE, IRenderManagerService, Rect, ScrollTimer, ScrollTimerType, SHEET_VIEWPORT_KEY, Vector2 } from '@univerjs/engine-render'; -import { SELECTION_CONTROL_BORDER_BUFFER_WIDTH } from '@univerjs/sheets'; -/* eslint-disable max-lines-per-function */ import type { IFreeze, Injector, IRange, IRangeWithCoord, Nullable, ThemeService } from '@univerjs/core'; import type { IMouseEvent, IPointerEvent, Scene, SpreadsheetSkeleton, Viewport } from '@univerjs/engine-render'; - +import type { ISelectionWithStyle } from '@univerjs/sheets'; import type { Subscription } from 'rxjs'; + import type { SelectionControl } from './selection-control'; +import { ColorKit, Quantity, UniverInstanceType } from '@univerjs/core'; + +import { CURSOR_TYPE, IRenderManagerService, Rect, ScrollTimer, ScrollTimerType, SHEET_VIEWPORT_KEY, Vector2 } from '@univerjs/engine-render'; +import { SELECTION_CONTROL_BORDER_BUFFER_WIDTH } from '@univerjs/sheets'; import { SheetSkeletonManagerService } from '../sheet-skeleton-manager.service'; import { ISheetSelectionRenderService } from './base-selection-render.service'; import { genNormalSelectionStyle, RANGE_FILL_PERMISSION_CHECK, RANGE_MOVE_PERMISSION_CHECK } from './const'; -import { attachPrimaryWithCoord, attachSelectionWithCoord } from './util'; +import { attachSelectionWithCoord } from './util'; const HELPER_SELECTION_TEMP_NAME = '__SpreadsheetHelperSelectionTempRect'; @@ -192,7 +192,7 @@ export class SelectionShapeExtension { control.resetCursor(); }); - control.onPointerDown$.subscribeEvent(this._controlEvent.bind(this)); + control.onPointerDown$.subscribeEvent(this._controlPointerDownHandler.bind(this)); }); } @@ -258,8 +258,11 @@ export class SelectionShapeExtension { }); this._targetSelection = { ...selectionWithCoord.rangeWithCoord }; - const primaryWithCoordAndMergeInfo = attachPrimaryWithCoord(this._skeleton, primaryCell); - this._control.updateCurrCell(primaryWithCoordAndMergeInfo); + // DO NOT UPDATE CURR CELL while dragging whole selection. + // That cause primary cell change in the middle of dragging, It may cause primary out of range in some cases. + // ex: dragging selection to a merged area. + // const primaryWithCoordAndMergeInfo = attachPrimaryWithCoord(this._skeleton, primaryCell); + // this._control.updateCurrCell(primaryWithCoordAndMergeInfo); this._control.selectionMoving$.next(selectionWithCoord.rangeWithCoord); } @@ -267,7 +270,7 @@ export class SelectionShapeExtension { * Drag move whole selectionControl when cursor turns to crosshair. Not for dragging 8 control points. * @param evt */ - private _controlEvent(evt: IMouseEvent | IPointerEvent) { + private _controlPointerDownHandler(evt: IMouseEvent | IPointerEvent) { const { offsetX: evtOffsetX, offsetY: evtOffsetY } = evt; const scene = this._scene; @@ -370,10 +373,9 @@ export class SelectionShapeExtension { this._clearObserverEvent(); scene.enableObjectsEvent(); this._scrollTimer?.dispose(); - this._control.selectionMoved$.next(this._targetSelection); + this._control.selectionMoveEnd$.next(this._targetSelection); - // _selectionHooks.selectionMoveEnd should placed after this._control.selectionMoved$, - // because selectionMoveEnd will dispose all selectionControls, then this._control will be null. + // _selectionHooks.selectionMoveEnd should placed after this._control.selectionMoveEnd$ this._selectionHooks.selectionMoveEnd?.(); }); } @@ -505,7 +507,6 @@ export class SelectionShapeExtension { this._control.selectionScaled$.next(this._targetSelection); // _selectionHooks.selectionMoveEnd should placed after this._control.selectionMoved$, - // because selectionMoveEnd will dispose all selectionControls, then this._control will be null. this._selectionHooks.selectionMoveEnd?.(); }); } diff --git a/packages/sheets/src/commands/commands/move-rows-cols.command.ts b/packages/sheets/src/commands/commands/move-rows-cols.command.ts index 3bb2ead99fb..dc9a98f733d 100644 --- a/packages/sheets/src/commands/commands/move-rows-cols.command.ts +++ b/packages/sheets/src/commands/commands/move-rows-cols.command.ts @@ -31,6 +31,7 @@ import { sequenceExecute, } from '@univerjs/core'; import { SheetsSelectionsService } from '../../services/selections/selection.service'; +import { SelectionMoveType } from '../../services/selections/type'; import { SheetInterceptorService } from '../../services/sheet-interceptor/sheet-interceptor.service'; import { MoveColsMutation, @@ -65,7 +66,7 @@ export const MoveRowsCommandId = 'sheet.command.move-rows' as const; export const MoveRowsCommand: ICommand = { id: MoveRowsCommandId, type: CommandType.COMMAND, - // eslint-disable-next-line max-lines-per-function + handler: async (accessor: IAccessor, params: IMoveRowsCommandParams) => { const selectionManagerService = accessor.get(SheetsSelectionsService); const { @@ -161,13 +162,17 @@ export const MoveRowsCommand: ICommand = { const setSelectionsParam: ISetSelectionsOperationParams = { unitId, subUnitId, - - selections: [{ range: destSelection, primary: getPrimaryForRange(destSelection, worksheet), style: null }], + type: SelectionMoveType.MOVE_END, + selections: [{ + range: destSelection, + primary: getPrimaryForRange(destSelection, worksheet), + style: null, + }], }; const undoSetSelectionsParam: ISetSelectionsOperationParams = { unitId, subUnitId, - + type: SelectionMoveType.MOVE_END, selections: [{ range: rangeToMove, primary: beforePrimary, style: null }], }; @@ -206,7 +211,7 @@ export const MoveColsCommandId = 'sheet.command.move-cols' as const; export const MoveColsCommand: ICommand = { id: MoveColsCommandId, type: CommandType.COMMAND, - // eslint-disable-next-line max-lines-per-function + handler: async (accessor: IAccessor, params: IMoveColsCommandParams) => { const selectionManagerService = accessor.get(SheetsSelectionsService); const { @@ -301,13 +306,13 @@ export const MoveColsCommand: ICommand = { const setSelectionsParam: ISetSelectionsOperationParams = { unitId, subUnitId, - + type: SelectionMoveType.MOVE_END, selections: [{ range: destSelection, primary: getPrimaryForRange(destSelection, worksheet), style: null }], }; const undoSetSelectionsParam: ISetSelectionsOperationParams = { unitId, subUnitId, - + type: SelectionMoveType.MOVE_END, selections: [{ range: rangeToMove, primary: beforePrimary, style: null }], }; diff --git a/packages/sheets/src/commands/utils/handle-merge-operation.ts b/packages/sheets/src/commands/utils/handle-merge-operation.ts index ec34b3a4d28..3362458f736 100644 --- a/packages/sheets/src/commands/utils/handle-merge-operation.ts +++ b/packages/sheets/src/commands/utils/handle-merge-operation.ts @@ -19,6 +19,7 @@ import type { IAddMergeCommandParams } from '../commands/add-worksheet-merge.com import type { ISetSelectionsOperationParams } from '../operations/selection.operation'; import { Dimension } from '@univerjs/core'; import { SheetsSelectionsService } from '../../services/selections/selection.service'; +import { SelectionMoveType } from '../../services/selections/type'; import { SetSelectionsOperation } from '../operations/selection.operation'; export const AddMergeRedoSelectionsOperationFactory = (accessor: IAccessor, params: IAddMergeCommandParams, ranges: IRange[]) => { @@ -66,7 +67,7 @@ export const AddMergeRedoSelectionsOperationFactory = (accessor: IAccessor, para const setSelectionsParamByRedo: ISetSelectionsOperationParams = { unitId, subUnitId, - + type: SelectionMoveType.ONLY_SET, selections: selectionsByRedo, }; return { @@ -90,7 +91,7 @@ export const AddMergeUndoSelectionsOperationFactory = (accessor: IAccessor, para const setSelectionsParamByUndo: ISetSelectionsOperationParams = { unitId, subUnitId, - + type: SelectionMoveType.ONLY_SET, selections: [...selectionsBeforeMutation], }; return { diff --git a/packages/sheets/src/services/selections/selection-data-model.ts b/packages/sheets/src/services/selections/selection-data-model.ts index ddc81707b14..c4874d76616 100644 --- a/packages/sheets/src/services/selections/selection-data-model.ts +++ b/packages/sheets/src/services/selections/selection-data-model.ts @@ -65,13 +65,13 @@ export class WorkbookSelectionModel extends Disposable { /** Clear all selections in this workbook. */ clear(): void { this._worksheetSelections.clear(); - this._emitOnEnd([]); + this._eventAfterSetSelections([]); } addSelections(sheetId: string, selectionDatas: ISelectionWithStyle[]): void { const selections = this.getSelectionsOfWorksheet(sheetId); selections.push(...selectionDatas); - this._emitOnEnd(selections); + this._eventAfterSetSelections(selections); } /** @@ -97,14 +97,13 @@ export class WorkbookSelectionModel extends Disposable { case SelectionMoveType.MOVE_END: this._beforeSelectionMoveEnd$.next(selectionDatas); this._selectionMoveEnd$.next(selectionDatas); - // this._emitOnEnd(selectionDatas); break; case SelectionMoveType.ONLY_SET: { - this._selectionSet$.next(selectionDatas); + this._eventAfterSetSelections(selectionDatas); break; } default: - this._emitOnEnd(selectionDatas); + this._eventAfterSetSelections(selectionDatas); break; } } @@ -156,7 +155,7 @@ export class WorkbookSelectionModel extends Disposable { this._worksheetSelections.set(sheetId, []); } - private _emitOnEnd(selections: ISelectionWithStyle[]): void { + private _eventAfterSetSelections(selections: ISelectionWithStyle[]): void { this._selectionSet$.next(selections); } } From cb285cfc58be3e8ff16747ce4fc929097b4f6135 Mon Sep 17 00:00:00 2001 From: lumixraku Date: Wed, 27 Nov 2024 17:55:50 +0800 Subject: [PATCH 003/134] fix: playwright prompt undefined --- .../sheets-formula-ui/src/controllers/prompt.controller.ts | 4 +++- .../src/services/selection/selection-render.service.ts | 2 -- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/sheets-formula-ui/src/controllers/prompt.controller.ts b/packages/sheets-formula-ui/src/controllers/prompt.controller.ts index 4ae77684e71..6c5dcad50aa 100644 --- a/packages/sheets-formula-ui/src/controllers/prompt.controller.ts +++ b/packages/sheets-formula-ui/src/controllers/prompt.controller.ts @@ -2010,7 +2010,9 @@ export class PromptController extends Disposable { } private _getEditorObject() { - const editorUnitId = this._univerInstanceService.getCurrentUniverDocInstance()!.getUnitId(); + const docInstance = this._univerInstanceService.getCurrentUniverDocInstance(); + if (!docInstance) return; + const editorUnitId = docInstance.getUnitId(); const editor = this._editorService.getEditor(editorUnitId); return editor?.render; } diff --git a/packages/sheets-ui/src/services/selection/selection-render.service.ts b/packages/sheets-ui/src/services/selection/selection-render.service.ts index 8f808b0f19d..bf432a100bf 100644 --- a/packages/sheets-ui/src/services/selection/selection-render.service.ts +++ b/packages/sheets-ui/src/services/selection/selection-render.service.ts @@ -60,8 +60,6 @@ export class SheetSelectionRenderService extends BaseSelectionRenderService impl this._workbookSelections = selectionManagerService.getWorkbookSelections(this._context.unitId); this._init(); - - window.srs = this; } private _init(): void { From 98a78e0985efba992875304f437e919091acd77b Mon Sep 17 00:00:00 2001 From: zhangw Date: Wed, 27 Nov 2024 19:17:52 +0800 Subject: [PATCH 004/134] feat: update --- packages/core/src/services/context/context.ts | 1 + .../services/editor/editor-manager.service.ts | 6 +- packages/docs-ui/src/shortcuts/utils.ts | 4 +- .../src/controllers/prompt.controller.ts | 22 +-- .../src/controllers/utils/utils.ts | 7 +- .../src/views/formula-editor/index.tsx | 28 ++-- .../activate-cell-edit.operation.ts | 4 +- .../editor/data-sync.controller.ts | 4 +- .../editor/editing.render-controller.ts | 8 +- .../editor-bridge.render-controller.ts | 4 +- .../src/services/editor-bridge.service.ts | 17 +-- .../editor-container/EditorContainer.tsx | 142 +++++++++++++----- .../views/editor-container/index.module.less | 7 + .../src/views/formula-bar/FormulaBar.tsx | 36 +++-- .../src/views/formula-bar/index.module.less | 8 + .../services/slide-editor-bridge.service.ts | 8 +- 16 files changed, 199 insertions(+), 107 deletions(-) diff --git a/packages/core/src/services/context/context.ts b/packages/core/src/services/context/context.ts index 94d2ebd2ff3..50aaa28a5d1 100644 --- a/packages/core/src/services/context/context.ts +++ b/packages/core/src/services/context/context.ts @@ -37,6 +37,7 @@ export const FOCUSING_EDITOR_INPUT_FORMULA = 'FOCUSING_EDITOR_INPUT_FORMULA'; /** The focusing state of the formula editor (Fx bar). */ export const FOCUSING_FX_BAR_EDITOR = 'FOCUSING_FX_BAR_EDITOR'; +/** The focusing state of the cell editor. */ export const FOCUSING_UNIVER_EDITOR = 'FOCUSING_UNIVER_EDITOR'; export const FOCUSING_EDITOR_STANDALONE = 'FOCUSING_EDITOR_INPUT_FORMULA'; 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 ec65a3bb0d8..2540b94d748 100644 --- a/packages/docs-ui/src/services/editor/editor-manager.service.ts +++ b/packages/docs-ui/src/services/editor/editor-manager.service.ts @@ -18,7 +18,7 @@ import type { DocumentDataModel, IDisposable, IDocumentBody, IDocumentData, Null import type { ISuccinctDocRangeParam, Scene } from '@univerjs/engine-render'; import type { Observable } from 'rxjs'; import type { IEditorConfigParams, IEditorStateParams } from './editor'; -import { createIdentifier, DEFAULT_EMPTY_DOCUMENT_VALUE, Disposable, EDITOR_ACTIVATED, FOCUSING_EDITOR_INPUT_FORMULA, FOCUSING_EDITOR_STANDALONE, FOCUSING_UNIVER_EDITOR_STANDALONE_SINGLE_MODE, HorizontalAlign, ICommandService, IContextService, Inject, isInternalEditorID, IUndoRedoService, IUniverInstanceService, toDisposable, UniverInstanceType, VerticalAlign } from '@univerjs/core'; +import { createIdentifier, DEFAULT_EMPTY_DOCUMENT_VALUE, Disposable, EDITOR_ACTIVATED, FOCUSING_EDITOR_INPUT_FORMULA, FOCUSING_EDITOR_STANDALONE, HorizontalAlign, ICommandService, IContextService, Inject, isInternalEditorID, IUndoRedoService, IUniverInstanceService, toDisposable, UniverInstanceType, VerticalAlign } from '@univerjs/core'; import { DocSelectionManagerService } from '@univerjs/docs'; import { isReferenceStrings, LexerTreeBuilder, operatorToken } from '@univerjs/engine-formula'; import { IRenderManagerService } from '@univerjs/engine-render'; @@ -339,7 +339,7 @@ export class EditorService extends Disposable implements IEditorService, IDispos if (!isInternalEditorID(editorUnitId)) { this._contextService.setContextValue(FOCUSING_EDITOR_STANDALONE, true); - this._contextService.setContextValue(FOCUSING_UNIVER_EDITOR_STANDALONE_SINGLE_MODE, editor.isSingle()); + // this._contextService.setContextValue(FOCUSING_UNIVER_EDITOR_STANDALONE_SINGLE_MODE, editor.isSingle()); } if (!this._spreadsheetFocusState) { @@ -586,7 +586,7 @@ export class EditorService extends Disposable implements IEditorService, IDispos editor.dispose(); this._editors.delete(editorUnitId); this._univerInstanceService.disposeUnit(editorUnitId); - this._contextService.setContextValue(FOCUSING_UNIVER_EDITOR_STANDALONE_SINGLE_MODE, false); + // this._contextService.setContextValue(FOCUSING_UNIVER_EDITOR_STANDALONE_SINGLE_MODE, false); // DEBT: no necessary when we refactor editor module if (!this.isSheetEditor(editorUnitId)) return; diff --git a/packages/docs-ui/src/shortcuts/utils.ts b/packages/docs-ui/src/shortcuts/utils.ts index d69fd6499d1..2e3e52cc49f 100644 --- a/packages/docs-ui/src/shortcuts/utils.ts +++ b/packages/docs-ui/src/shortcuts/utils.ts @@ -15,7 +15,7 @@ */ import type { IContextService } from '@univerjs/core'; -import { FOCUSING_COMMON_DRAWINGS, FOCUSING_DOC, FOCUSING_UNIVER_EDITOR, FOCUSING_UNIVER_EDITOR_STANDALONE_SINGLE_MODE } from '@univerjs/core'; +import { FOCUSING_COMMON_DRAWINGS, FOCUSING_DOC, FOCUSING_UNIVER_EDITOR } from '@univerjs/core'; export function whenDocAndEditorFocused(contextService: IContextService): boolean { return contextService.getContextValue(FOCUSING_DOC) @@ -26,6 +26,6 @@ export function whenDocAndEditorFocused(contextService: IContextService): boolea export function whenDocAndEditorFocusedWithBreakLine(contextService: IContextService): boolean { return contextService.getContextValue(FOCUSING_DOC) && contextService.getContextValue(FOCUSING_UNIVER_EDITOR) - && !contextService.getContextValue(FOCUSING_UNIVER_EDITOR_STANDALONE_SINGLE_MODE) + // && !contextService.getContextValue(FOCUSING_UNIVER_EDITOR_STANDALONE_SINGLE_MODE) && !contextService.getContextValue(FOCUSING_COMMON_DRAWINGS); } diff --git a/packages/sheets-formula-ui/src/controllers/prompt.controller.ts b/packages/sheets-formula-ui/src/controllers/prompt.controller.ts index 4ae77684e71..ef4ef292784 100644 --- a/packages/sheets-formula-ui/src/controllers/prompt.controller.ts +++ b/packages/sheets-formula-ui/src/controllers/prompt.controller.ts @@ -197,7 +197,7 @@ export class PromptController extends Disposable { ) { super(); - this._initialize(); + // this._initialize(); } override dispose(): void { @@ -218,17 +218,17 @@ export class PromptController extends Disposable { } private _initialize(): void { - this._initialCursorSync(); - this._initAcceptFormula(); - this._initialFormulaTheme(); - this._initSelectionsEndListener(); + // this._initialCursorSync(); + // this._initAcceptFormula(); + // this._initialFormulaTheme(); + // this._initSelectionsEndListener(); this._closeRangePromptWhenEditorInvisible(); - this._initialEditorInputChange(); - this._commandExecutedListener(); - this._cursorStateListener(); - this._inputFormulaListener(); - this._userMouseListener(); - this._initialChangeEditor(); + // this._initialEditorInputChange(); + // this._commandExecutedListener(); + // this._cursorStateListener(); + // this._inputFormulaListener(); + // this._userMouseListener(); + // this._initialChangeEditor(); } private _initialFormulaTheme() { diff --git a/packages/sheets-formula-ui/src/controllers/utils/utils.ts b/packages/sheets-formula-ui/src/controllers/utils/utils.ts index b092d20df47..33345832e0f 100644 --- a/packages/sheets-formula-ui/src/controllers/utils/utils.ts +++ b/packages/sheets-formula-ui/src/controllers/utils/utils.ts @@ -16,14 +16,15 @@ import type { ICellData, IContextService, Nullable } from '@univerjs/core'; import type { ErrorType } from '@univerjs/engine-formula'; -import { CellValueType, FOCUSING_DOC, FOCUSING_UNIVER_EDITOR, FOCUSING_UNIVER_EDITOR_STANDALONE_SINGLE_MODE, isFormulaId, isFormulaString } from '@univerjs/core'; +import { CellValueType, FOCUSING_DOC, FOCUSING_UNIVER_EDITOR, isFormulaId, isFormulaString } from '@univerjs/core'; import { ERROR_TYPE_SET, stripErrorMargin } from '@univerjs/engine-formula'; export function whenEditorStandalone(contextService: IContextService) { return ( contextService.getContextValue(FOCUSING_DOC) && - contextService.getContextValue(FOCUSING_UNIVER_EDITOR) && - contextService.getContextValue(FOCUSING_UNIVER_EDITOR_STANDALONE_SINGLE_MODE) + contextService.getContextValue(FOCUSING_UNIVER_EDITOR) + // && + // contextService.getContextValue(FOCUSING_UNIVER_EDITOR_STANDALONE_SINGLE_MODE) ); } diff --git a/packages/sheets-formula-ui/src/views/formula-editor/index.tsx b/packages/sheets-formula-ui/src/views/formula-editor/index.tsx index 803f5a9ba75..2e052d37f2c 100644 --- a/packages/sheets-formula-ui/src/views/formula-editor/index.tsx +++ b/packages/sheets-formula-ui/src/views/formula-editor/index.tsx @@ -57,19 +57,28 @@ export interface IFormulaEditorProps { actions?: { handleOutClick?: (e: MouseEvent, cb: () => void) => void; }; + className?: string; + editorId?: string; } const noop = () => { }; export function FormulaEditor(props: IFormulaEditorProps) { - const { errorText, initValue, unitId, subUnitId, isFocus: _isFocus = true, isSupportAcrossSheet = false, - onFocus = noop, - onBlur = noop, - onChange, - onVerify, - actions, + const { + errorText, + initValue, + unitId, + subUnitId, + isFocus: _isFocus = true, + isSupportAcrossSheet = false, + onFocus = noop, + onBlur = noop, + onChange, + onVerify, + actions, + className, + editorId: propEditorId, } = props; const editorService = useDependency(IEditorService); - const sheetEmbeddingRef = useRef(null); const [formulaText, formulaTextSet] = useState(() => { if (initValue.startsWith(operatorToken.EQUALS)) { @@ -77,7 +86,6 @@ export function FormulaEditor(props: IFormulaEditorProps) { } return ''; }); - // init actions if (actions) { actions.handleOutClick = (e: MouseEvent, cb: () => void) => { @@ -96,7 +104,7 @@ export function FormulaEditor(props: IFormulaEditorProps) { const [editor, editorSet] = useState(); const [isFocus, isFocusSet] = useState(_isFocus); const formulaEditorContainerRef = useRef(null); - const editorId = useMemo(() => createInternalEditorID(`${EMBEDDING_FORMULA_EDITOR}-${generateRandomId(4)}`), []); + const editorId = useMemo(() => propEditorId ?? createInternalEditorID(`${EMBEDDING_FORMULA_EDITOR}-${generateRandomId(4)}`), []); const isError = useMemo(() => errorText !== undefined, [errorText]); const getFormulaToken = useFormulaToken(); @@ -243,7 +251,7 @@ export function FormulaEditor(props: IFormulaEditorProps) { }, 30); }; return ( -
+
= { diff --git a/packages/sheets-ui/src/controllers/editor/data-sync.controller.ts b/packages/sheets-ui/src/controllers/editor/data-sync.controller.ts index 11fda5f36ae..abdbe6abfd3 100644 --- a/packages/sheets-ui/src/controllers/editor/data-sync.controller.ts +++ b/packages/sheets-ui/src/controllers/editor/data-sync.controller.ts @@ -45,8 +45,8 @@ export class EditorDataSyncController extends Disposable { } private _initialize() { - this._syncFormulaEditorContent(); - this._commandExecutedListener(); + // this._syncFormulaEditorContent(); + // this._commandExecutedListener(); } private _getEditorViewModel(unitId: string): Nullable { 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 f730b637538..2f03951421d 100644 --- a/packages/sheets-ui/src/controllers/editor/editing.render-controller.ts +++ b/packages/sheets-ui/src/controllers/editor/editing.render-controller.ts @@ -63,9 +63,9 @@ import { getEditorObject } from '../../basics/editor/get-editor-object'; import { MoveSelectionCommand, MoveSelectionEnterAndTabCommand } from '../../commands/commands/set-selection.command'; import { SetCellEditVisibleArrowOperation, SetCellEditVisibleWithF2Operation } from '../../commands/operations/cell-edit.operation'; import { ScrollToRangeOperation } from '../../commands/operations/scroll-to-range.operation'; +import { IEditorBridgeService } from '../../services/editor-bridge.service'; import { ICellEditorManagerService } from '../../services/editor/cell-editor-manager.service'; import { SheetCellEditorResizeService } from '../../services/editor/cell-editor-resize.service'; -import { IEditorBridgeService } from '../../services/editor-bridge.service'; import { MOVE_SELECTION_KEYCODE_LIST } from '../shortcuts/editor.shortcut'; import { extractStringFromForceString, isForceString } from '../utils/cell-tools'; import { normalizeString } from '../utils/char-tools'; @@ -293,17 +293,14 @@ export class EditingRenderController extends Disposable implements IRenderModule * Listen to document edits to refresh the size of the sheet editor, not for normal editor. */ private _commandExecutedListener(d: DisposableCollection) { - const updateCommandList = [RichTextEditingMutation.id]; - d.add(this._commandService.onCommandExecuted((command: ICommandInfo) => { - if (updateCommandList.includes(command.id)) { + if (command.id === RichTextEditingMutation.id) { const params = command.params as IRichTextEditingMutationParams; const { unitId: commandUnitId } = params; // Only when the sheet it attached to is focused. Maybe we should change it to the render unit sys. if ( !this._isCurrentSheetFocused() || - !this._editorService.isSheetEditor(commandUnitId) || isRangeSelector(commandUnitId) ) { return; @@ -449,7 +446,6 @@ export class EditingRenderController extends Disposable implements IRenderModule private async _handleEditorInvisible(param: IEditorBridgeServiceVisibleParam) { const editCellState = this._editorBridgeService.getEditCellState(); - let { keycode } = param; this._setOpenForCurrent(null, null); diff --git a/packages/sheets-ui/src/controllers/render-controllers/editor-bridge.render-controller.ts b/packages/sheets-ui/src/controllers/render-controllers/editor-bridge.render-controller.ts index 57ff7368783..6011fe73798 100644 --- a/packages/sheets-ui/src/controllers/render-controllers/editor-bridge.render-controller.ts +++ b/packages/sheets-ui/src/controllers/render-controllers/editor-bridge.render-controller.ts @@ -171,13 +171,11 @@ export class EditorBridgeRenderController extends RxDisposable implements IRende if (!this._isCurrentSheetFocused()) { return; } - const isFocusFormulaEditor = this._contextService.getContextValue(FOCUSING_FX_BAR_EDITOR); const isFocusSheets = this._contextService.getContextValue(FOCUSING_SHEET); const unitId = render.unitId; if (this._editorBridgeService.isVisible().visible) return; - - if (unitId && isFocusSheets && !isFocusFormulaEditor && this._editorService.isSheetEditor(unitId)) { + if (unitId && isFocusSheets && !isFocusFormulaEditor) { this._showEditorByKeyboard(config); } })); diff --git a/packages/sheets-ui/src/services/editor-bridge.service.ts b/packages/sheets-ui/src/services/editor-bridge.service.ts index 0b057073713..0dc96fa77fb 100644 --- a/packages/sheets-ui/src/services/editor-bridge.service.ts +++ b/packages/sheets-ui/src/services/editor-bridge.service.ts @@ -27,7 +27,6 @@ import { DOCS_NORMAL_EDITOR_UNIT_ID_KEY, EDITOR_ACTIVATED, FOCUSING_EDITOR_STANDALONE, - FOCUSING_UNIVER_EDITOR_STANDALONE_SINGLE_MODE, IContextService, Inject, IUniverInstanceService, @@ -83,8 +82,8 @@ export interface IEditorBridgeService { currentEditCellState$: Observable>; currentEditCellLayout$: Observable>; currentEditCell$: Observable>; - visible$: Observable; + forceKeepVisible$: Observable; dispose(): void; refreshEditCellState(): void; @@ -107,9 +106,6 @@ export interface IEditorBridgeService { export class EditorBridgeService extends Disposable implements IEditorBridgeService, IDisposable { private _editorUnitId: string = DOCS_NORMAL_EDITOR_UNIT_ID_KEY; - - private _isForceKeepVisible: boolean = false; - private _editorIsDirty: boolean = false; private _isDisabled: boolean = false; @@ -140,6 +136,9 @@ export class EditorBridgeService extends Disposable implements IEditorBridgeServ private readonly _afterVisible$ = new BehaviorSubject(this._visible); readonly afterVisible$ = this._afterVisible$.asObservable(); + private readonly _forceKeepVisible$ = new BehaviorSubject(false); + readonly forceKeepVisible$ = this._forceKeepVisible$.asObservable(); + constructor( @Inject(SheetInterceptorService) private readonly _sheetInterceptorService: SheetInterceptorService, @IRenderManagerService private readonly _renderManagerService: IRenderManagerService, @@ -251,7 +250,7 @@ export class EditorBridgeService extends Disposable implements IEditorBridgeServ */ this._contextService.setContextValue(EDITOR_ACTIVATED, false); this._contextService.setContextValue(FOCUSING_EDITOR_STANDALONE, false); - this._contextService.setContextValue(FOCUSING_UNIVER_EDITOR_STANDALONE_SINGLE_MODE, false); + // this._contextService.setContextValue(FOCUSING_UNIVER_EDITOR_STANDALONE_SINGLE_MODE, false); } const editCellState = this.getLatestEditCellState(); @@ -442,15 +441,15 @@ export class EditorBridgeService extends Disposable implements IEditorBridgeServ } enableForceKeepVisible(): void { - this._isForceKeepVisible = true; + this._forceKeepVisible$.next(true); } disableForceKeepVisible(): void { - this._isForceKeepVisible = false; + this._forceKeepVisible$.next(false); } isForceKeepVisible(): boolean { - return this._isForceKeepVisible; + return this._forceKeepVisible$.getValue(); } changeEditorDirty(dirtyStatus: boolean) { diff --git a/packages/sheets-ui/src/views/editor-container/EditorContainer.tsx b/packages/sheets-ui/src/views/editor-container/EditorContainer.tsx index 2522069a324..6e3367caf34 100644 --- a/packages/sheets-ui/src/views/editor-container/EditorContainer.tsx +++ b/packages/sheets-ui/src/views/editor-container/EditorContainer.tsx @@ -14,13 +14,18 @@ * limitations under the License. */ -import type { IDocumentData } from '@univerjs/core'; -import { DEFAULT_EMPTY_DOCUMENT_VALUE, DOCS_NORMAL_EDITOR_UNIT_ID_KEY, DocumentFlavor, IContextService, useDependency } from '@univerjs/core'; -import { IEditorService, TextEditor } from '@univerjs/docs-ui'; - -import { FIX_ONE_PIXEL_BLUR_OFFSET } from '@univerjs/engine-render'; -import { DISABLE_AUTO_FOCUS_KEY, useObservable } from '@univerjs/ui'; -import React, { useEffect, useState } from 'react'; +import type { IAccessor, Workbook } from '@univerjs/core'; +import { DOCS_NORMAL_EDITOR_UNIT_ID_KEY, IContextService, Injector, IUniverInstanceService, UniverInstanceType, useDependency } from '@univerjs/core'; +import { DocSelectionManagerService } from '@univerjs/docs'; + +import { DocSelectionRenderService, IEditorService } from '@univerjs/docs-ui'; +import { matchRefDrawToken } from '@univerjs/engine-formula'; +import { FIX_ONE_PIXEL_BLUR_OFFSET, IRenderManagerService } from '@univerjs/engine-render'; +import { ComponentManager, DISABLE_AUTO_FOCUS_KEY, useObservable } from '@univerjs/ui'; +import React, { useEffect, useMemo, useState } from 'react'; +import { of } from 'rxjs'; +import { EMBEDDING_FORMULA_EDITOR_COMPONENT_KEY } from '../../common/keys'; +import { IEditorBridgeService } from '../../services/editor-bridge.service'; import { ICellEditorManagerService } from '../../services/editor/cell-editor-manager.service'; import styles from './index.module.less'; @@ -35,6 +40,38 @@ const EDITOR_DEFAULT_POSITION = { left: HIDDEN_EDITOR_POSITION, }; +function getCurrentBodyDataStreamAndOffset(accssor: IAccessor) { + const univerInstanceService = accssor.get(IUniverInstanceService); + const documentModel = univerInstanceService.getCurrentUniverDocInstance(); + + if (!documentModel?.getBody()) { + return; + } + + const dataStream = documentModel.getBody()?.dataStream ?? ''; + return { dataStream, offset: 0 }; +} + +function getCurrentChar(accssor: IAccessor) { + const docSelectionManagerService = accssor.get(DocSelectionManagerService); + const activeRange = docSelectionManagerService.getActiveTextRange(); + + if (activeRange == null) { + return; + } + + const { startOffset } = activeRange; + + const config = getCurrentBodyDataStreamAndOffset(accssor); + + if (config == null || startOffset == null) { + return; + } + + const dataStream = config.dataStream; + + return dataStream[startOffset - 1 + config.offset]; +} /** * Cell editor container. * @returns @@ -44,38 +81,33 @@ export const EditorContainer: React.FC = () => { ...EDITOR_DEFAULT_POSITION, }); const cellEditorManagerService = useDependency(ICellEditorManagerService); + const univerInstanceService = useDependency(IUniverInstanceService); + const workbook$ = useMemo(() => univerInstanceService.getCurrentTypeOfUnit$(UniverInstanceType.UNIVER_SHEET), [univerInstanceService]); + const workbook = useObservable(workbook$); + const worksheet = useObservable(workbook?.activeSheet$); + // const editorBridgeService const editorService = useDependency(IEditorService); const contextService = useDependency(IContextService); - + const componentManager = useDependency(ComponentManager); + const renderManagerService = useDependency(IRenderManagerService); + const editorBridgeService = useDependency(IEditorBridgeService); + const renderer = renderManagerService.getRenderById(DOCS_NORMAL_EDITOR_UNIT_ID_KEY); + const docSelectionRenderService = renderer?.with(DocSelectionRenderService); + const [selectionFocusing, setSelectionFocusing] = useState(false); + const visible = useObservable(editorBridgeService.visible$); + const injector = useDependency(Injector); + const onFocus$ = useMemo(() => docSelectionRenderService?.onFocus$ ?? of(), [docSelectionRenderService?.onFocus$]); + const onBlur$ = useMemo(() => docSelectionRenderService?.onBlur$ ?? of(), [docSelectionRenderService?.onBlur$]); + const textSelections$ = useMemo(() => docSelectionRenderService?.textSelectionInner$ ?? of(), [docSelectionRenderService?.textSelectionInner$]); + const forceKeepVisible = useObservable(editorBridgeService.forceKeepVisible$); const disableAutoFocus = useObservable( () => contextService.subscribeContextValue$(DISABLE_AUTO_FOCUS_KEY), false, undefined, [contextService, DISABLE_AUTO_FOCUS_KEY] ); - - const snapshot: IDocumentData = { - id: DOCS_NORMAL_EDITOR_UNIT_ID_KEY, - body: { - dataStream: `${DEFAULT_EMPTY_DOCUMENT_VALUE}`, - tables: [], - textRuns: [], - paragraphs: [ - { - startIndex: 0, - }, - ], - sectionBreaks: [ - { - startIndex: 1, - }, - ], - }, - tableSource: {}, - documentStyle: { - documentFlavor: DocumentFlavor.UNSPECIFIED, - }, - }; + const FormulaEditor = componentManager.get(EMBEDDING_FORMULA_EDITOR_COMPONENT_KEY); + const editState = editorBridgeService.getEditLocation(); useEffect(() => { const sub = cellEditorManagerService.state$.subscribe((param) => { @@ -127,6 +159,34 @@ export const EditorContainer: React.FC = () => { // eslint-disable-next-line react-hooks/exhaustive-deps }, [disableAutoFocus, state]); + useEffect(() => { + const sub1 = onFocus$.subscribe(() => { + setSelectionFocusing(true); + }); + + const sub2 = onBlur$.subscribe(() => { + setSelectionFocusing(false); + }); + + return () => { + sub1.unsubscribe(); + sub2.unsubscribe(); + }; + }, [onBlur$, onFocus$]); + + useEffect(() => { + const sub = textSelections$.subscribe(() => { + const char = getCurrentChar(injector); + const dataStream = getCurrentBodyDataStreamAndOffset(injector)?.dataStream; + if (dataStream?.substring(0, 1) === '=' && char && matchRefDrawToken(char)) { + editorBridgeService.enableForceKeepVisible(); + } else { + editorBridgeService.disableForceKeepVisible(); + } + }); + + return () => sub.unsubscribe(); + }, [editorBridgeService, injector, textSelections$]); return (
= () => { height: state.height, }} > - + {FormulaEditor && ( + {}} + isFocus={visible?.visible && forceKeepVisible} + unitId={editState?.unitId} + subUnitId={editState?.sheetId} + /> + )}
); }; diff --git a/packages/sheets-ui/src/views/editor-container/index.module.less b/packages/sheets-ui/src/views/editor-container/index.module.less index ec4ab4cb5d2..35af97927e7 100644 --- a/packages/sheets-ui/src/views/editor-container/index.module.less +++ b/packages/sheets-ui/src/views/editor-container/index.module.less @@ -30,5 +30,12 @@ // top: -1px !important; // left: -1px !important; } + + .sheet-embedding-formula-editor-wrap { + height: auto; + border: none; + padding: 0; + border-radius: 0; + } } } diff --git a/packages/sheets-ui/src/views/formula-bar/FormulaBar.tsx b/packages/sheets-ui/src/views/formula-bar/FormulaBar.tsx index 71703b6ae79..8ca6761ec15 100644 --- a/packages/sheets-ui/src/views/formula-bar/FormulaBar.tsx +++ b/packages/sheets-ui/src/views/formula-bar/FormulaBar.tsx @@ -16,18 +16,18 @@ import type { IDocumentData, Nullable, Workbook } from '@univerjs/core'; import { BooleanNumber, DEFAULT_EMPTY_DOCUMENT_VALUE, DOCS_FORMULA_BAR_EDITOR_UNIT_ID_KEY, DocumentFlavor, HorizontalAlign, IPermissionService, IUniverInstanceService, Rectangle, UniverInstanceType, useDependency, useObservable, VerticalAlign, WrapStrategy } from '@univerjs/core'; -import { TextEditor } from '@univerjs/docs-ui'; import { DeviceInputEventType } from '@univerjs/engine-render'; import { CheckMarkSingle, CloseSingle, DropdownSingle, FxSingle } from '@univerjs/icons'; import { RangeProtectionPermissionEditPoint, RangeProtectionRuleModel, SheetsSelectionsService, WorkbookEditablePermission, WorksheetEditPermission, WorksheetProtectionRuleModel, WorksheetSetCellValuePermission } from '@univerjs/sheets'; -import { ComponentContainer, KeyCode, useComponentsOfPart } from '@univerjs/ui'; +import { ComponentContainer, ComponentManager, KeyCode, useComponentsOfPart } from '@univerjs/ui'; import clsx from 'clsx'; import React, { useEffect, useLayoutEffect, useState } from 'react'; import { EMPTY, merge, switchMap } from 'rxjs'; +import { EMBEDDING_FORMULA_EDITOR_COMPONENT_KEY } from '../../common/keys'; import { useActiveWorkbook } from '../../components/hook'; import { SheetsUIPart } from '../../consts/ui-name'; -import { IFormulaEditorManagerService } from '../../services/editor/formula-editor-manager.service'; import { IEditorBridgeService } from '../../services/editor-bridge.service'; +import { IFormulaEditorManagerService } from '../../services/editor/formula-editor-manager.service'; import { DefinedName } from '../defined-name/DefinedName'; import styles from './index.module.less'; @@ -47,11 +47,14 @@ export function FormulaBar() { const univerInstanceService = useDependency(IUniverInstanceService); const selectionManager = useDependency(SheetsSelectionsService); const permissionService = useDependency(IPermissionService); + const [isFocus, setIsFocus] = useState(false); const [disable, setDisable] = useState(false); const currentWorkbook = useActiveWorkbook(); + const componentManager = useDependency(ComponentManager); const workbook = useObservable(() => univerInstanceService.getCurrentTypeOfUnit$(UniverInstanceType.UNIVER_SHEET), undefined, undefined, [])!; + const FormulaEditor = componentManager.get(EMBEDDING_FORMULA_EDITOR_COMPONENT_KEY); const formulaAuxUIParts = useComponentsOfPart(SheetsUIPart.FORMULA_AUX); function getPermissionIds(unitId: string, subUnitId: string): string[] { @@ -235,16 +238,23 @@ export function FormulaBar() {
- e.preventDefault()} - className={styles.formulaContent} - snapshot={INITIAL_SNAPSHOT} - isSingle={false} - /> + {FormulaEditor && ( + { + + }} + isFocus={false} + // onFocus={() => setIsFocus(true)} + // onBlur={() => setIsFocus(false)} + /> + )}
{arrowDirection === ArrowDirection.Down ? ( diff --git a/packages/sheets-ui/src/views/formula-bar/index.module.less b/packages/sheets-ui/src/views/formula-bar/index.module.less index ee2fc16823a..ba6f7ec3afe 100644 --- a/packages/sheets-ui/src/views/formula-bar/index.module.less +++ b/packages/sheets-ui/src/views/formula-bar/index.module.less @@ -94,6 +94,14 @@ width: 100%; padding: 0 0 0 10px; + .sheet-embedding-formula-editor-wrap { + height: auto; + border: none; + padding: 0; + border-radius: 0; + height: 100%; + } + .formula-content { position: relative; diff --git a/packages/slides-ui/src/services/slide-editor-bridge.service.ts b/packages/slides-ui/src/services/slide-editor-bridge.service.ts index 097236eade5..ed9dd80ba3f 100644 --- a/packages/slides-ui/src/services/slide-editor-bridge.service.ts +++ b/packages/slides-ui/src/services/slide-editor-bridge.service.ts @@ -14,6 +14,10 @@ * limitations under the License. */ +import type { IDisposable, IDocumentBody, IDocumentData, IDocumentSettings, IDocumentStyle, IParagraph, IParagraphStyle, IPosition, Nullable } from '@univerjs/core'; +import type { Engine, IDocumentLayoutObject, RichText, Scene } from '@univerjs/engine-render'; +import type { KeyCode } from '@univerjs/ui'; +import type { Observable } from 'rxjs'; import { createIdentifier, Disposable, @@ -29,10 +33,6 @@ import { IEditorService } from '@univerjs/docs-ui'; import { DeviceInputEventType, IRenderManagerService } from '@univerjs/engine-render'; import { SLIDE_KEY } from '@univerjs/slides'; import { BehaviorSubject, Subject } from 'rxjs'; -import type { IDisposable, IDocumentBody, IDocumentData, IDocumentSettings, IDocumentStyle, IParagraph, IParagraphStyle, IPosition, Nullable } from '@univerjs/core'; -import type { Engine, IDocumentLayoutObject, RichText, Scene } from '@univerjs/engine-render'; -import type { KeyCode } from '@univerjs/ui'; -import type { Observable } from 'rxjs'; import { SLIDE_EDITOR_ID } from '../const'; // TODO same as @univerjs/slides/views/render/adaptors/index.js From 0a9b30d34717e1057b77ba1ef7fe6358468eaa10 Mon Sep 17 00:00:00 2001 From: lumixraku Date: Wed, 27 Nov 2024 20:34:01 +0800 Subject: [PATCH 005/134] fix: rename --- .../sheets-formula-ui/src/controllers/prompt.controller.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/sheets-formula-ui/src/controllers/prompt.controller.ts b/packages/sheets-formula-ui/src/controllers/prompt.controller.ts index 6c5dcad50aa..8a8084752ed 100644 --- a/packages/sheets-formula-ui/src/controllers/prompt.controller.ts +++ b/packages/sheets-formula-ui/src/controllers/prompt.controller.ts @@ -464,7 +464,7 @@ export class PromptController extends Disposable { } this._onSelectionControlChange(toRange, c); })); - d.add(merge(c.selectionMoved$, c.selectionScaled$).subscribe(() => { + d.add(merge(c.selectionMoveEnd$, c.selectionScaled$).subscribe(() => { this._formulaPromptService.disableLockedSelectionChange(); })); }); @@ -1242,7 +1242,7 @@ export class PromptController extends Disposable { * @param sequenceNodes * @param textSelectionOffset */ - // eslint-disable-next-line max-lines-per-function + private _syncToEditor( sequenceNodes: Array, textSelectionOffset: number, @@ -1542,7 +1542,6 @@ export class PromptController extends Disposable { } } - // eslint-disable-next-line max-lines-per-function private _onSelectionControlChange(toRange: IRangeWithCoord, selectionControl: SelectionControl) { // FIXME: change here const { skeleton } = this._getCurrentUnitIdAndSheetId(); @@ -1759,7 +1758,6 @@ export class PromptController extends Disposable { } } - // eslint-disable-next-line max-lines-per-function private _commandExecutedListener() { // Listen to document edits to refresh the size of the editor. const updateCommandList = [SelectEditorFormulaOperation.id]; From c96e249b0f87e195a639865117f7277b07169561 Mon Sep 17 00:00:00 2001 From: lumixraku Date: Thu, 28 Nov 2024 12:16:06 +0800 Subject: [PATCH 006/134] fix: add a comma after selection would change sheetId !!! --- .../src/controllers/prompt.controller.ts | 8 ++++---- .../src/services/selection/selection-control.ts | 2 ++ .../services/selection/selection-shape-extension.ts | 11 ++++++----- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/packages/sheets-formula-ui/src/controllers/prompt.controller.ts b/packages/sheets-formula-ui/src/controllers/prompt.controller.ts index 8a8084752ed..a79b87e58a2 100644 --- a/packages/sheets-formula-ui/src/controllers/prompt.controller.ts +++ b/packages/sheets-formula-ui/src/controllers/prompt.controller.ts @@ -1004,9 +1004,9 @@ export class PromptController extends Disposable { private _refreshSelectionForReference(refSelectionRenderService: RefSelectionsRenderService, refSelections: IRefSelection[]) { // const [unitId, sheetId] = refSelectionRenderService.getLocation(); const { unitId, sheetId } = this._editorBridgeService.getEditCellState()!; - const { unitId: selfUnitId, sheetId: selfSheetId } = this._getCurrentUnitIdAndSheetId(); + const { unitId: selfUnitId, sheetId: currSheetId } = this._getCurrentUnitIdAndSheetId(); - const isSelfSheet = sheetId === selfSheetId; + const isSelfSheet = sheetId === currSheetId; const workbook = this._univerInstanceService.getUniverSheetInstance(unitId)!; const worksheet = workbook.getSheetBySheetId(sheetId)!; @@ -1033,7 +1033,7 @@ export class PromptController extends Disposable { const refSheetId = this._getSheetIdByName(unitId, sheetName.trim()); // Cross sheet operation - if (!isSelfSheet && refSheetId !== selfSheetId) continue; + if (!isSelfSheet && refSheetId !== currSheetId) continue; // Current sheet operation if (isSelfSheet && sheetName.length !== 0 && refSheetId !== sheetId) continue; @@ -1073,7 +1073,7 @@ export class PromptController extends Disposable { // } if (selectionWithStyle.length) { - this._refSelectionsService.addSelections(unitId, sheetId, selectionWithStyle); + this._refSelectionsService.addSelections(unitId, currSheetId, selectionWithStyle); } } diff --git a/packages/sheets-ui/src/services/selection/selection-control.ts b/packages/sheets-ui/src/services/selection/selection-control.ts index 9d6797ff729..ded5dc32d6f 100644 --- a/packages/sheets-ui/src/services/selection/selection-control.ts +++ b/packages/sheets-ui/src/services/selection/selection-control.ts @@ -688,6 +688,8 @@ export class SelectionControl extends Disposable { /** * Update selection model with new range & primary cell(aka: highlight/current), also update row/col selection size & style. * + * @deprecated use `updateRangeBySelectionWithCoord` and `updateStyle` to do same thing. + * * @param newSelectionRange * @param rowHeaderWidth * @param columnHeaderHeight diff --git a/packages/sheets-ui/src/services/selection/selection-shape-extension.ts b/packages/sheets-ui/src/services/selection/selection-shape-extension.ts index d7a9fb7bf4d..ae6e2d3d8d7 100644 --- a/packages/sheets-ui/src/services/selection/selection-shape-extension.ts +++ b/packages/sheets-ui/src/services/selection/selection-shape-extension.ts @@ -259,10 +259,11 @@ export class SelectionShapeExtension { this._targetSelection = { ...selectionWithCoord.rangeWithCoord }; // DO NOT UPDATE CURR CELL while dragging whole selection. - // That cause primary cell change in the middle of dragging, It may cause primary out of range in some cases. - // ex: dragging selection to a merged area. - // const primaryWithCoordAndMergeInfo = attachPrimaryWithCoord(this._skeleton, primaryCell); - // this._control.updateCurrCell(primaryWithCoordAndMergeInfo); + // Updating the primary cell during the middle of a drag operation may result in the primary cell being out of range in certain scenarios. + // ex: dragging normal selection to a merged area. there is a check to see if this move is valid, if not, the selection process would revert back to original state. + + // normal selection should keep the original state when dragging whole selection. + // Now ref selection needs _control.selectionMoving$ update selection when dragging. this._control.selectionMoving$.next(selectionWithCoord.rangeWithCoord); } @@ -506,7 +507,7 @@ export class SelectionShapeExtension { this._scrollTimer?.dispose(); this._control.selectionScaled$.next(this._targetSelection); - // _selectionHooks.selectionMoveEnd should placed after this._control.selectionMoved$, + // _selectionHooks.selectionMoveEnd should placed after this._control.selectionMoveEnd$, this._selectionHooks.selectionMoveEnd?.(); }); } From 19aba7a8e6ba3e8c18a12d1ade439ec55010f35f Mon Sep 17 00:00:00 2001 From: zhangw Date: Thu, 28 Nov 2024 15:54:35 +0800 Subject: [PATCH 007/134] feat: update --- .../hooks/useFormulaSelection.ts | 82 ++++++++++ .../src/views/formula-editor/index.tsx | 24 ++- .../range-selector/hooks/useKeyboardEvent.ts | 70 +++++++++ .../editor-container/EditorContainer.tsx | 143 ++++++++++-------- 4 files changed, 255 insertions(+), 64 deletions(-) create mode 100644 packages/sheets-formula-ui/src/views/formula-editor/hooks/useFormulaSelection.ts create mode 100644 packages/sheets-formula-ui/src/views/range-selector/hooks/useKeyboardEvent.ts diff --git a/packages/sheets-formula-ui/src/views/formula-editor/hooks/useFormulaSelection.ts b/packages/sheets-formula-ui/src/views/formula-editor/hooks/useFormulaSelection.ts new file mode 100644 index 00000000000..cbb7024ea3f --- /dev/null +++ b/packages/sheets-formula-ui/src/views/formula-editor/hooks/useFormulaSelection.ts @@ -0,0 +1,82 @@ +/** + * 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 type { IAccessor } from '@univerjs/core'; +import { Injector, IUniverInstanceService, useDependency } from '@univerjs/core'; +import { DocSelectionManagerService } from '@univerjs/docs'; +import { DocSelectionRenderService } from '@univerjs/docs-ui'; +import { matchRefDrawToken } from '@univerjs/engine-formula'; +import { IRenderManagerService } from '@univerjs/engine-render'; +import { useEffect, useMemo, useState } from 'react'; +import { of } from 'rxjs'; + +function getCurrentBodyDataStreamAndOffset(accssor: IAccessor) { + const univerInstanceService = accssor.get(IUniverInstanceService); + const documentModel = univerInstanceService.getCurrentUniverDocInstance(); + + if (!documentModel?.getBody()) { + return; + } + + const dataStream = documentModel.getBody()?.dataStream ?? ''; + return { dataStream, offset: 0 }; +} + +function getCurrentChar(accssor: IAccessor) { + const docSelectionManagerService = accssor.get(DocSelectionManagerService); + const activeRange = docSelectionManagerService.getActiveTextRange(); + + if (activeRange == null) { + return; + } + + const { startOffset } = activeRange; + + const config = getCurrentBodyDataStreamAndOffset(accssor); + + if (config == null || startOffset == null) { + return; + } + + const dataStream = config.dataStream; + + return dataStream[startOffset - 1 + config.offset]; +} + +export function useFormulaSelecting(editorId: string) { + const renderManagerService = useDependency(IRenderManagerService); + const renderer = renderManagerService.getRenderById(editorId); + const docSelectionRenderService = renderer?.with(DocSelectionRenderService); + const injector = useDependency(Injector); + const textSelections$ = useMemo(() => docSelectionRenderService?.textSelectionInner$ ?? of(), [docSelectionRenderService?.textSelectionInner$]); + const [isSelecting, setIsSelecting] = useState(false); + + useEffect(() => { + const sub = textSelections$.subscribe(() => { + const char = getCurrentChar(injector); + const dataStream = getCurrentBodyDataStreamAndOffset(injector)?.dataStream; + if (dataStream?.substring(0, 1) === '=' && char && matchRefDrawToken(char)) { + setIsSelecting(true); + } else { + setIsSelecting(false); + } + }); + + return () => sub.unsubscribe(); + }, [injector, textSelections$]); + + return isSelecting; +} diff --git a/packages/sheets-formula-ui/src/views/formula-editor/index.tsx b/packages/sheets-formula-ui/src/views/formula-editor/index.tsx index 2e052d37f2c..822711c3019 100644 --- a/packages/sheets-formula-ui/src/views/formula-editor/index.tsx +++ b/packages/sheets-formula-ui/src/views/formula-editor/index.tsx @@ -17,10 +17,12 @@ import type { IDisposable } from '@univerjs/core'; import type { Editor } from '@univerjs/docs-ui'; import type { ReactNode } from 'react'; +import type { IKeyboardEventConfig } from '../range-selector/hooks/useKeyboardEvent'; import { createInternalEditorID, generateRandomId, useDependency } from '@univerjs/core'; import { DocBackScrollRenderController, IEditorService } from '@univerjs/docs-ui'; import { operatorToken } from '@univerjs/engine-formula'; import { EMBEDDING_FORMULA_EDITOR } from '@univerjs/sheets-ui'; +import { useEvent } from '@univerjs/ui'; import clsx from 'clsx'; import React, { useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react'; import { useEmitChange } from '../range-selector/hooks/useEmitChange'; @@ -28,6 +30,7 @@ import { useFirstHighlightDoc } from '../range-selector/hooks/useFirstHighlightD import { useFocus } from '../range-selector/hooks/useFocus'; import { useFormulaToken } from '../range-selector/hooks/useFormulaToken'; import { useDocHight, useSheetHighlight } from '../range-selector/hooks/useHighlight'; +import { useKeyboardEvent } from '../range-selector/hooks/useKeyboardEvent'; import { useLeftAndRightArrow } from '../range-selector/hooks/useLeftAndRightArrow'; import { useRefactorEffect } from '../range-selector/hooks/useRefactorEffect'; import { useRefocus } from '../range-selector/hooks/useRefocus'; @@ -37,6 +40,7 @@ import { useSwitchSheet } from '../range-selector/hooks/useSwitchSheet'; import { HelpFunction } from './help-function/HelpFunction'; import { useFormulaDescribe } from './hooks/useFormulaDescribe'; import { useFormulaSearch } from './hooks/useFormulaSearch'; +import { useFormulaSelecting } from './hooks/useFormulaSelection'; import { useSheetSelectionChange } from './hooks/useSheetSelectionChange'; import { useVerify } from './hooks/useVerify'; import styles from './index.module.less'; @@ -59,6 +63,9 @@ export interface IFormulaEditorProps { }; className?: string; editorId?: string; + moveCursor?: boolean; + onFormulaSelectingChange?: (isSelecting: boolean) => void; + keyboradEventConfig?: IKeyboardEventConfig; } const noop = () => { }; export function FormulaEditor(props: IFormulaEditorProps) { @@ -76,6 +83,9 @@ export function FormulaEditor(props: IFormulaEditorProps) { actions, className, editorId: propEditorId, + moveCursor = true, + onFormulaSelectingChange: propOnFormulaSelectingChange, + keyboradEventConfig, } = props; const editorService = useDependency(IEditorService); @@ -99,14 +109,14 @@ export function FormulaEditor(props: IFormulaEditorProps) { const formulaWithoutEqualSymbol = useMemo(() => { return getFormulaText(formulaText); }, [formulaText]); - + const onFormulaSelectingChange = useEvent(propOnFormulaSelectingChange); const searchFunctionRef = useRef(null); const [editor, editorSet] = useState(); const [isFocus, isFocusSet] = useState(_isFocus); const formulaEditorContainerRef = useRef(null); const editorId = useMemo(() => propEditorId ?? createInternalEditorID(`${EMBEDDING_FORMULA_EDITOR}-${generateRandomId(4)}`), []); const isError = useMemo(() => errorText !== undefined, [errorText]); - + const isSelecting = useFormulaSelecting(editorId); const getFormulaToken = useFormulaToken(); const sequenceNodes = useMemo(() => getFormulaToken(formulaWithoutEqualSymbol), [formulaWithoutEqualSymbol]); @@ -125,12 +135,16 @@ export function FormulaEditor(props: IFormulaEditorProps) { highlightSheet(ranges); }; - // const refSelections = useDocHight(editorId, sequenceNodes); useVerify(isFocus, onVerify, formulaText); const focus = useFocus(editor); const resetSelection = useResetSelection(isFocus); + useEffect(() => { + onFormulaSelectingChange(isSelecting); + }, [onFormulaSelectingChange, isSelecting]); + + useKeyboardEvent(isFocus, keyboradEventConfig, editor); useLayoutEffect(() => { // 在进行多个 input 切换的时候,失焦必须快于获得焦点. if (_isFocus) { @@ -150,8 +164,8 @@ export function FormulaEditor(props: IFormulaEditorProps) { }, [_isFocus, focus]); const { checkScrollBar } = useResize(editor); - useRefactorEffect(isFocus, unitId); - useLeftAndRightArrow(isFocus, editor); + useRefactorEffect(isFocus && isSelecting, unitId); + useLeftAndRightArrow(isFocus && moveCursor, editor); const handleSelectionChange = (refString: string, offset: number, isEnd: boolean) => { const result = `=${refString}`; diff --git a/packages/sheets-formula-ui/src/views/range-selector/hooks/useKeyboardEvent.ts b/packages/sheets-formula-ui/src/views/range-selector/hooks/useKeyboardEvent.ts new file mode 100644 index 00000000000..0e0d3f5318a --- /dev/null +++ b/packages/sheets-formula-ui/src/views/range-selector/hooks/useKeyboardEvent.ts @@ -0,0 +1,70 @@ +/** + * 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 type { Editor } from '@univerjs/docs-ui'; +import type { KeyCode, MetaKeys } from '@univerjs/ui'; +import { CommandType, DisposableCollection, ICommandService, useDependency } from '@univerjs/core'; +import { DeviceInputEventType } from '@univerjs/engine-render'; +import { IShortcutService } from '@univerjs/ui'; +import { useEffect } from 'react'; + +export interface IKeyboardEventConfig { + keyCodes: { keyCode: KeyCode; metaKey?: MetaKeys }[]; + handler: (keyCode: KeyCode, metaKey?: MetaKeys) => void; +} + +export function useKeyboardEvent(isNeed: boolean, config?: IKeyboardEventConfig, editor?: Editor) { + const commandService = useDependency(ICommandService); + const shortcutService = useDependency(IShortcutService); + + useEffect(() => { + if (!editor || !isNeed || !config) { + return; + } + const editorId = editor.getEditorId(); + const operationId = `sheet.operation.formula-embedding-editor-${editorId}-keyboard-event`; + const d = new DisposableCollection(); + + d.add(commandService.registerCommand({ + id: operationId, + type: CommandType.OPERATION, + handler(_event, params) { + const { keyCode, metaKey } = params as { eventType: DeviceInputEventType; keyCode: KeyCode; metaKey?: MetaKeys }; + config.handler(keyCode, metaKey); + }, + })); + + config.keyCodes.map((keyCode) => { + return { + id: operationId, + binding: keyCode.metaKey ? keyCode.keyCode | keyCode.metaKey : keyCode.keyCode, + preconditions: () => true, + priority: 901, + staticParameters: { + eventType: DeviceInputEventType.Keyboard, + keyCode: keyCode.keyCode, + metaKey: keyCode.metaKey, + }, + }; + }).forEach((item) => { + d.add(shortcutService.registerShortcut(item)); + }); + + return () => { + d.dispose(); + }; + }, [commandService, config, editor, isNeed, shortcutService]); +} diff --git a/packages/sheets-ui/src/views/editor-container/EditorContainer.tsx b/packages/sheets-ui/src/views/editor-container/EditorContainer.tsx index 6e3367caf34..cd817a5584a 100644 --- a/packages/sheets-ui/src/views/editor-container/EditorContainer.tsx +++ b/packages/sheets-ui/src/views/editor-container/EditorContainer.tsx @@ -14,16 +14,13 @@ * limitations under the License. */ -import type { IAccessor, Workbook } from '@univerjs/core'; -import { DOCS_NORMAL_EDITOR_UNIT_ID_KEY, IContextService, Injector, IUniverInstanceService, UniverInstanceType, useDependency } from '@univerjs/core'; -import { DocSelectionManagerService } from '@univerjs/docs'; - +import { Direction, DOCS_NORMAL_EDITOR_UNIT_ID_KEY, ICommandService, IContextService, useDependency } from '@univerjs/core'; import { DocSelectionRenderService, IEditorService } from '@univerjs/docs-ui'; -import { matchRefDrawToken } from '@univerjs/engine-formula'; -import { FIX_ONE_PIXEL_BLUR_OFFSET, IRenderManagerService } from '@univerjs/engine-render'; -import { ComponentManager, DISABLE_AUTO_FOCUS_KEY, useObservable } from '@univerjs/ui'; -import React, { useEffect, useMemo, useState } from 'react'; +import { DeviceInputEventType, FIX_ONE_PIXEL_BLUR_OFFSET, IRenderManagerService } from '@univerjs/engine-render'; +import { ComponentManager, DISABLE_AUTO_FOCUS_KEY, KeyCode, MetaKeys, useObservable } from '@univerjs/ui'; +import React, { useEffect, useMemo, useRef, useState } from 'react'; import { of } from 'rxjs'; +import { ExpandSelectionCommand, JumpOver, MoveSelectionCommand } from '../../commands/commands/set-selection.command'; import { EMBEDDING_FORMULA_EDITOR_COMPONENT_KEY } from '../../common/keys'; import { IEditorBridgeService } from '../../services/editor-bridge.service'; import { ICellEditorManagerService } from '../../services/editor/cell-editor-manager.service'; @@ -40,38 +37,6 @@ const EDITOR_DEFAULT_POSITION = { left: HIDDEN_EDITOR_POSITION, }; -function getCurrentBodyDataStreamAndOffset(accssor: IAccessor) { - const univerInstanceService = accssor.get(IUniverInstanceService); - const documentModel = univerInstanceService.getCurrentUniverDocInstance(); - - if (!documentModel?.getBody()) { - return; - } - - const dataStream = documentModel.getBody()?.dataStream ?? ''; - return { dataStream, offset: 0 }; -} - -function getCurrentChar(accssor: IAccessor) { - const docSelectionManagerService = accssor.get(DocSelectionManagerService); - const activeRange = docSelectionManagerService.getActiveTextRange(); - - if (activeRange == null) { - return; - } - - const { startOffset } = activeRange; - - const config = getCurrentBodyDataStreamAndOffset(accssor); - - if (config == null || startOffset == null) { - return; - } - - const dataStream = config.dataStream; - - return dataStream[startOffset - 1 + config.offset]; -} /** * Cell editor container. * @returns @@ -81,11 +46,6 @@ export const EditorContainer: React.FC = () => { ...EDITOR_DEFAULT_POSITION, }); const cellEditorManagerService = useDependency(ICellEditorManagerService); - const univerInstanceService = useDependency(IUniverInstanceService); - const workbook$ = useMemo(() => univerInstanceService.getCurrentTypeOfUnit$(UniverInstanceType.UNIVER_SHEET), [univerInstanceService]); - const workbook = useObservable(workbook$); - const worksheet = useObservable(workbook?.activeSheet$); - // const editorBridgeService const editorService = useDependency(IEditorService); const contextService = useDependency(IContextService); const componentManager = useDependency(ComponentManager); @@ -95,11 +55,11 @@ export const EditorContainer: React.FC = () => { const docSelectionRenderService = renderer?.with(DocSelectionRenderService); const [selectionFocusing, setSelectionFocusing] = useState(false); const visible = useObservable(editorBridgeService.visible$); - const injector = useDependency(Injector); const onFocus$ = useMemo(() => docSelectionRenderService?.onFocus$ ?? of(), [docSelectionRenderService?.onFocus$]); const onBlur$ = useMemo(() => docSelectionRenderService?.onBlur$ ?? of(), [docSelectionRenderService?.onBlur$]); - const textSelections$ = useMemo(() => docSelectionRenderService?.textSelectionInner$ ?? of(), [docSelectionRenderService?.textSelectionInner$]); const forceKeepVisible = useObservable(editorBridgeService.forceKeepVisible$); + const commandService = useDependency(ICommandService); + const isRefSelecting = useRef(false); const disableAutoFocus = useObservable( () => contextService.subscribeContextValue$(DISABLE_AUTO_FOCUS_KEY), false, @@ -174,19 +134,74 @@ export const EditorContainer: React.FC = () => { }; }, [onBlur$, onFocus$]); - useEffect(() => { - const sub = textSelections$.subscribe(() => { - const char = getCurrentChar(injector); - const dataStream = getCurrentBodyDataStreamAndOffset(injector)?.dataStream; - if (dataStream?.substring(0, 1) === '=' && char && matchRefDrawToken(char)) { - editorBridgeService.enableForceKeepVisible(); - } else { - editorBridgeService.disableForceKeepVisible(); + const keyCodeConfig = useMemo(() => ({ + keyCodes: [ + { keyCode: KeyCode.ENTER }, + { keyCode: KeyCode.ESC }, + { keyCode: KeyCode.TAB }, + { keyCode: KeyCode.ARROW_DOWN }, + { keyCode: KeyCode.ARROW_LEFT }, + { keyCode: KeyCode.ARROW_RIGHT }, + { keyCode: KeyCode.ARROW_UP }, + { keyCode: KeyCode.ARROW_DOWN, metaKey: MetaKeys.SHIFT }, + { keyCode: KeyCode.ARROW_LEFT, metaKey: MetaKeys.SHIFT }, + { keyCode: KeyCode.ARROW_RIGHT, metaKey: MetaKeys.SHIFT }, + { keyCode: KeyCode.ARROW_UP, metaKey: MetaKeys.SHIFT }, + { keyCode: KeyCode.ARROW_DOWN, metaKey: MetaKeys.CTRL_COMMAND }, + { keyCode: KeyCode.ARROW_LEFT, metaKey: MetaKeys.CTRL_COMMAND }, + { keyCode: KeyCode.ARROW_RIGHT, metaKey: MetaKeys.CTRL_COMMAND }, + { keyCode: KeyCode.ARROW_UP, metaKey: MetaKeys.CTRL_COMMAND }, + { keyCode: KeyCode.ARROW_DOWN, metaKey: MetaKeys.CTRL_COMMAND | MetaKeys.SHIFT }, + { keyCode: KeyCode.ARROW_LEFT, metaKey: MetaKeys.CTRL_COMMAND | MetaKeys.SHIFT }, + { keyCode: KeyCode.ARROW_RIGHT, metaKey: MetaKeys.CTRL_COMMAND | MetaKeys.SHIFT }, + { keyCode: KeyCode.ARROW_UP, metaKey: MetaKeys.CTRL_COMMAND | MetaKeys.SHIFT }, + ], + handler: (keycode: KeyCode, metaKey?: MetaKeys) => { + if (keycode === KeyCode.ENTER || keycode === KeyCode.ESC || keycode === KeyCode.TAB) { + editorBridgeService.changeVisible({ + visible: false, + eventType: DeviceInputEventType.Keyboard, + keycode, + unitId: '', + }); + return; } - }); - return () => sub.unsubscribe(); - }, [editorBridgeService, injector, textSelections$]); + let direction = Direction.DOWN; + if (keycode === KeyCode.ARROW_DOWN) { + direction = Direction.DOWN; + } else if (keycode === KeyCode.ARROW_UP) { + direction = Direction.UP; + } else if (keycode === KeyCode.ARROW_LEFT) { + direction = Direction.LEFT; + } else if (keycode === KeyCode.ARROW_RIGHT) { + direction = Direction.RIGHT; + } + + if (isRefSelecting.current) { + if (metaKey === MetaKeys.CTRL_COMMAND) { + commandService.executeCommand(MoveSelectionCommand.id, { + direction, + jumpOver: JumpOver.moveGap, + }); + } else if (metaKey === MetaKeys.SHIFT) { + commandService.executeCommand(ExpandSelectionCommand.id, { + direction, + }); + } else if (metaKey === (MetaKeys.CTRL_COMMAND | MetaKeys.SHIFT)) { + commandService.executeCommand(ExpandSelectionCommand.id, { + direction, + jumpOver: JumpOver.moveGap, + }); + } else { + commandService.executeCommand(MoveSelectionCommand.id, { + direction, + }); + } + } + }, + }), [commandService, editorBridgeService]); + return (
= () => { isSingle={false} initValue="" onChange={() => {}} - isFocus={visible?.visible && forceKeepVisible} + isFocus={visible?.visible} unitId={editState?.unitId} subUnitId={editState?.sheetId} + moveCursor={false} + keyboradEventConfig={keyCodeConfig} + onFormulaSelectingChange={(isSelecting: boolean) => { + isRefSelecting.current = isSelecting; + if (isSelecting) { + editorBridgeService.enableForceKeepVisible(); + } else { + editorBridgeService.disableForceKeepVisible(); + } + }} /> )}
From a09e6792d5b5c4d03f55e81ef130414352473cbc Mon Sep 17 00:00:00 2001 From: zhangw Date: Thu, 28 Nov 2024 16:42:24 +0800 Subject: [PATCH 008/134] feat: update --- .../src/views/formula-editor/index.tsx | 2 +- .../views/range-selector/hooks/useRefactorEffect.ts | 6 +++--- .../src/views/range-selector/index.tsx | 4 ++-- .../controllers/editor/editing.render-controller.ts | 1 - .../src/views/editor-container/EditorContainer.tsx | 11 ++++++++++- 5 files changed, 16 insertions(+), 8 deletions(-) diff --git a/packages/sheets-formula-ui/src/views/formula-editor/index.tsx b/packages/sheets-formula-ui/src/views/formula-editor/index.tsx index 822711c3019..021051bc1dc 100644 --- a/packages/sheets-formula-ui/src/views/formula-editor/index.tsx +++ b/packages/sheets-formula-ui/src/views/formula-editor/index.tsx @@ -164,7 +164,7 @@ export function FormulaEditor(props: IFormulaEditorProps) { }, [_isFocus, focus]); const { checkScrollBar } = useResize(editor); - useRefactorEffect(isFocus && isSelecting, unitId); + useRefactorEffect(isFocus, isSelecting, unitId); useLeftAndRightArrow(isFocus && moveCursor, editor); const handleSelectionChange = (refString: string, offset: number, isEnd: boolean) => { diff --git a/packages/sheets-formula-ui/src/views/range-selector/hooks/useRefactorEffect.ts b/packages/sheets-formula-ui/src/views/range-selector/hooks/useRefactorEffect.ts index c032e395d3c..d6e745424be 100644 --- a/packages/sheets-formula-ui/src/views/range-selector/hooks/useRefactorEffect.ts +++ b/packages/sheets-formula-ui/src/views/range-selector/hooks/useRefactorEffect.ts @@ -22,7 +22,7 @@ import { useEffect, useLayoutEffect } from 'react'; import { RefSelectionsRenderService } from '../../../services/render-services/ref-selections.render-service'; -export const useRefactorEffect = (isNeed: boolean, unitId: string) => { +export const useRefactorEffect = (isNeed: boolean, selecting: boolean, unitId: string) => { const renderManagerService = useDependency(IRenderManagerService); const contextService = useDependency(IContextService); const contextMenuService = useDependency(IContextMenuService); @@ -31,7 +31,7 @@ export const useRefactorEffect = (isNeed: boolean, unitId: string) => { const render = renderManagerService.getRenderById(unitId); const refSelectionsRenderService = render?.with(RefSelectionsRenderService); useLayoutEffect(() => { - if (isNeed) { + if (isNeed && selecting) { const d1 = refSelectionsRenderService?.enableSelectionChanging(); contextService.setContextValue(DISABLE_NORMAL_SELECTIONS, true); contextService.setContextValue(EDITOR_ACTIVATED, true); @@ -42,7 +42,7 @@ export const useRefactorEffect = (isNeed: boolean, unitId: string) => { d1?.dispose(); }; } - }, [isNeed]); + }, [isNeed, selecting]); useLayoutEffect(() => { if (isNeed) { diff --git a/packages/sheets-formula-ui/src/views/range-selector/index.tsx b/packages/sheets-formula-ui/src/views/range-selector/index.tsx index 904f2a7bb5a..0399cd95c23 100644 --- a/packages/sheets-formula-ui/src/views/range-selector/index.tsx +++ b/packages/sheets-formula-ui/src/views/range-selector/index.tsx @@ -229,7 +229,7 @@ export function RangeSelector(props: IRangeSelectorProps) { useSheetSelectionChange(isNeed, unitId, subUnitId, sequenceNodes, isSupportAcrossSheet, isOnlyOneRange, handleSheetSelectionChange); - useRefactorEffect(isNeed, unitId); + useRefactorEffect(isNeed, isNeed, unitId); useOnlyOneRange(unitId, isOnlyOneRange); @@ -479,7 +479,7 @@ function RangeSelectorDialog(props: { const highlightSheet = useSheetHighlight(unitId); useSheetSelectionChange(focusIndex >= 0, unitId, subUnitId, sequenceNodes, isSupportAcrossSheet, isOnlyOneRange, handleSheetSelectionChange); - useRefactorEffect(focusIndex >= 0, unitId); + useRefactorEffect(focusIndex >= 0, focusIndex >= 0, unitId); useOnlyOneRange(unitId, isOnlyOneRange); useSwitchSheet(focusIndex >= 0, unitId, isSupportAcrossSheet, noop, noop, () => highlightSheet(refSelections)); 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 2f03951421d..253a4bc4365 100644 --- a/packages/sheets-ui/src/controllers/editor/editing.render-controller.ts +++ b/packages/sheets-ui/src/controllers/editor/editing.render-controller.ts @@ -448,7 +448,6 @@ export class EditingRenderController extends Disposable implements IRenderModule const editCellState = this._editorBridgeService.getEditCellState(); let { keycode } = param; this._setOpenForCurrent(null, null); - this._cursorChange = CursorChange.InitialState; this._exitInput(param); diff --git a/packages/sheets-ui/src/views/editor-container/EditorContainer.tsx b/packages/sheets-ui/src/views/editor-container/EditorContainer.tsx index cd817a5584a..3ff8eca63b3 100644 --- a/packages/sheets-ui/src/views/editor-container/EditorContainer.tsx +++ b/packages/sheets-ui/src/views/editor-container/EditorContainer.tsx @@ -21,6 +21,7 @@ import { ComponentManager, DISABLE_AUTO_FOCUS_KEY, KeyCode, MetaKeys, useObserva import React, { useEffect, useMemo, useRef, useState } from 'react'; import { of } from 'rxjs'; import { ExpandSelectionCommand, JumpOver, MoveSelectionCommand } from '../../commands/commands/set-selection.command'; +import { SetCellEditVisibleArrowOperation } from '../../commands/operations/cell-edit.operation'; import { EMBEDDING_FORMULA_EDITOR_COMPONENT_KEY } from '../../common/keys'; import { IEditorBridgeService } from '../../services/editor-bridge.service'; import { ICellEditorManagerService } from '../../services/editor/cell-editor-manager.service'; @@ -198,9 +199,17 @@ export const EditorContainer: React.FC = () => { direction, }); } + } else { + commandService.executeCommand(SetCellEditVisibleArrowOperation.id, { + keycode, + visible: false, + eventType: DeviceInputEventType.Keyboard, + isShift: metaKey === MetaKeys.SHIFT || metaKey === (MetaKeys.CTRL_COMMAND | MetaKeys.SHIFT), + unitId: editState?.unitId, + }); } }, - }), [commandService, editorBridgeService]); + }), [commandService, editState?.unitId, editorBridgeService]); return (
Date: Thu, 28 Nov 2024 18:53:12 +0800 Subject: [PATCH 009/134] feat: update --- .../formula-editor/hooks/useFormulaSelection.ts | 17 ++++++++++++----- .../hooks/useSheetSelectionChange.ts | 3 ++- .../src/views/formula-editor/index.tsx | 3 ++- .../commands/operations/selection.operation.ts | 1 - 4 files changed, 16 insertions(+), 8 deletions(-) diff --git a/packages/sheets-formula-ui/src/views/formula-editor/hooks/useFormulaSelection.ts b/packages/sheets-formula-ui/src/views/formula-editor/hooks/useFormulaSelection.ts index cbb7024ea3f..34564f4a8e0 100644 --- a/packages/sheets-formula-ui/src/views/formula-editor/hooks/useFormulaSelection.ts +++ b/packages/sheets-formula-ui/src/views/formula-editor/hooks/useFormulaSelection.ts @@ -15,12 +15,13 @@ */ import type { IAccessor } from '@univerjs/core'; +import type { ISequenceNode } from '@univerjs/engine-formula'; import { Injector, IUniverInstanceService, useDependency } from '@univerjs/core'; import { DocSelectionManagerService } from '@univerjs/docs'; import { DocSelectionRenderService } from '@univerjs/docs-ui'; -import { matchRefDrawToken } from '@univerjs/engine-formula'; +import { matchRefDrawToken, sequenceNodeType } from '@univerjs/engine-formula'; import { IRenderManagerService } from '@univerjs/engine-render'; -import { useEffect, useMemo, useState } from 'react'; +import { useEffect, useMemo, useRef, useState } from 'react'; import { of } from 'rxjs'; function getCurrentBodyDataStreamAndOffset(accssor: IAccessor) { @@ -56,19 +57,25 @@ function getCurrentChar(accssor: IAccessor) { return dataStream[startOffset - 1 + config.offset]; } -export function useFormulaSelecting(editorId: string) { +export function useFormulaSelecting(editorId: string, nodes: (string | ISequenceNode)[]) { const renderManagerService = useDependency(IRenderManagerService); const renderer = renderManagerService.getRenderById(editorId); const docSelectionRenderService = renderer?.with(DocSelectionRenderService); const injector = useDependency(Injector); const textSelections$ = useMemo(() => docSelectionRenderService?.textSelectionInner$ ?? of(), [docSelectionRenderService?.textSelectionInner$]); const [isSelecting, setIsSelecting] = useState(false); + const nodesRef = useRef(nodes); + nodesRef.current = nodes; useEffect(() => { const sub = textSelections$.subscribe(() => { const char = getCurrentChar(injector); + const activeRange = docSelectionRenderService?.getActiveTextRange(); + const index = activeRange?.collapsed ? activeRange.startOffset! : -1; + const lastNode = nodesRef.current[nodesRef.current.length - 1]; + const isFocusingLastNode = typeof lastNode === 'object' && lastNode.nodeType === sequenceNodeType.REFERENCE && lastNode.startIndex <= index - 1 && lastNode.endIndex >= index - 2; const dataStream = getCurrentBodyDataStreamAndOffset(injector)?.dataStream; - if (dataStream?.substring(0, 1) === '=' && char && matchRefDrawToken(char)) { + if (dataStream?.substring(0, 1) === '=' && ((char && matchRefDrawToken(char)) || isFocusingLastNode)) { setIsSelecting(true); } else { setIsSelecting(false); @@ -76,7 +83,7 @@ export function useFormulaSelecting(editorId: string) { }); return () => sub.unsubscribe(); - }, [injector, textSelections$]); + }, [docSelectionRenderService, injector, textSelections$]); return isSelecting; } diff --git a/packages/sheets-formula-ui/src/views/formula-editor/hooks/useSheetSelectionChange.ts b/packages/sheets-formula-ui/src/views/formula-editor/hooks/useSheetSelectionChange.ts index 1ae2f2a23c6..12517dcc5b3 100644 --- a/packages/sheets-formula-ui/src/views/formula-editor/hooks/useSheetSelectionChange.ts +++ b/packages/sheets-formula-ui/src/views/formula-editor/hooks/useSheetSelectionChange.ts @@ -43,7 +43,8 @@ export const useSheetSelectionChange = ( sequenceNodes: INode[], isSupportAcrossSheet: boolean, editor?: Editor, - handleRangeChange: ((refString: string, offset: number, isEnd: boolean) => void) = noop) => { + handleRangeChange: ((refString: string, offset: number, isEnd: boolean) => void) = noop +) => { const renderManagerService = useDependency(IRenderManagerService); const univerInstanceService = useDependency(IUniverInstanceService); diff --git a/packages/sheets-formula-ui/src/views/formula-editor/index.tsx b/packages/sheets-formula-ui/src/views/formula-editor/index.tsx index 021051bc1dc..908304a30f0 100644 --- a/packages/sheets-formula-ui/src/views/formula-editor/index.tsx +++ b/packages/sheets-formula-ui/src/views/formula-editor/index.tsx @@ -116,9 +116,10 @@ export function FormulaEditor(props: IFormulaEditorProps) { const formulaEditorContainerRef = useRef(null); const editorId = useMemo(() => propEditorId ?? createInternalEditorID(`${EMBEDDING_FORMULA_EDITOR}-${generateRandomId(4)}`), []); const isError = useMemo(() => errorText !== undefined, [errorText]); - const isSelecting = useFormulaSelecting(editorId); + const getFormulaToken = useFormulaToken(); const sequenceNodes = useMemo(() => getFormulaToken(formulaWithoutEqualSymbol), [formulaWithoutEqualSymbol]); + const isSelecting = useFormulaSelecting(editorId, sequenceNodes); const needEmit = useEmitChange(sequenceNodes, (text: string) => { onChange(`=${text}`); diff --git a/packages/sheets/src/commands/operations/selection.operation.ts b/packages/sheets/src/commands/operations/selection.operation.ts index 603067d4a56..b97eac85198 100644 --- a/packages/sheets/src/commands/operations/selection.operation.ts +++ b/packages/sheets/src/commands/operations/selection.operation.ts @@ -38,7 +38,6 @@ export const SetSelectionsOperation: IOperation = type: CommandType.OPERATION, handler: (accessor, params) => { if (!params) return false; - const { selections, type, unitId, subUnitId } = params; const selectionManagerService = getSelectionsService(accessor); From 2f3d44ff5b92a1121e4c41a229645acf0bf11f8c Mon Sep 17 00:00:00 2001 From: zhangw Date: Thu, 28 Nov 2024 21:58:17 +0800 Subject: [PATCH 010/134] feat: update --- .../hooks/useFormulaSelection.ts | 16 ++++++-- .../src/views/formula-editor/index.tsx | 5 ++- .../src/services/editor-bridge.service.ts | 9 ----- .../editor-container/EditorContainer.tsx | 37 ++++--------------- 4 files changed, 23 insertions(+), 44 deletions(-) diff --git a/packages/sheets-formula-ui/src/views/formula-editor/hooks/useFormulaSelection.ts b/packages/sheets-formula-ui/src/views/formula-editor/hooks/useFormulaSelection.ts index 34564f4a8e0..b6d836845fe 100644 --- a/packages/sheets-formula-ui/src/views/formula-editor/hooks/useFormulaSelection.ts +++ b/packages/sheets-formula-ui/src/views/formula-editor/hooks/useFormulaSelection.ts @@ -57,13 +57,19 @@ function getCurrentChar(accssor: IAccessor) { return dataStream[startOffset - 1 + config.offset]; } +export enum FormulaSelectingType { + NOT_SELECT = 0, + NEED_ADD = 1, + CAN_EDIT = 2, +} + export function useFormulaSelecting(editorId: string, nodes: (string | ISequenceNode)[]) { const renderManagerService = useDependency(IRenderManagerService); const renderer = renderManagerService.getRenderById(editorId); const docSelectionRenderService = renderer?.with(DocSelectionRenderService); const injector = useDependency(Injector); const textSelections$ = useMemo(() => docSelectionRenderService?.textSelectionInner$ ?? of(), [docSelectionRenderService?.textSelectionInner$]); - const [isSelecting, setIsSelecting] = useState(false); + const [isSelecting, setIsSelecting] = useState(FormulaSelectingType.NOT_SELECT); const nodesRef = useRef(nodes); nodesRef.current = nodes; @@ -76,9 +82,13 @@ export function useFormulaSelecting(editorId: string, nodes: (string | ISequence const isFocusingLastNode = typeof lastNode === 'object' && lastNode.nodeType === sequenceNodeType.REFERENCE && lastNode.startIndex <= index - 1 && lastNode.endIndex >= index - 2; const dataStream = getCurrentBodyDataStreamAndOffset(injector)?.dataStream; if (dataStream?.substring(0, 1) === '=' && ((char && matchRefDrawToken(char)) || isFocusingLastNode)) { - setIsSelecting(true); + if (isFocusingLastNode) { + setIsSelecting(FormulaSelectingType.CAN_EDIT); + } else { + setIsSelecting(FormulaSelectingType.NEED_ADD); + } } else { - setIsSelecting(false); + setIsSelecting(FormulaSelectingType.NOT_SELECT); } }); diff --git a/packages/sheets-formula-ui/src/views/formula-editor/index.tsx b/packages/sheets-formula-ui/src/views/formula-editor/index.tsx index 908304a30f0..071c7b870f8 100644 --- a/packages/sheets-formula-ui/src/views/formula-editor/index.tsx +++ b/packages/sheets-formula-ui/src/views/formula-editor/index.tsx @@ -18,6 +18,7 @@ import type { IDisposable } from '@univerjs/core'; import type { Editor } from '@univerjs/docs-ui'; import type { ReactNode } from 'react'; import type { IKeyboardEventConfig } from '../range-selector/hooks/useKeyboardEvent'; +import type { FormulaSelectingType } from './hooks/useFormulaSelection'; import { createInternalEditorID, generateRandomId, useDependency } from '@univerjs/core'; import { DocBackScrollRenderController, IEditorService } from '@univerjs/docs-ui'; import { operatorToken } from '@univerjs/engine-formula'; @@ -64,7 +65,7 @@ export interface IFormulaEditorProps { className?: string; editorId?: string; moveCursor?: boolean; - onFormulaSelectingChange?: (isSelecting: boolean) => void; + onFormulaSelectingChange?: (isSelecting: FormulaSelectingType) => void; keyboradEventConfig?: IKeyboardEventConfig; } const noop = () => { }; @@ -165,7 +166,7 @@ export function FormulaEditor(props: IFormulaEditorProps) { }, [_isFocus, focus]); const { checkScrollBar } = useResize(editor); - useRefactorEffect(isFocus, isSelecting, unitId); + useRefactorEffect(isFocus, Boolean(isSelecting), unitId); useLeftAndRightArrow(isFocus && moveCursor, editor); const handleSelectionChange = (refString: string, offset: number, isEnd: boolean) => { diff --git a/packages/sheets-ui/src/services/editor-bridge.service.ts b/packages/sheets-ui/src/services/editor-bridge.service.ts index 0dc96fa77fb..6d9cb42ef99 100644 --- a/packages/sheets-ui/src/services/editor-bridge.service.ts +++ b/packages/sheets-ui/src/services/editor-bridge.service.ts @@ -416,15 +416,6 @@ export class EditorBridgeService extends Disposable implements IEditorBridgeServ } changeVisible(param: IEditorBridgeServiceVisibleParam) { - /** - * Non-sheetEditor and formula selection mode, - * double-clicking cannot activate the sheet editor. - */ - const editor = this._editorService.getFocusEditor(); - if (this._refSelectionsService.getCurrentSelections().length > 0 && editor && !editor.isSheetEditor()) { - return; - } - this._visible = param; // Reset the dirty status when the editor is visible. diff --git a/packages/sheets-ui/src/views/editor-container/EditorContainer.tsx b/packages/sheets-ui/src/views/editor-container/EditorContainer.tsx index 3ff8eca63b3..ebf32306e54 100644 --- a/packages/sheets-ui/src/views/editor-container/EditorContainer.tsx +++ b/packages/sheets-ui/src/views/editor-container/EditorContainer.tsx @@ -15,11 +15,10 @@ */ import { Direction, DOCS_NORMAL_EDITOR_UNIT_ID_KEY, ICommandService, IContextService, useDependency } from '@univerjs/core'; -import { DocSelectionRenderService, IEditorService } from '@univerjs/docs-ui'; -import { DeviceInputEventType, FIX_ONE_PIXEL_BLUR_OFFSET, IRenderManagerService } from '@univerjs/engine-render'; +import { IEditorService } from '@univerjs/docs-ui'; +import { DeviceInputEventType, FIX_ONE_PIXEL_BLUR_OFFSET } from '@univerjs/engine-render'; import { ComponentManager, DISABLE_AUTO_FOCUS_KEY, KeyCode, MetaKeys, useObservable } from '@univerjs/ui'; import React, { useEffect, useMemo, useRef, useState } from 'react'; -import { of } from 'rxjs'; import { ExpandSelectionCommand, JumpOver, MoveSelectionCommand } from '../../commands/commands/set-selection.command'; import { SetCellEditVisibleArrowOperation } from '../../commands/operations/cell-edit.operation'; import { EMBEDDING_FORMULA_EDITOR_COMPONENT_KEY } from '../../common/keys'; @@ -50,17 +49,10 @@ export const EditorContainer: React.FC = () => { const editorService = useDependency(IEditorService); const contextService = useDependency(IContextService); const componentManager = useDependency(ComponentManager); - const renderManagerService = useDependency(IRenderManagerService); const editorBridgeService = useDependency(IEditorBridgeService); - const renderer = renderManagerService.getRenderById(DOCS_NORMAL_EDITOR_UNIT_ID_KEY); - const docSelectionRenderService = renderer?.with(DocSelectionRenderService); - const [selectionFocusing, setSelectionFocusing] = useState(false); const visible = useObservable(editorBridgeService.visible$); - const onFocus$ = useMemo(() => docSelectionRenderService?.onFocus$ ?? of(), [docSelectionRenderService?.onFocus$]); - const onBlur$ = useMemo(() => docSelectionRenderService?.onBlur$ ?? of(), [docSelectionRenderService?.onBlur$]); - const forceKeepVisible = useObservable(editorBridgeService.forceKeepVisible$); const commandService = useDependency(ICommandService); - const isRefSelecting = useRef(false); + const isRefSelecting = useRef<0 | 1 | 2>(0); const disableAutoFocus = useObservable( () => contextService.subscribeContextValue$(DISABLE_AUTO_FOCUS_KEY), false, @@ -120,21 +112,6 @@ export const EditorContainer: React.FC = () => { // eslint-disable-next-line react-hooks/exhaustive-deps }, [disableAutoFocus, state]); - useEffect(() => { - const sub1 = onFocus$.subscribe(() => { - setSelectionFocusing(true); - }); - - const sub2 = onBlur$.subscribe(() => { - setSelectionFocusing(false); - }); - - return () => { - sub1.unsubscribe(); - sub2.unsubscribe(); - }; - }, [onBlur$, onFocus$]); - const keyCodeConfig = useMemo(() => ({ keyCodes: [ { keyCode: KeyCode.ENTER }, @@ -163,7 +140,7 @@ export const EditorContainer: React.FC = () => { visible: false, eventType: DeviceInputEventType.Keyboard, keycode, - unitId: '', + unitId: editState!.unitId!, }); return; } @@ -209,7 +186,7 @@ export const EditorContainer: React.FC = () => { }); } }, - }), [commandService, editState?.unitId, editorBridgeService]); + }), [commandService, editState, editorBridgeService]); return (
= () => { subUnitId={editState?.sheetId} moveCursor={false} keyboradEventConfig={keyCodeConfig} - onFormulaSelectingChange={(isSelecting: boolean) => { + onFormulaSelectingChange={(isSelecting: 0 | 1 | 2) => { isRefSelecting.current = isSelecting; - if (isSelecting) { + if (isSelecting === 1) { editorBridgeService.enableForceKeepVisible(); } else { editorBridgeService.disableForceKeepVisible(); From dcc3def60affee63a1ac598f112e8aeb0cee05fd Mon Sep 17 00:00:00 2001 From: zhangw Date: Fri, 29 Nov 2024 00:53:30 +0800 Subject: [PATCH 011/134] feat: update --- .../docs/data-model/document-data-model.ts | 17 ++++++----- .../src/views/formula-editor/index.tsx | 27 ++++++----------- .../range-selector/hooks/useFormulaToken.ts | 3 +- .../editor/editing.render-controller.ts | 1 - .../src/views/formula-bar/FormulaBar.tsx | 29 ++++++++++--------- 5 files changed, 36 insertions(+), 41 deletions(-) diff --git a/packages/core/src/docs/data-model/document-data-model.ts b/packages/core/src/docs/data-model/document-data-model.ts index c1673d9f09b..b463640aeae 100644 --- a/packages/core/src/docs/data-model/document-data-model.ts +++ b/packages/core/src/docs/data-model/document-data-model.ts @@ -14,13 +14,6 @@ * limitations under the License. */ -import { BehaviorSubject } from 'rxjs'; -import { UnitModel, UniverInstanceType } from '../../common/unit'; -import { Tools } from '../../shared/tools'; -import { getEmptySnapshot } from './empty-snapshot'; -import { JSONX } from './json-x/json-x'; -import { PRESET_LIST_TYPE } from './preset-list-type'; -import { getBodySlice, SliceBodyType } from './text-x/utils'; import type { Nullable } from '../../shared'; import type { IDocumentBody, @@ -32,6 +25,13 @@ import type { } from '../../types/interfaces/i-document-data'; import type { IPaddingData } from '../../types/interfaces/i-style-data'; import type { JSONXActions } from './json-x/json-x'; +import { BehaviorSubject, Subject } from 'rxjs'; +import { UnitModel, UniverInstanceType } from '../../common/unit'; +import { Tools } from '../../shared/tools'; +import { getEmptySnapshot } from './empty-snapshot'; +import { JSONX } from './json-x/json-x'; +import { PRESET_LIST_TYPE } from './preset-list-type'; +import { getBodySlice, SliceBodyType } from './text-x/utils'; export const DEFAULT_DOC = { id: 'default_doc', @@ -228,6 +228,7 @@ export class DocumentDataModel extends DocumentDataModelSimple { headerModelMap: Map = new Map(); footerModelMap: Map = new Map(); + change$ = new Subject(); constructor(snapshot: Partial) { super(Tools.isEmptyObject(snapshot) ? getEmptySnapshot() : snapshot); @@ -281,6 +282,7 @@ export class DocumentDataModel extends DocumentDataModelSimple { this.snapshot = { ...DEFAULT_DOC, ...snapshot }; this._initializeHeaderFooterModel(); + this.change$.next(Date.now()); } getSelfOrHeaderFooterModel(segmentId?: string) { @@ -315,6 +317,7 @@ export class DocumentDataModel extends DocumentDataModelSimple { this._initializeHeaderFooterModel(); } + this.change$.next(Date.now()); return this.snapshot; } diff --git a/packages/sheets-formula-ui/src/views/formula-editor/index.tsx b/packages/sheets-formula-ui/src/views/formula-editor/index.tsx index 071c7b870f8..f00bae70231 100644 --- a/packages/sheets-formula-ui/src/views/formula-editor/index.tsx +++ b/packages/sheets-formula-ui/src/views/formula-editor/index.tsx @@ -14,14 +14,13 @@ * limitations under the License. */ -import type { IDisposable } from '@univerjs/core'; +import type { DocumentDataModel, IDisposable } from '@univerjs/core'; import type { Editor } from '@univerjs/docs-ui'; import type { ReactNode } from 'react'; import type { IKeyboardEventConfig } from '../range-selector/hooks/useKeyboardEvent'; import type { FormulaSelectingType } from './hooks/useFormulaSelection'; -import { createInternalEditorID, generateRandomId, useDependency } from '@univerjs/core'; +import { BuildTextUtils, createInternalEditorID, generateRandomId, IUniverInstanceService, useDependency, useObservable } from '@univerjs/core'; import { DocBackScrollRenderController, IEditorService } from '@univerjs/docs-ui'; -import { operatorToken } from '@univerjs/engine-formula'; import { EMBEDDING_FORMULA_EDITOR } from '@univerjs/sheets-ui'; import { useEvent } from '@univerjs/ui'; import clsx from 'clsx'; @@ -91,12 +90,7 @@ export function FormulaEditor(props: IFormulaEditorProps) { const editorService = useDependency(IEditorService); const sheetEmbeddingRef = useRef(null); - const [formulaText, formulaTextSet] = useState(() => { - if (initValue.startsWith(operatorToken.EQUALS)) { - return initValue; - } - return ''; - }); + // init actions if (actions) { actions.handleOutClick = (e: MouseEvent, cb: () => void) => { @@ -107,9 +101,6 @@ export function FormulaEditor(props: IFormulaEditorProps) { }; } - const formulaWithoutEqualSymbol = useMemo(() => { - return getFormulaText(formulaText); - }, [formulaText]); const onFormulaSelectingChange = useEvent(propOnFormulaSelectingChange); const searchFunctionRef = useRef(null); const [editor, editorSet] = useState(); @@ -117,9 +108,13 @@ export function FormulaEditor(props: IFormulaEditorProps) { const formulaEditorContainerRef = useRef(null); const editorId = useMemo(() => propEditorId ?? createInternalEditorID(`${EMBEDDING_FORMULA_EDITOR}-${generateRandomId(4)}`), []); const isError = useMemo(() => errorText !== undefined, [errorText]); - + const univerInstanceService = useDependency(IUniverInstanceService); + const document = univerInstanceService.getUnit(editorId); + useObservable(document?.change$); const getFormulaToken = useFormulaToken(); - const sequenceNodes = useMemo(() => getFormulaToken(formulaWithoutEqualSymbol), [formulaWithoutEqualSymbol]); + const formulaText = BuildTextUtils.transform.getPlainText(document?.getBody()?.dataStream ?? ''); + const formulaWithoutEqualSymbol = useMemo(() => getFormulaText(formulaText), [formulaText]); + const sequenceNodes = useMemo(() => getFormulaToken(formulaWithoutEqualSymbol), [formulaWithoutEqualSymbol, getFormulaToken]); const isSelecting = useFormulaSelecting(editorId, sequenceNodes); const needEmit = useEmitChange(sequenceNodes, (text: string) => { @@ -170,9 +165,7 @@ export function FormulaEditor(props: IFormulaEditorProps) { useLeftAndRightArrow(isFocus && moveCursor, editor); const handleSelectionChange = (refString: string, offset: number, isEnd: boolean) => { - const result = `=${refString}`; needEmit(); - formulaTextSet(result); highligh(refString); if (isEnd) { focus(); @@ -201,7 +194,6 @@ export function FormulaEditor(props: IFormulaEditorProps) { const d = editor.input$.subscribe((e) => { const text = (e.data.body?.dataStream ?? '').replaceAll(/\n|\r/g, ''); needEmit(); - formulaTextSet(text); highligh(getFormulaText(text), false); }); return () => { @@ -237,7 +229,6 @@ export function FormulaEditor(props: IFormulaEditorProps) { const handleFunctionSelect = (v: string) => { const res = handlerFormulaReplace(v); if (res) { - formulaTextSet(`=${res.text}`); const selections = editor?.getSelectionRanges(); if (selections && selections.length === 1) { const range = selections[0]; diff --git a/packages/sheets-formula-ui/src/views/range-selector/hooks/useFormulaToken.ts b/packages/sheets-formula-ui/src/views/range-selector/hooks/useFormulaToken.ts index 6d90ce9e0b8..253fdad8073 100644 --- a/packages/sheets-formula-ui/src/views/range-selector/hooks/useFormulaToken.ts +++ b/packages/sheets-formula-ui/src/views/range-selector/hooks/useFormulaToken.ts @@ -17,10 +17,11 @@ import type { ISequenceNode } from '@univerjs/engine-formula'; import { useDependency } from '@univerjs/core'; import { LexerTreeBuilder } from '@univerjs/engine-formula'; +import { useCallback } from 'react'; export type INode = (string | ISequenceNode); export const useFormulaToken = () => { const lexerTreeBuilder = useDependency(LexerTreeBuilder); - const getFormulaToken = (text: string) => lexerTreeBuilder.sequenceNodesBuilder(text) || []; + const getFormulaToken = useCallback((text: string) => lexerTreeBuilder.sequenceNodesBuilder(text) || [], [lexerTreeBuilder]); return getFormulaToken; }; 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 253a4bc4365..aba83ca71dc 100644 --- a/packages/sheets-ui/src/controllers/editor/editing.render-controller.ts +++ b/packages/sheets-ui/src/controllers/editor/editing.render-controller.ts @@ -380,7 +380,6 @@ export class EditingRenderController extends Disposable implements IRenderModule sheetId, isInArrayFormulaRange = false, } = editCellState; - const editorObject = this._getEditorObject(); if (editorObject == null) { diff --git a/packages/sheets-ui/src/views/formula-bar/FormulaBar.tsx b/packages/sheets-ui/src/views/formula-bar/FormulaBar.tsx index 8ca6761ec15..0a069d5e4ba 100644 --- a/packages/sheets-ui/src/views/formula-bar/FormulaBar.tsx +++ b/packages/sheets-ui/src/views/formula-bar/FormulaBar.tsx @@ -239,21 +239,22 @@ export function FormulaBar() {
{FormulaEditor && ( - { + null + // { - }} - isFocus={false} - // onFocus={() => setIsFocus(true)} - // onBlur={() => setIsFocus(false)} - /> + // }} + // isFocus={false} + // // onFocus={() => setIsFocus(true)} + // // onBlur={() => setIsFocus(false)} + // /> )}
{arrowDirection === ArrowDirection.Down From 8ea49fad88fe5ddadc7ec2aa64ec09b8f6204ed5 Mon Sep 17 00:00:00 2001 From: zhangw Date: Fri, 29 Nov 2024 16:21:51 +0800 Subject: [PATCH 012/134] feat: update --- .../editor/data-sync.controller.ts | 5 +- .../editor-container/EditorContainer.tsx | 88 +------------- .../src/views/editor-container/hooks.ts | 107 ++++++++++++++++++ .../src/views/formula-bar/FormulaBar.tsx | 89 +++++---------- 4 files changed, 145 insertions(+), 144 deletions(-) create mode 100644 packages/sheets-ui/src/views/editor-container/hooks.ts diff --git a/packages/sheets-ui/src/controllers/editor/data-sync.controller.ts b/packages/sheets-ui/src/controllers/editor/data-sync.controller.ts index abdbe6abfd3..2e97cb2b14b 100644 --- a/packages/sheets-ui/src/controllers/editor/data-sync.controller.ts +++ b/packages/sheets-ui/src/controllers/editor/data-sync.controller.ts @@ -45,8 +45,8 @@ export class EditorDataSyncController extends Disposable { } private _initialize() { - // this._syncFormulaEditorContent(); - // this._commandExecutedListener(); + this._syncFormulaEditorContent(); + this._commandExecutedListener(); } private _getEditorViewModel(unitId: string): Nullable { @@ -105,6 +105,7 @@ export class EditorDataSyncController extends Disposable { if (params.isSync) { return; } + if (INCLUDE_LIST.includes(unitId)) { // sync cell content to formula editor bar when edit cell editor and vice verse. const editorDocDataModel = this._univerInstanceService.getUnit(unitId, UniverInstanceType.UNIVER_DOC); diff --git a/packages/sheets-ui/src/views/editor-container/EditorContainer.tsx b/packages/sheets-ui/src/views/editor-container/EditorContainer.tsx index ebf32306e54..631268bf840 100644 --- a/packages/sheets-ui/src/views/editor-container/EditorContainer.tsx +++ b/packages/sheets-ui/src/views/editor-container/EditorContainer.tsx @@ -14,16 +14,15 @@ * limitations under the License. */ -import { Direction, DOCS_NORMAL_EDITOR_UNIT_ID_KEY, ICommandService, IContextService, useDependency } from '@univerjs/core'; +import { DOCS_NORMAL_EDITOR_UNIT_ID_KEY, IContextService, useDependency } from '@univerjs/core'; import { IEditorService } from '@univerjs/docs-ui'; -import { DeviceInputEventType, FIX_ONE_PIXEL_BLUR_OFFSET } from '@univerjs/engine-render'; -import { ComponentManager, DISABLE_AUTO_FOCUS_KEY, KeyCode, MetaKeys, useObservable } from '@univerjs/ui'; -import React, { useEffect, useMemo, useRef, useState } from 'react'; -import { ExpandSelectionCommand, JumpOver, MoveSelectionCommand } from '../../commands/commands/set-selection.command'; -import { SetCellEditVisibleArrowOperation } from '../../commands/operations/cell-edit.operation'; +import { FIX_ONE_PIXEL_BLUR_OFFSET } from '@univerjs/engine-render'; +import { ComponentManager, DISABLE_AUTO_FOCUS_KEY, useObservable } from '@univerjs/ui'; +import React, { useEffect, useRef, useState } from 'react'; import { EMBEDDING_FORMULA_EDITOR_COMPONENT_KEY } from '../../common/keys'; import { IEditorBridgeService } from '../../services/editor-bridge.service'; import { ICellEditorManagerService } from '../../services/editor/cell-editor-manager.service'; +import { useKeyEventConfig } from './hooks'; import styles from './index.module.less'; interface ICellIEditorProps { } @@ -51,7 +50,6 @@ export const EditorContainer: React.FC = () => { const componentManager = useDependency(ComponentManager); const editorBridgeService = useDependency(IEditorBridgeService); const visible = useObservable(editorBridgeService.visible$); - const commandService = useDependency(ICommandService); const isRefSelecting = useRef<0 | 1 | 2>(0); const disableAutoFocus = useObservable( () => contextService.subscribeContextValue$(DISABLE_AUTO_FOCUS_KEY), @@ -112,81 +110,7 @@ export const EditorContainer: React.FC = () => { // eslint-disable-next-line react-hooks/exhaustive-deps }, [disableAutoFocus, state]); - const keyCodeConfig = useMemo(() => ({ - keyCodes: [ - { keyCode: KeyCode.ENTER }, - { keyCode: KeyCode.ESC }, - { keyCode: KeyCode.TAB }, - { keyCode: KeyCode.ARROW_DOWN }, - { keyCode: KeyCode.ARROW_LEFT }, - { keyCode: KeyCode.ARROW_RIGHT }, - { keyCode: KeyCode.ARROW_UP }, - { keyCode: KeyCode.ARROW_DOWN, metaKey: MetaKeys.SHIFT }, - { keyCode: KeyCode.ARROW_LEFT, metaKey: MetaKeys.SHIFT }, - { keyCode: KeyCode.ARROW_RIGHT, metaKey: MetaKeys.SHIFT }, - { keyCode: KeyCode.ARROW_UP, metaKey: MetaKeys.SHIFT }, - { keyCode: KeyCode.ARROW_DOWN, metaKey: MetaKeys.CTRL_COMMAND }, - { keyCode: KeyCode.ARROW_LEFT, metaKey: MetaKeys.CTRL_COMMAND }, - { keyCode: KeyCode.ARROW_RIGHT, metaKey: MetaKeys.CTRL_COMMAND }, - { keyCode: KeyCode.ARROW_UP, metaKey: MetaKeys.CTRL_COMMAND }, - { keyCode: KeyCode.ARROW_DOWN, metaKey: MetaKeys.CTRL_COMMAND | MetaKeys.SHIFT }, - { keyCode: KeyCode.ARROW_LEFT, metaKey: MetaKeys.CTRL_COMMAND | MetaKeys.SHIFT }, - { keyCode: KeyCode.ARROW_RIGHT, metaKey: MetaKeys.CTRL_COMMAND | MetaKeys.SHIFT }, - { keyCode: KeyCode.ARROW_UP, metaKey: MetaKeys.CTRL_COMMAND | MetaKeys.SHIFT }, - ], - handler: (keycode: KeyCode, metaKey?: MetaKeys) => { - if (keycode === KeyCode.ENTER || keycode === KeyCode.ESC || keycode === KeyCode.TAB) { - editorBridgeService.changeVisible({ - visible: false, - eventType: DeviceInputEventType.Keyboard, - keycode, - unitId: editState!.unitId!, - }); - return; - } - - let direction = Direction.DOWN; - if (keycode === KeyCode.ARROW_DOWN) { - direction = Direction.DOWN; - } else if (keycode === KeyCode.ARROW_UP) { - direction = Direction.UP; - } else if (keycode === KeyCode.ARROW_LEFT) { - direction = Direction.LEFT; - } else if (keycode === KeyCode.ARROW_RIGHT) { - direction = Direction.RIGHT; - } - - if (isRefSelecting.current) { - if (metaKey === MetaKeys.CTRL_COMMAND) { - commandService.executeCommand(MoveSelectionCommand.id, { - direction, - jumpOver: JumpOver.moveGap, - }); - } else if (metaKey === MetaKeys.SHIFT) { - commandService.executeCommand(ExpandSelectionCommand.id, { - direction, - }); - } else if (metaKey === (MetaKeys.CTRL_COMMAND | MetaKeys.SHIFT)) { - commandService.executeCommand(ExpandSelectionCommand.id, { - direction, - jumpOver: JumpOver.moveGap, - }); - } else { - commandService.executeCommand(MoveSelectionCommand.id, { - direction, - }); - } - } else { - commandService.executeCommand(SetCellEditVisibleArrowOperation.id, { - keycode, - visible: false, - eventType: DeviceInputEventType.Keyboard, - isShift: metaKey === MetaKeys.SHIFT || metaKey === (MetaKeys.CTRL_COMMAND | MetaKeys.SHIFT), - unitId: editState?.unitId, - }); - } - }, - }), [commandService, editState, editorBridgeService]); + const keyCodeConfig = useKeyEventConfig(isRefSelecting, editState?.unitId!); return (
, unitId: string) { + const commandService = useDependency(ICommandService); + const editorBridgeService = useDependency(IEditorBridgeService); + + const keyCodeConfig = useMemo(() => ({ + keyCodes: [ + { keyCode: KeyCode.ENTER }, + { keyCode: KeyCode.ESC }, + { keyCode: KeyCode.TAB }, + { keyCode: KeyCode.ARROW_DOWN }, + { keyCode: KeyCode.ARROW_LEFT }, + { keyCode: KeyCode.ARROW_RIGHT }, + { keyCode: KeyCode.ARROW_UP }, + { keyCode: KeyCode.ARROW_DOWN, metaKey: MetaKeys.SHIFT }, + { keyCode: KeyCode.ARROW_LEFT, metaKey: MetaKeys.SHIFT }, + { keyCode: KeyCode.ARROW_RIGHT, metaKey: MetaKeys.SHIFT }, + { keyCode: KeyCode.ARROW_UP, metaKey: MetaKeys.SHIFT }, + { keyCode: KeyCode.ARROW_DOWN, metaKey: MetaKeys.CTRL_COMMAND }, + { keyCode: KeyCode.ARROW_LEFT, metaKey: MetaKeys.CTRL_COMMAND }, + { keyCode: KeyCode.ARROW_RIGHT, metaKey: MetaKeys.CTRL_COMMAND }, + { keyCode: KeyCode.ARROW_UP, metaKey: MetaKeys.CTRL_COMMAND }, + { keyCode: KeyCode.ARROW_DOWN, metaKey: MetaKeys.CTRL_COMMAND | MetaKeys.SHIFT }, + { keyCode: KeyCode.ARROW_LEFT, metaKey: MetaKeys.CTRL_COMMAND | MetaKeys.SHIFT }, + { keyCode: KeyCode.ARROW_RIGHT, metaKey: MetaKeys.CTRL_COMMAND | MetaKeys.SHIFT }, + { keyCode: KeyCode.ARROW_UP, metaKey: MetaKeys.CTRL_COMMAND | MetaKeys.SHIFT }, + ], + handler: (keycode: KeyCode, metaKey?: MetaKeys) => { + if (keycode === KeyCode.ENTER || keycode === KeyCode.ESC || keycode === KeyCode.TAB) { + editorBridgeService.changeVisible({ + visible: false, + eventType: DeviceInputEventType.Keyboard, + keycode, + unitId: unitId!, + }); + return; + } + + let direction = Direction.DOWN; + if (keycode === KeyCode.ARROW_DOWN) { + direction = Direction.DOWN; + } else if (keycode === KeyCode.ARROW_UP) { + direction = Direction.UP; + } else if (keycode === KeyCode.ARROW_LEFT) { + direction = Direction.LEFT; + } else if (keycode === KeyCode.ARROW_RIGHT) { + direction = Direction.RIGHT; + } + + if (isRefSelecting.current) { + if (metaKey === MetaKeys.CTRL_COMMAND) { + commandService.executeCommand(MoveSelectionCommand.id, { + direction, + jumpOver: JumpOver.moveGap, + }); + } else if (metaKey === MetaKeys.SHIFT) { + commandService.executeCommand(ExpandSelectionCommand.id, { + direction, + }); + } else if (metaKey === (MetaKeys.CTRL_COMMAND | MetaKeys.SHIFT)) { + commandService.executeCommand(ExpandSelectionCommand.id, { + direction, + jumpOver: JumpOver.moveGap, + }); + } else { + commandService.executeCommand(MoveSelectionCommand.id, { + direction, + }); + } + } else { + commandService.executeCommand(SetCellEditVisibleArrowOperation.id, { + keycode, + visible: false, + eventType: DeviceInputEventType.Keyboard, + isShift: metaKey === MetaKeys.SHIFT || metaKey === (MetaKeys.CTRL_COMMAND | MetaKeys.SHIFT), + unitId, + }); + } + }, + }), [isRefSelecting, editorBridgeService, unitId, commandService]); + + return keyCodeConfig; +} diff --git a/packages/sheets-ui/src/views/formula-bar/FormulaBar.tsx b/packages/sheets-ui/src/views/formula-bar/FormulaBar.tsx index 0a069d5e4ba..cc5cd46a70f 100644 --- a/packages/sheets-ui/src/views/formula-bar/FormulaBar.tsx +++ b/packages/sheets-ui/src/views/formula-bar/FormulaBar.tsx @@ -14,14 +14,14 @@ * limitations under the License. */ -import type { IDocumentData, Nullable, Workbook } from '@univerjs/core'; -import { BooleanNumber, DEFAULT_EMPTY_DOCUMENT_VALUE, DOCS_FORMULA_BAR_EDITOR_UNIT_ID_KEY, DocumentFlavor, HorizontalAlign, IPermissionService, IUniverInstanceService, Rectangle, UniverInstanceType, useDependency, useObservable, VerticalAlign, WrapStrategy } from '@univerjs/core'; +import type { Nullable, Workbook } from '@univerjs/core'; +import { DOCS_FORMULA_BAR_EDITOR_UNIT_ID_KEY, FOCUSING_FX_BAR_EDITOR, IContextService, IPermissionService, IUniverInstanceService, Rectangle, UniverInstanceType, useDependency, useObservable } from '@univerjs/core'; import { DeviceInputEventType } from '@univerjs/engine-render'; import { CheckMarkSingle, CloseSingle, DropdownSingle, FxSingle } from '@univerjs/icons'; import { RangeProtectionPermissionEditPoint, RangeProtectionRuleModel, SheetsSelectionsService, WorkbookEditablePermission, WorksheetEditPermission, WorksheetProtectionRuleModel, WorksheetSetCellValuePermission } from '@univerjs/sheets'; import { ComponentContainer, ComponentManager, KeyCode, useComponentsOfPart } from '@univerjs/ui'; import clsx from 'clsx'; -import React, { useEffect, useLayoutEffect, useState } from 'react'; +import React, { useEffect, useLayoutEffect, useRef, useState } from 'react'; import { EMPTY, merge, switchMap } from 'rxjs'; import { EMBEDDING_FORMULA_EDITOR_COMPONENT_KEY } from '../../common/keys'; import { useActiveWorkbook } from '../../components/hook'; @@ -29,6 +29,7 @@ import { SheetsUIPart } from '../../consts/ui-name'; import { IEditorBridgeService } from '../../services/editor-bridge.service'; import { IFormulaEditorManagerService } from '../../services/editor/formula-editor-manager.service'; import { DefinedName } from '../defined-name/DefinedName'; +import { useKeyEventConfig } from '../editor-container/hooks'; import styles from './index.module.less'; enum ArrowDirection { @@ -47,15 +48,17 @@ export function FormulaBar() { const univerInstanceService = useDependency(IUniverInstanceService); const selectionManager = useDependency(SheetsSelectionsService); const permissionService = useDependency(IPermissionService); - const [isFocus, setIsFocus] = useState(false); - const [disable, setDisable] = useState(false); const currentWorkbook = useActiveWorkbook(); const componentManager = useDependency(ComponentManager); const workbook = useObservable(() => univerInstanceService.getCurrentTypeOfUnit$(UniverInstanceType.UNIVER_SHEET), undefined, undefined, [])!; - + const isRefSelecting = useRef<0 | 1 | 2>(0); + const editState = editorBridgeService.getEditLocation(); + const keyCodeConfig = useKeyEventConfig(isRefSelecting, editState?.unitId ?? ''); const FormulaEditor = componentManager.get(EMBEDDING_FORMULA_EDITOR_COMPONENT_KEY); const formulaAuxUIParts = useComponentsOfPart(SheetsUIPart.FORMULA_AUX); + const contextService = useDependency(IContextService); + const isFocusFxBar = contextService.getContextValue(FOCUSING_FX_BAR_EDITOR); function getPermissionIds(unitId: string, subUnitId: string): string[] { return [ @@ -108,44 +111,6 @@ export function FormulaBar() { }; }, [workbook]); - const INITIAL_SNAPSHOT: IDocumentData = { - id: DOCS_FORMULA_BAR_EDITOR_UNIT_ID_KEY, - body: { - dataStream: `${DEFAULT_EMPTY_DOCUMENT_VALUE}`, - textRuns: [], - tables: [], - paragraphs: [ - { - startIndex: 0, - }, - ], - sectionBreaks: [{ - startIndex: 1, - }], - }, - tableSource: {}, - documentStyle: { - pageSize: { - width: Number.POSITIVE_INFINITY, - height: Number.POSITIVE_INFINITY, - }, - documentFlavor: DocumentFlavor.UNSPECIFIED, - marginTop: 5, - marginBottom: 5, - marginRight: 0, - marginLeft: 0, - paragraphLineGapDefault: 0, - renderConfig: { - horizontalAlign: HorizontalAlign.UNSPECIFIED, - verticalAlign: VerticalAlign.TOP, - centerAngle: 0, - vertexAngle: 0, - wrapStrategy: WrapStrategy.WRAP, - isRenderStyle: BooleanNumber.FALSE, - }, - }, - }; - useEffect(() => { const subscription = editorBridgeService.visible$.subscribe((visibleInfo) => { setIconStyle(visibleInfo.visible ? styles.formulaActive : styles.formulaGrey); @@ -239,22 +204,26 @@ export function FormulaBar() {
{FormulaEditor && ( - null - // { - - // }} - // isFocus={false} - // // onFocus={() => setIsFocus(true)} - // // onBlur={() => setIsFocus(false)} - // /> + {}} + isFocus={isFocusFxBar} + className={styles.formulaContent} + unitId={editState?.unitId} + subUnitId={editState?.sheetId} + moveCursor={false} + keyboradEventConfig={keyCodeConfig} + onFormulaSelectingChange={(isSelecting: 0 | 1 | 2) => { + isRefSelecting.current = isSelecting; + if (isSelecting === 1) { + editorBridgeService.enableForceKeepVisible(); + } else { + editorBridgeService.disableForceKeepVisible(); + } + }} + /> )}
{arrowDirection === ArrowDirection.Down From f96a3b142f27a862a04883cb047729cfa1272979 Mon Sep 17 00:00:00 2001 From: zhangw Date: Fri, 29 Nov 2024 19:03:53 +0800 Subject: [PATCH 013/134] feat: update --- .../src/views/formula-editor/index.tsx | 23 ++++++------------- .../range-selector/hooks/useHighlight.ts | 16 ++++++------- .../editor/data-sync.controller.ts | 5 ++-- 3 files changed, 18 insertions(+), 26 deletions(-) diff --git a/packages/sheets-formula-ui/src/views/formula-editor/index.tsx b/packages/sheets-formula-ui/src/views/formula-editor/index.tsx index f00bae70231..3fe4deb5923 100644 --- a/packages/sheets-formula-ui/src/views/formula-editor/index.tsx +++ b/packages/sheets-formula-ui/src/views/formula-editor/index.tsx @@ -24,7 +24,7 @@ import { DocBackScrollRenderController, IEditorService } from '@univerjs/docs-ui import { EMBEDDING_FORMULA_EDITOR } from '@univerjs/sheets-ui'; import { useEvent } from '@univerjs/ui'; import clsx from 'clsx'; -import React, { useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react'; +import React, { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react'; import { useEmitChange } from '../range-selector/hooks/useEmitChange'; import { useFirstHighlightDoc } from '../range-selector/hooks/useFirstHighlightDoc'; import { useFocus } from '../range-selector/hooks/useFocus'; @@ -123,14 +123,18 @@ export function FormulaEditor(props: IFormulaEditorProps) { const highlightDoc = useDocHight('='); const highlightSheet = useSheetHighlight(unitId); - const highligh = (text: string, isNeedResetSelection: boolean = true) => { + const highligh = useCallback((text: string, isNeedResetSelection: boolean = true) => { if (!editor) { return; } const sequenceNodes = getFormulaToken(text); const ranges = highlightDoc(editor, sequenceNodes, isNeedResetSelection); highlightSheet(ranges); - }; + }, [editor, getFormulaToken, highlightDoc, highlightSheet]); + + useEffect(() => { + highligh(formulaWithoutEqualSymbol, false); + }, [highligh, formulaWithoutEqualSymbol]); useVerify(isFocus, onVerify, formulaText); const focus = useFocus(editor); @@ -189,19 +193,6 @@ export function FormulaEditor(props: IFormulaEditorProps) { const { searchList, searchText, handlerFormulaReplace, reset: resetFormulaSearch } = useFormulaSearch(isFocus, sequenceNodes, editor); const { functionInfo, paramIndex, reset } = useFormulaDescribe(isFocus, formulaText, editor); - useEffect(() => { - if (editor) { - const d = editor.input$.subscribe((e) => { - const text = (e.data.body?.dataStream ?? '').replaceAll(/\n|\r/g, ''); - needEmit(); - highligh(getFormulaText(text), false); - }); - return () => { - d.unsubscribe(); - }; - } - }, [editor]); - useFirstHighlightDoc(formulaWithoutEqualSymbol, '=', isFocus, highlightDoc, highlightSheet, editor); useLayoutEffect(() => { diff --git a/packages/sheets-formula-ui/src/views/range-selector/hooks/useHighlight.ts b/packages/sheets-formula-ui/src/views/range-selector/hooks/useHighlight.ts index 0111c0778db..5659f7fd9cf 100644 --- a/packages/sheets-formula-ui/src/views/range-selector/hooks/useHighlight.ts +++ b/packages/sheets-formula-ui/src/views/range-selector/hooks/useHighlight.ts @@ -25,7 +25,7 @@ import { IRenderManagerService } from '@univerjs/engine-render'; import { IRefSelectionsService, setEndForRange } from '@univerjs/sheets'; import { IDescriptionService } from '@univerjs/sheets-formula'; import { SheetSkeletonManagerService } from '@univerjs/sheets-ui'; -import { useMemo } from 'react'; +import { useCallback, useMemo } from 'react'; import { genFormulaRefSelectionStyle } from '../../../common/selection'; import { RefSelectionsRenderService } from '../../../services/render-services/ref-selections.render-service'; @@ -50,7 +50,7 @@ export function useSheetHighlight(unitId: string) { const refSelectionsRenderService = render?.with(RefSelectionsRenderService); const sheetSkeletonManagerService = render?.with(SheetSkeletonManagerService); - const highlightSheet = (refSelections: IRefSelection[]) => { + const highlightSheet = useCallback((refSelections: IRefSelection[]) => { const workbook = univerInstanceService.getUnit(unitId); const worksheet = workbook?.getActiveSheet(); const selectionWithStyle: ISelectionWithStyle[] = []; @@ -94,7 +94,7 @@ export function useSheetHighlight(unitId: string) { } else { refSelectionsService.setSelections(selectionWithStyle); } - }; + }, [refSelectionsRenderService, refSelectionsService, sheetSkeletonManagerService, themeService, unitId, univerInstanceService]); return highlightSheet; } @@ -103,7 +103,7 @@ export function useDocHight(_leadingCharacter: string = '') { const colorMap = useColor(); const leadingCharacterLength = useMemo(() => _leadingCharacter.length, [_leadingCharacter]); - const highlightDoc = (editor: Editor, sequenceNodes: INode[], isNeedResetSelection = true) => { + const highlightDoc = useCallback((editor: Editor, sequenceNodes: INode[], isNeedResetSelection = true) => { const data = editor.getDocumentData(); if (!data) { return []; @@ -114,9 +114,9 @@ export function useDocHight(_leadingCharacter: string = '') { } const cloneBody = { dataStream: '', ...data.body }; if (sequenceNodes == null || sequenceNodes.length === 0) { - cloneBody.textRuns = []; - const cloneData = { ...data, body: cloneBody }; - editor.setDocumentData(cloneData); + // cloneBody.textRuns = []; + // const cloneData = { ...data, body: cloneBody }; + // editor.setDocumentData(cloneData); return []; } else { const { textRuns, refSelections } = buildTextRuns(descriptionService, colorMap, sequenceNodes); @@ -150,7 +150,7 @@ export function useDocHight(_leadingCharacter: string = '') { editor.setDocumentData(cloneData, selections); return refSelections; } - }; + }, [descriptionService, colorMap, leadingCharacterLength, _leadingCharacter]); return highlightDoc; } diff --git a/packages/sheets-ui/src/controllers/editor/data-sync.controller.ts b/packages/sheets-ui/src/controllers/editor/data-sync.controller.ts index 2e97cb2b14b..4bdd4e8ef9e 100644 --- a/packages/sheets-ui/src/controllers/editor/data-sync.controller.ts +++ b/packages/sheets-ui/src/controllers/editor/data-sync.controller.ts @@ -21,6 +21,7 @@ import type { IMoveRangeMutationParams, ISetRangeValuesMutationParams } from '@u import type { ICellEditorState } from '../../services/editor-bridge.service'; import { BooleanNumber, Disposable, DOCS_FORMULA_BAR_EDITOR_UNIT_ID_KEY, DOCS_NORMAL_EDITOR_UNIT_ID_KEY, HorizontalAlign, ICommandService, Inject, IUniverInstanceService, Tools, UniverInstanceType } from '@univerjs/core'; import { DocSkeletonManagerService, RichTextEditingMutation } from '@univerjs/docs'; +import { ReplaceSnapshotCommand } from '@univerjs/docs-ui'; import { DeviceInputEventType, IRenderManagerService } from '@univerjs/engine-render'; import { MoveRangeMutation, RangeProtectionRuleModel, SetRangeValuesMutation, WorksheetProtectionRuleModel } from '@univerjs/sheets'; import { IEditorBridgeService } from '../../services/editor-bridge.service'; @@ -101,8 +102,8 @@ export class EditorDataSyncController extends Disposable { this._commandService.onCommandExecuted((command: ICommandInfo) => { if (command.id === RichTextEditingMutation.id) { const params = command.params as IRichTextEditingMutationParams; - const { unitId } = params; - if (params.isSync) { + const { unitId, trigger, isSync } = params; + if (isSync || trigger === ReplaceSnapshotCommand.id) { return; } From 1e9ce8ce0f905bd3f78775d63e16509016206ce6 Mon Sep 17 00:00:00 2001 From: zhangw Date: Fri, 29 Nov 2024 20:04:33 +0800 Subject: [PATCH 014/134] feat: update --- .../hooks/useSheetSelectionChange.ts | 12 +- .../src/views/formula-editor/index.tsx | 17 ++- .../hooks/useLeftAndRightArrow.ts | 116 ++++++++++++++---- .../src/views/range-selector/index.tsx | 2 +- .../editor-container/EditorContainer.tsx | 1 - .../src/views/editor-container/hooks.ts | 78 ++---------- .../src/views/formula-bar/FormulaBar.tsx | 1 - .../selections/ref-selections.service.ts | 1 + .../services/selections/selection.service.ts | 2 + 9 files changed, 132 insertions(+), 98 deletions(-) diff --git a/packages/sheets-formula-ui/src/views/formula-editor/hooks/useSheetSelectionChange.ts b/packages/sheets-formula-ui/src/views/formula-editor/hooks/useSheetSelectionChange.ts index 12517dcc5b3..68a5f188f35 100644 --- a/packages/sheets-formula-ui/src/views/formula-editor/hooks/useSheetSelectionChange.ts +++ b/packages/sheets-formula-ui/src/views/formula-editor/hooks/useSheetSelectionChange.ts @@ -19,11 +19,11 @@ import type { Workbook } from '@univerjs/core'; import type { Editor } from '@univerjs/docs-ui'; -import type { ISelectionWithCoord } from '@univerjs/sheets'; import type { INode } from '../../range-selector/utils/filterReferenceNode'; import { DisposableCollection, IUniverInstanceService, useDependency, useObservable } from '@univerjs/core'; import { deserializeRangeWithSheet, sequenceNodeType, serializeRange, serializeRangeWithSheet } from '@univerjs/engine-formula'; import { IRenderManagerService } from '@univerjs/engine-render'; +import { IRefSelectionsService, type ISelectionWithCoord } from '@univerjs/sheets'; import { useEffect, useMemo, useRef } from 'react'; import { merge } from 'rxjs'; import { debounceTime, distinctUntilChanged, map } from 'rxjs/operators'; @@ -47,7 +47,7 @@ export const useSheetSelectionChange = ( ) => { const renderManagerService = useDependency(IRenderManagerService); const univerInstanceService = useDependency(IUniverInstanceService); - + const refSelectionService = useDependency(IRefSelectionsService); const sequenceNodesRef = useStateRef(sequenceNodes); const { getIsNeedAddSelection } = useSelectionAdd(unitId, sequenceNodes, editor); @@ -179,6 +179,13 @@ export const useSheetSelectionChange = ( } }); + const d3 = refSelectionService.selectionSet$.subscribe((selections) => { + handleSelectionsChange(selections.map((item) => ({ + ...item, + rangeWithCoord: item.range!, + primaryWithCoord: item.primary!, + } as any))); + }); // const d2 = refSelectionsRenderService.selectionMoving$.subscribe((selections) => { // handleSelectionsChange(selections); // }); @@ -186,6 +193,7 @@ export const useSheetSelectionChange = ( return () => { d1.unsubscribe(); // d2.unsubscribe(); + d3.unsubscribe(); }; } }, [refSelectionsRenderService, editor, isSupportAcrossSheet, isNeed]); diff --git a/packages/sheets-formula-ui/src/views/formula-editor/index.tsx b/packages/sheets-formula-ui/src/views/formula-editor/index.tsx index 3fe4deb5923..ea0cb70a18e 100644 --- a/packages/sheets-formula-ui/src/views/formula-editor/index.tsx +++ b/packages/sheets-formula-ui/src/views/formula-editor/index.tsx @@ -18,7 +18,6 @@ import type { DocumentDataModel, IDisposable } from '@univerjs/core'; import type { Editor } from '@univerjs/docs-ui'; import type { ReactNode } from 'react'; import type { IKeyboardEventConfig } from '../range-selector/hooks/useKeyboardEvent'; -import type { FormulaSelectingType } from './hooks/useFormulaSelection'; import { BuildTextUtils, createInternalEditorID, generateRandomId, IUniverInstanceService, useDependency, useObservable } from '@univerjs/core'; import { DocBackScrollRenderController, IEditorService } from '@univerjs/docs-ui'; import { EMBEDDING_FORMULA_EDITOR } from '@univerjs/sheets-ui'; @@ -40,7 +39,7 @@ import { useSwitchSheet } from '../range-selector/hooks/useSwitchSheet'; import { HelpFunction } from './help-function/HelpFunction'; import { useFormulaDescribe } from './hooks/useFormulaDescribe'; import { useFormulaSearch } from './hooks/useFormulaSearch'; -import { useFormulaSelecting } from './hooks/useFormulaSelection'; +import { FormulaSelectingType, useFormulaSelecting } from './hooks/useFormulaSelection'; import { useSheetSelectionChange } from './hooks/useSheetSelectionChange'; import { useVerify } from './hooks/useVerify'; import styles from './index.module.less'; @@ -116,6 +115,15 @@ export function FormulaEditor(props: IFormulaEditorProps) { const formulaWithoutEqualSymbol = useMemo(() => getFormulaText(formulaText), [formulaText]); const sequenceNodes = useMemo(() => getFormulaToken(formulaWithoutEqualSymbol), [formulaWithoutEqualSymbol, getFormulaToken]); const isSelecting = useFormulaSelecting(editorId, sequenceNodes); + const [shouldMoveRefSelection, setShouldMoveRefSelection] = useState(false); + useEffect(() => { + if (isSelecting === FormulaSelectingType.NEED_ADD) { + setShouldMoveRefSelection(true); + } + if (isSelecting === FormulaSelectingType.NOT_SELECT) { + setShouldMoveRefSelection(false); + } + }, [isSelecting]); const needEmit = useEmitChange(sequenceNodes, (text: string) => { onChange(`=${text}`); @@ -166,7 +174,7 @@ export function FormulaEditor(props: IFormulaEditorProps) { const { checkScrollBar } = useResize(editor); useRefactorEffect(isFocus, Boolean(isSelecting), unitId); - useLeftAndRightArrow(isFocus && moveCursor, editor); + useLeftAndRightArrow(isFocus && moveCursor, shouldMoveRefSelection, editor); const handleSelectionChange = (refString: string, offset: number, isEnd: boolean) => { needEmit(); @@ -241,6 +249,9 @@ export function FormulaEditor(props: IFormulaEditorProps) { // 即使失焦是 mousedown 事件, // 聚焦是 mouseup 事件, // 但是 react 的 useEffect 无法保证顺序,无法确保失焦在聚焦之前. + if (isSelecting !== FormulaSelectingType.NEED_ADD) { + setShouldMoveRefSelection(false); + } setTimeout(() => { isFocusSet(true); diff --git a/packages/sheets-formula-ui/src/views/range-selector/hooks/useLeftAndRightArrow.ts b/packages/sheets-formula-ui/src/views/range-selector/hooks/useLeftAndRightArrow.ts index a3fcd0135c6..4faf597302a 100644 --- a/packages/sheets-formula-ui/src/views/range-selector/hooks/useLeftAndRightArrow.ts +++ b/packages/sheets-formula-ui/src/views/range-selector/hooks/useLeftAndRightArrow.ts @@ -14,16 +14,23 @@ * limitations under the License. */ -import type { Editor } from '@univerjs/docs-ui'; -import { CommandType, DisposableCollection, ICommandService, useDependency } from '@univerjs/core'; +import { CommandType, Direction, DisposableCollection, ICommandService, useDependency } from '@univerjs/core'; +import { type Editor, MoveCursorOperation, MoveSelectionOperation } from '@univerjs/docs-ui'; import { DeviceInputEventType } from '@univerjs/engine-render'; -import { IShortcutService, KeyCode } from '@univerjs/ui'; -import { useEffect } from 'react'; +import { ExpandSelectionCommand, JumpOver, MoveSelectionCommand } from '@univerjs/sheets-ui'; +import { IShortcutService, KeyCode, MetaKeys } from '@univerjs/ui'; +import { useEffect, useRef } from 'react'; -export const useLeftAndRightArrow = (isNeed: boolean, editor?: Editor) => { +// eslint-disable-next-line max-lines-per-function +export const useLeftAndRightArrow = (isNeed: boolean, shouldMoveSelection: boolean, editor?: Editor, onMoveInEditor?: (keyCode: KeyCode, metaKey?: MetaKeys) => void) => { const commandService = useDependency(ICommandService); const shortcutService = useDependency(IShortcutService); + const shouldMoveSelectionRef = useRef(shouldMoveSelection); + shouldMoveSelectionRef.current = shouldMoveSelection; + const onMoveInEditorRef = useRef(onMoveInEditor); + onMoveInEditorRef.current = onMoveInEditor; + // eslint-disable-next-line max-lines-per-function useEffect(() => { if (!editor || !isNeed) { return; @@ -31,23 +38,65 @@ export const useLeftAndRightArrow = (isNeed: boolean, editor?: Editor) => { const editorId = editor.getEditorId(); const operationId = `sheet.formula-embedding-editor.${editorId}`; const d = new DisposableCollection(); - const handleKeycode = (keycode: KeyCode) => { - const selections = editor.getSelectionRanges(); - if (selections.length === 1) { - const range = selections[0]; - switch (keycode) { - case KeyCode.ARROW_LEFT: { - const offset = Math.max(range.startOffset - 1, 0); - editor.setSelectionRanges([{ startOffset: offset, endOffset: offset }]); - break; - } - case KeyCode.ARROW_RIGHT: { - const content = (editor.getDocumentData().body?.dataStream || ',,').length - 2; - const offset = Math.min(range.endOffset + 1, content); - editor.setSelectionRanges([{ startOffset: offset, endOffset: offset }]); - break; - } + const handleMoveInEditor = (keycode: KeyCode, metaKey?: MetaKeys) => { + if (onMoveInEditorRef.current) { + onMoveInEditorRef.current(keycode, metaKey); + return; + } + + let direction = Direction.LEFT; + if (keycode === KeyCode.ARROW_DOWN) { + direction = Direction.DOWN; + } else if (keycode === KeyCode.ARROW_UP) { + direction = Direction.UP; + } else if (keycode === KeyCode.ARROW_RIGHT) { + direction = Direction.RIGHT; + } + + if (metaKey === MetaKeys.SHIFT) { + commandService.executeCommand(MoveSelectionOperation.id, { + direction, + }); + } else { + commandService.executeCommand(MoveCursorOperation.id, { + direction, + }); + } + }; + + const handleKeycode = (keycode: KeyCode, metaKey?: MetaKeys) => { + let direction = Direction.DOWN; + if (keycode === KeyCode.ARROW_DOWN) { + direction = Direction.DOWN; + } else if (keycode === KeyCode.ARROW_UP) { + direction = Direction.UP; + } else if (keycode === KeyCode.ARROW_LEFT) { + direction = Direction.LEFT; + } else if (keycode === KeyCode.ARROW_RIGHT) { + direction = Direction.RIGHT; + } + if (shouldMoveSelectionRef.current) { + if (metaKey === MetaKeys.CTRL_COMMAND) { + commandService.executeCommand(MoveSelectionCommand.id, { + direction, + jumpOver: JumpOver.moveGap, + }); + } else if (metaKey === MetaKeys.SHIFT) { + commandService.executeCommand(ExpandSelectionCommand.id, { + direction, + }); + } else if (metaKey === (MetaKeys.CTRL_COMMAND | MetaKeys.SHIFT)) { + commandService.executeCommand(ExpandSelectionCommand.id, { + direction, + jumpOver: JumpOver.moveGap, + }); + } else { + commandService.executeCommand(MoveSelectionCommand.id, { + direction, + }); } + } else { + handleMoveInEditor(keycode, metaKey); } }; @@ -60,10 +109,29 @@ export const useLeftAndRightArrow = (isNeed: boolean, editor?: Editor) => { }, })); - [KeyCode.ARROW_LEFT, KeyCode.ARROW_RIGHT, KeyCode.ARROW_DOWN, KeyCode.ARROW_UP].map((keyCode) => { + const keyCodes = [ + { keyCode: KeyCode.ARROW_DOWN }, + { keyCode: KeyCode.ARROW_LEFT }, + { keyCode: KeyCode.ARROW_RIGHT }, + { keyCode: KeyCode.ARROW_UP }, + { keyCode: KeyCode.ARROW_DOWN, metaKey: MetaKeys.SHIFT }, + { keyCode: KeyCode.ARROW_LEFT, metaKey: MetaKeys.SHIFT }, + { keyCode: KeyCode.ARROW_RIGHT, metaKey: MetaKeys.SHIFT }, + { keyCode: KeyCode.ARROW_UP, metaKey: MetaKeys.SHIFT }, + { keyCode: KeyCode.ARROW_DOWN, metaKey: MetaKeys.CTRL_COMMAND }, + { keyCode: KeyCode.ARROW_LEFT, metaKey: MetaKeys.CTRL_COMMAND }, + { keyCode: KeyCode.ARROW_RIGHT, metaKey: MetaKeys.CTRL_COMMAND }, + { keyCode: KeyCode.ARROW_UP, metaKey: MetaKeys.CTRL_COMMAND }, + { keyCode: KeyCode.ARROW_DOWN, metaKey: MetaKeys.CTRL_COMMAND | MetaKeys.SHIFT }, + { keyCode: KeyCode.ARROW_LEFT, metaKey: MetaKeys.CTRL_COMMAND | MetaKeys.SHIFT }, + { keyCode: KeyCode.ARROW_RIGHT, metaKey: MetaKeys.CTRL_COMMAND | MetaKeys.SHIFT }, + { keyCode: KeyCode.ARROW_UP, metaKey: MetaKeys.CTRL_COMMAND | MetaKeys.SHIFT }, + ]; + + keyCodes.map(({ keyCode, metaKey }) => { return { id: operationId, - binding: keyCode, + binding: metaKey ? keyCode | metaKey : keyCode, preconditions: () => true, priority: 900, staticParameters: { @@ -78,5 +146,5 @@ export const useLeftAndRightArrow = (isNeed: boolean, editor?: Editor) => { return () => { d.dispose(); }; - }, [editor, isNeed]); + }, [commandService, editor, isNeed, shortcutService]); }; diff --git a/packages/sheets-formula-ui/src/views/range-selector/index.tsx b/packages/sheets-formula-ui/src/views/range-selector/index.tsx index 0399cd95c23..b3db2116d8d 100644 --- a/packages/sheets-formula-ui/src/views/range-selector/index.tsx +++ b/packages/sheets-formula-ui/src/views/range-selector/index.tsx @@ -237,7 +237,7 @@ export function RangeSelector(props: IRangeSelectorProps) { useVerify(isNeed, onVerify, sequenceNodes); - useLeftAndRightArrow(isNeed, editor); + useLeftAndRightArrow(isNeed, false, editor); useRefocus(); diff --git a/packages/sheets-ui/src/views/editor-container/EditorContainer.tsx b/packages/sheets-ui/src/views/editor-container/EditorContainer.tsx index 631268bf840..5f00b786ede 100644 --- a/packages/sheets-ui/src/views/editor-container/EditorContainer.tsx +++ b/packages/sheets-ui/src/views/editor-container/EditorContainer.tsx @@ -132,7 +132,6 @@ export const EditorContainer: React.FC = () => { isFocus={visible?.visible} unitId={editState?.unitId} subUnitId={editState?.sheetId} - moveCursor={false} keyboradEventConfig={keyCodeConfig} onFormulaSelectingChange={(isSelecting: 0 | 1 | 2) => { isRefSelecting.current = isSelecting; diff --git a/packages/sheets-ui/src/views/editor-container/hooks.ts b/packages/sheets-ui/src/views/editor-container/hooks.ts index 107ef256dce..5ff62b11b81 100644 --- a/packages/sheets-ui/src/views/editor-container/hooks.ts +++ b/packages/sheets-ui/src/views/editor-container/hooks.ts @@ -14,17 +14,21 @@ * limitations under the License. */ -import { Direction, ICommandService, useDependency } from '@univerjs/core'; +import { useDependency } from '@univerjs/core'; import { DeviceInputEventType } from '@univerjs/engine-render'; -import { KeyCode, MetaKeys } from '@univerjs/ui'; +import { KeyCode } from '@univerjs/ui'; import { useMemo } from 'react'; -import { ExpandSelectionCommand, JumpOver, MoveSelectionCommand } from '../../commands/commands/set-selection.command'; -import { SetCellEditVisibleArrowOperation } from '../../commands/operations/cell-edit.operation'; import { IEditorBridgeService } from '../../services/editor-bridge.service'; -// eslint-disable-next-line max-lines-per-function +// commandService.executeCommand(SetCellEditVisibleArrowOperation.id, { +// keycode, +// visible: false, +// eventType: DeviceInputEventType.Keyboard, +// isShift: metaKey === MetaKeys.SHIFT || metaKey === (MetaKeys.CTRL_COMMAND | MetaKeys.SHIFT), +// unitId, +// }); + export function useKeyEventConfig(isRefSelecting: React.MutableRefObject<0 | 1 | 2>, unitId: string) { - const commandService = useDependency(ICommandService); const editorBridgeService = useDependency(IEditorBridgeService); const keyCodeConfig = useMemo(() => ({ @@ -32,24 +36,8 @@ export function useKeyEventConfig(isRefSelecting: React.MutableRefObject<0 | 1 | { keyCode: KeyCode.ENTER }, { keyCode: KeyCode.ESC }, { keyCode: KeyCode.TAB }, - { keyCode: KeyCode.ARROW_DOWN }, - { keyCode: KeyCode.ARROW_LEFT }, - { keyCode: KeyCode.ARROW_RIGHT }, - { keyCode: KeyCode.ARROW_UP }, - { keyCode: KeyCode.ARROW_DOWN, metaKey: MetaKeys.SHIFT }, - { keyCode: KeyCode.ARROW_LEFT, metaKey: MetaKeys.SHIFT }, - { keyCode: KeyCode.ARROW_RIGHT, metaKey: MetaKeys.SHIFT }, - { keyCode: KeyCode.ARROW_UP, metaKey: MetaKeys.SHIFT }, - { keyCode: KeyCode.ARROW_DOWN, metaKey: MetaKeys.CTRL_COMMAND }, - { keyCode: KeyCode.ARROW_LEFT, metaKey: MetaKeys.CTRL_COMMAND }, - { keyCode: KeyCode.ARROW_RIGHT, metaKey: MetaKeys.CTRL_COMMAND }, - { keyCode: KeyCode.ARROW_UP, metaKey: MetaKeys.CTRL_COMMAND }, - { keyCode: KeyCode.ARROW_DOWN, metaKey: MetaKeys.CTRL_COMMAND | MetaKeys.SHIFT }, - { keyCode: KeyCode.ARROW_LEFT, metaKey: MetaKeys.CTRL_COMMAND | MetaKeys.SHIFT }, - { keyCode: KeyCode.ARROW_RIGHT, metaKey: MetaKeys.CTRL_COMMAND | MetaKeys.SHIFT }, - { keyCode: KeyCode.ARROW_UP, metaKey: MetaKeys.CTRL_COMMAND | MetaKeys.SHIFT }, ], - handler: (keycode: KeyCode, metaKey?: MetaKeys) => { + handler: (keycode: KeyCode) => { if (keycode === KeyCode.ENTER || keycode === KeyCode.ESC || keycode === KeyCode.TAB) { editorBridgeService.changeVisible({ visible: false, @@ -57,51 +45,9 @@ export function useKeyEventConfig(isRefSelecting: React.MutableRefObject<0 | 1 | keycode, unitId: unitId!, }); - return; - } - - let direction = Direction.DOWN; - if (keycode === KeyCode.ARROW_DOWN) { - direction = Direction.DOWN; - } else if (keycode === KeyCode.ARROW_UP) { - direction = Direction.UP; - } else if (keycode === KeyCode.ARROW_LEFT) { - direction = Direction.LEFT; - } else if (keycode === KeyCode.ARROW_RIGHT) { - direction = Direction.RIGHT; - } - - if (isRefSelecting.current) { - if (metaKey === MetaKeys.CTRL_COMMAND) { - commandService.executeCommand(MoveSelectionCommand.id, { - direction, - jumpOver: JumpOver.moveGap, - }); - } else if (metaKey === MetaKeys.SHIFT) { - commandService.executeCommand(ExpandSelectionCommand.id, { - direction, - }); - } else if (metaKey === (MetaKeys.CTRL_COMMAND | MetaKeys.SHIFT)) { - commandService.executeCommand(ExpandSelectionCommand.id, { - direction, - jumpOver: JumpOver.moveGap, - }); - } else { - commandService.executeCommand(MoveSelectionCommand.id, { - direction, - }); - } - } else { - commandService.executeCommand(SetCellEditVisibleArrowOperation.id, { - keycode, - visible: false, - eventType: DeviceInputEventType.Keyboard, - isShift: metaKey === MetaKeys.SHIFT || metaKey === (MetaKeys.CTRL_COMMAND | MetaKeys.SHIFT), - unitId, - }); } }, - }), [isRefSelecting, editorBridgeService, unitId, commandService]); + }), [editorBridgeService, unitId]); return keyCodeConfig; } diff --git a/packages/sheets-ui/src/views/formula-bar/FormulaBar.tsx b/packages/sheets-ui/src/views/formula-bar/FormulaBar.tsx index cc5cd46a70f..accfde9d795 100644 --- a/packages/sheets-ui/src/views/formula-bar/FormulaBar.tsx +++ b/packages/sheets-ui/src/views/formula-bar/FormulaBar.tsx @@ -213,7 +213,6 @@ export function FormulaBar() { className={styles.formulaContent} unitId={editState?.unitId} subUnitId={editState?.sheetId} - moveCursor={false} keyboradEventConfig={keyCodeConfig} onFormulaSelectingChange={(isSelecting: 0 | 1 | 2) => { isRefSelecting.current = isSelecting; diff --git a/packages/sheets/src/services/selections/ref-selections.service.ts b/packages/sheets/src/services/selections/ref-selections.service.ts index 59f830ca3a8..878c703b3fb 100644 --- a/packages/sheets/src/services/selections/ref-selections.service.ts +++ b/packages/sheets/src/services/selections/ref-selections.service.ts @@ -45,6 +45,7 @@ export class RefSelectionsService extends SheetsSelectionsService { this.selectionMoveStart$ = $.pipe(switchMap((ss) => merge(...ss.map((s) => s.selectionMoveStart$)))); this.selectionMoving$ = $.pipe(switchMap((ss) => merge(...ss.map((s) => s.selectionMoving$)))); this.selectionMoveEnd$ = $.pipe(switchMap((ss) => merge(...ss.map((s) => s.selectionMoveEnd$)))); + this.selectionSet$ = $.pipe(switchMap((ss) => merge(...ss.map((s) => s.selectionSet$)))); } private _getAliveWorkbooks$(): Observable { diff --git a/packages/sheets/src/services/selections/selection.service.ts b/packages/sheets/src/services/selections/selection.service.ts index 6b846bd3f12..b929de25e21 100644 --- a/packages/sheets/src/services/selections/selection.service.ts +++ b/packages/sheets/src/services/selections/selection.service.ts @@ -40,6 +40,7 @@ export class SheetsSelectionsService extends RxDisposable { selectionMoveStart$: Observable>; selectionMoving$: Observable>; selectionMoveEnd$: Observable; + selectionSet$: Observable; constructor( @IUniverInstanceService protected readonly _instanceSrv: IUniverInstanceService @@ -54,6 +55,7 @@ export class SheetsSelectionsService extends RxDisposable { this.selectionMoveStart$ = c$.pipe(switchMap((workbook) => !workbook ? of() : this._ensureWorkbookSelection(workbook.getUnitId()).selectionMoveStart$)); this.selectionMoving$ = c$.pipe(switchMap((workbook) => !workbook ? of() : this._ensureWorkbookSelection(workbook.getUnitId()).selectionMoving$)); this.selectionMoveEnd$ = c$.pipe(switchMap((workbook) => !workbook ? of([]) : this._ensureWorkbookSelection(workbook.getUnitId()).selectionMoveEnd$)); + this.selectionSet$ = c$.pipe(switchMap((workbook) => !workbook ? of([]) : this._ensureWorkbookSelection(workbook.getUnitId()).selectionSet$)); this._instanceSrv.getTypeOfUnitDisposed$(UniverInstanceType.UNIVER_SHEET).pipe(takeUntil(this.dispose$)).subscribe((workbook) => { this._removeWorkbookSelection(workbook.getUnitId()); From c9b9e212f170c16d1e07a0bbda52e43cda13730a Mon Sep 17 00:00:00 2001 From: zhangw Date: Fri, 29 Nov 2024 21:37:06 +0800 Subject: [PATCH 015/134] feat: update --- .../src/views/formula-editor/index.tsx | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/packages/sheets-formula-ui/src/views/formula-editor/index.tsx b/packages/sheets-formula-ui/src/views/formula-editor/index.tsx index ea0cb70a18e..76e0b1b64f3 100644 --- a/packages/sheets-formula-ui/src/views/formula-editor/index.tsx +++ b/packages/sheets-formula-ui/src/views/formula-editor/index.tsx @@ -16,6 +16,7 @@ import type { DocumentDataModel, IDisposable } from '@univerjs/core'; import type { Editor } from '@univerjs/docs-ui'; +import type { KeyCode, MetaKeys } from '@univerjs/ui'; import type { ReactNode } from 'react'; import type { IKeyboardEventConfig } from '../range-selector/hooks/useKeyboardEvent'; import { BuildTextUtils, createInternalEditorID, generateRandomId, IUniverInstanceService, useDependency, useObservable } from '@univerjs/core'; @@ -65,7 +66,9 @@ export interface IFormulaEditorProps { moveCursor?: boolean; onFormulaSelectingChange?: (isSelecting: FormulaSelectingType) => void; keyboradEventConfig?: IKeyboardEventConfig; + onMoveInEditor?: (keyCode: KeyCode, metaKey?: MetaKeys) => void; } + const noop = () => { }; export function FormulaEditor(props: IFormulaEditorProps) { const { @@ -85,6 +88,7 @@ export function FormulaEditor(props: IFormulaEditorProps) { moveCursor = true, onFormulaSelectingChange: propOnFormulaSelectingChange, keyboradEventConfig, + onMoveInEditor, } = props; const editorService = useDependency(IEditorService); @@ -116,6 +120,7 @@ export function FormulaEditor(props: IFormulaEditorProps) { const sequenceNodes = useMemo(() => getFormulaToken(formulaWithoutEqualSymbol), [formulaWithoutEqualSymbol, getFormulaToken]); const isSelecting = useFormulaSelecting(editorId, sequenceNodes); const [shouldMoveRefSelection, setShouldMoveRefSelection] = useState(false); + useEffect(() => { if (isSelecting === FormulaSelectingType.NEED_ADD) { setShouldMoveRefSelection(true); @@ -174,11 +179,13 @@ export function FormulaEditor(props: IFormulaEditorProps) { const { checkScrollBar } = useResize(editor); useRefactorEffect(isFocus, Boolean(isSelecting), unitId); - useLeftAndRightArrow(isFocus && moveCursor, shouldMoveRefSelection, editor); + useLeftAndRightArrow(isFocus && moveCursor, shouldMoveRefSelection, editor, onMoveInEditor); - const handleSelectionChange = (refString: string, offset: number, isEnd: boolean) => { + const handleSelectionChange = useEvent((refString: string, offset: number, isEnd: boolean) => { needEmit(); - highligh(refString); + if (refString !== formulaText) { + highligh(refString); + } if (isEnd) { focus(); if (offset !== -1) { @@ -192,8 +199,8 @@ export function FormulaEditor(props: IFormulaEditorProps) { } checkScrollBar(); } - }; - useSheetSelectionChange(isFocus, unitId, subUnitId, sequenceNodes, isSupportAcrossSheet, editor, handleSelectionChange); + }); + useSheetSelectionChange(isFocus, unitId, subUnitId, sequenceNodes, isSupportAcrossSheet, shouldMoveRefSelection, editor, handleSelectionChange); useRefocus(); useSwitchSheet(isFocus, unitId, isSupportAcrossSheet, isFocusSet, onBlur, noop); From ee807ea93e0dff9c6d5f182d2d58e9e6c9afc847 Mon Sep 17 00:00:00 2001 From: zhangw Date: Fri, 29 Nov 2024 21:52:53 +0800 Subject: [PATCH 016/134] feat: update --- .../src/sheets-formula-ui.plugin.ts | 4 +- .../src/views/formula-editor/cell-editor.tsx | 250 ++++++++++++++++++ .../hooks/useSheetSelectionChange.ts | 87 ++++-- .../views/range-selector/hooks/useFocus.ts | 27 +- .../range-selector/hooks/useRefactorEffect.ts | 4 +- .../commands/set-selection.command.ts | 18 +- packages/sheets-ui/src/common/keys.ts | 1 + packages/sheets-ui/src/index.ts | 6 +- .../editor-container/EditorContainer.tsx | 20 +- .../operations/selection.operation.ts | 1 + 10 files changed, 368 insertions(+), 50 deletions(-) create mode 100644 packages/sheets-formula-ui/src/views/formula-editor/cell-editor.tsx diff --git a/packages/sheets-formula-ui/src/sheets-formula-ui.plugin.ts b/packages/sheets-formula-ui/src/sheets-formula-ui.plugin.ts index 9e097a76622..a0ecbbbebf6 100644 --- a/packages/sheets-formula-ui/src/sheets-formula-ui.plugin.ts +++ b/packages/sheets-formula-ui/src/sheets-formula-ui.plugin.ts @@ -20,7 +20,7 @@ import { DependentOn, IConfigService, Inject, Injector, Plugin, touchDependencie import { UniverFormulaEnginePlugin } from '@univerjs/engine-formula'; import { IRenderManagerService } from '@univerjs/engine-render'; import { UniverSheetsFormulaPlugin } from '@univerjs/sheets-formula'; -import { EMBEDDING_FORMULA_EDITOR_COMPONENT_KEY, RANGE_SELECTOR_COMPONENT_KEY } from '@univerjs/sheets-ui'; +import { EMBEDDING_CELL_EDITOR_COMPONENT_KEY, EMBEDDING_FORMULA_EDITOR_COMPONENT_KEY, RANGE_SELECTOR_COMPONENT_KEY } from '@univerjs/sheets-ui'; import { ComponentManager } from '@univerjs/ui'; import { FORMULA_UI_PLUGIN_NAME } from './common/plugin-name'; import { @@ -36,6 +36,7 @@ import { FormulaUIController } from './controllers/formula-ui.controller'; import { PromptController } from './controllers/prompt.controller'; import { FormulaPromptService, IFormulaPromptService } from './services/prompt.service'; import { RefSelectionsRenderService } from './services/render-services/ref-selections.render-service'; +import { CellEditor } from './views/formula-editor/cell-editor'; import { FormulaEditor } from './views/formula-editor/index'; import { RangeSelector } from './views/range-selector'; @@ -96,6 +97,7 @@ export class UniverSheetsFormulaUIPlugin extends Plugin { componentManager.register(RANGE_SELECTOR_COMPONENT_KEY, RangeSelector); componentManager.register(EMBEDDING_FORMULA_EDITOR_COMPONENT_KEY, FormulaEditor); + componentManager.register(EMBEDDING_CELL_EDITOR_COMPONENT_KEY, CellEditor); } override onSteady(): void { diff --git a/packages/sheets-formula-ui/src/views/formula-editor/cell-editor.tsx b/packages/sheets-formula-ui/src/views/formula-editor/cell-editor.tsx new file mode 100644 index 00000000000..3c9a003283a --- /dev/null +++ b/packages/sheets-formula-ui/src/views/formula-editor/cell-editor.tsx @@ -0,0 +1,250 @@ +/** + * 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 type { DocumentDataModel, IDisposable } from '@univerjs/core'; +import type { Editor } from '@univerjs/docs-ui'; +import type { KeyCode, MetaKeys } from '@univerjs/ui'; +import type { IKeyboardEventConfig } from '../range-selector/hooks/useKeyboardEvent'; +import { BuildTextUtils, IUniverInstanceService, useDependency, useObservable } from '@univerjs/core'; +import { DocBackScrollRenderController, IEditorService } from '@univerjs/docs-ui'; +import { useEvent } from '@univerjs/ui'; +import clsx from 'clsx'; +import React, { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react'; +import { useEmitChange } from '../range-selector/hooks/useEmitChange'; +import { useFirstHighlightDoc } from '../range-selector/hooks/useFirstHighlightDoc'; +import { useFocus } from '../range-selector/hooks/useFocus'; +import { useFormulaToken } from '../range-selector/hooks/useFormulaToken'; +import { useDocHight, useSheetHighlight } from '../range-selector/hooks/useHighlight'; +import { useKeyboardEvent } from '../range-selector/hooks/useKeyboardEvent'; +import { useLeftAndRightArrow } from '../range-selector/hooks/useLeftAndRightArrow'; +import { useRefactorEffect } from '../range-selector/hooks/useRefactorEffect'; +import { useRefocus } from '../range-selector/hooks/useRefocus'; +import { useResize } from '../range-selector/hooks/useResize'; +import { useSwitchSheet } from '../range-selector/hooks/useSwitchSheet'; +import { HelpFunction } from './help-function/HelpFunction'; +import { useFormulaDescribe } from './hooks/useFormulaDescribe'; +import { useFormulaSearch } from './hooks/useFormulaSearch'; +import { FormulaSelectingType, useFormulaSelecting } from './hooks/useFormulaSelection'; +import { useSheetSelectionChange } from './hooks/useSheetSelectionChange'; +import styles from './index.module.less'; +import { SearchFunction } from './search-function/SearchFunction'; +import { getFormulaText } from './utils/getFormulaText'; + +export interface IFormulaEditorProps { + unitId: string; + subUnitId: string; + onChange: (text: string) => void; + isFocus?: boolean; + onFocus?: () => void; + onBlur?: () => void; + isSupportAcrossSheet?: boolean; + className?: string; + editorId: string; + moveCursor?: boolean; + onFormulaSelectingChange?: (isSelecting: FormulaSelectingType) => void; + keyboradEventConfig?: IKeyboardEventConfig; + onMoveInEditor?: (keyCode: KeyCode, metaKey?: MetaKeys) => void; +} + +const noop = () => { }; +export function CellEditor(props: IFormulaEditorProps) { + const { + unitId, + subUnitId, + isFocus: _isFocus = true, + isSupportAcrossSheet = false, + onFocus = noop, + onBlur = noop, + onChange, + className, + editorId, + moveCursor = true, + onFormulaSelectingChange: propOnFormulaSelectingChange, + keyboradEventConfig, + onMoveInEditor, + } = props; + + const editorService = useDependency(IEditorService); + const sheetEmbeddingRef = useRef(null); + const onFormulaSelectingChange = useEvent(propOnFormulaSelectingChange); + const searchFunctionRef = useRef(null); + const [editor, editorSet] = useState(); + const [isFocus, isFocusSet] = useState(_isFocus); + const formulaEditorContainerRef = useRef(null); + const univerInstanceService = useDependency(IUniverInstanceService); + const document = univerInstanceService.getUnit(editorId); + useObservable(document?.change$); + const getFormulaToken = useFormulaToken(); + const formulaText = BuildTextUtils.transform.getPlainText(document?.getBody()?.dataStream ?? ''); + const formulaWithoutEqualSymbol = useMemo(() => getFormulaText(formulaText), [formulaText]); + const sequenceNodes = useMemo(() => getFormulaToken(formulaWithoutEqualSymbol), [formulaWithoutEqualSymbol, getFormulaToken]); + const isSelecting = useFormulaSelecting(editorId, sequenceNodes); + const [shouldMoveRefSelection, setShouldMoveRefSelection] = useState(false); + + useEffect(() => { + if (isSelecting === FormulaSelectingType.NEED_ADD) { + setShouldMoveRefSelection(true); + } + if (isSelecting === FormulaSelectingType.NOT_SELECT) { + setShouldMoveRefSelection(false); + } + }, [isSelecting]); + + const needEmit = useEmitChange(sequenceNodes, (text: string) => { + onChange(`=${text}`); + }, editor); + + const highlightDoc = useDocHight('='); + const highlightSheet = useSheetHighlight(unitId); + const highligh = useCallback((text: string, isNeedResetSelection: boolean = true) => { + if (!editor) { + return; + } + const sequenceNodes = getFormulaToken(text); + const ranges = highlightDoc(editor, sequenceNodes, isNeedResetSelection); + highlightSheet(ranges); + }, [editor, getFormulaToken, highlightDoc, highlightSheet]); + + useEffect(() => { + highligh(formulaWithoutEqualSymbol, false); + }, [highligh, formulaWithoutEqualSymbol]); + + const focus = useFocus(editor); + + useEffect(() => { + onFormulaSelectingChange(isSelecting); + }, [onFormulaSelectingChange, isSelecting]); + + useKeyboardEvent(isFocus, keyboradEventConfig, editor); + + const { checkScrollBar } = useResize(editor); + useRefactorEffect(isFocus, Boolean(isSelecting), unitId); + useLeftAndRightArrow(isFocus && moveCursor, shouldMoveRefSelection, editor, onMoveInEditor); + + const handleSelectionChange = useEvent((refString: string, offset: number, isEnd: boolean) => { + needEmit(); + if (refString !== formulaText) { + highligh(refString); + } + if (isEnd) { + focus(); + if (offset !== -1) { + // 在渲染结束之后再设置选区 + setTimeout(() => { + const range = { startOffset: offset + 1, endOffset: offset + 1 }; + editor?.setSelectionRanges([range]); + const docBackScrollRenderController = editor?.render.with(DocBackScrollRenderController); + docBackScrollRenderController?.scrollToRange({ ...range, collapsed: true }); + }, 50); + } + checkScrollBar(); + } + }); + useSheetSelectionChange(isFocus, unitId, subUnitId, sequenceNodes, isSupportAcrossSheet, shouldMoveRefSelection, editor, handleSelectionChange); + + useRefocus(); + useSwitchSheet(isFocus, unitId, isSupportAcrossSheet, isFocusSet, onBlur, noop); + + const { searchList, searchText, handlerFormulaReplace, reset: resetFormulaSearch } = useFormulaSearch(isFocus, sequenceNodes, editor); + const { functionInfo, paramIndex, reset } = useFormulaDescribe(isFocus, formulaText, editor); + + useFirstHighlightDoc(formulaWithoutEqualSymbol, '=', isFocus, highlightDoc, highlightSheet, editor); + + useLayoutEffect(() => { + let dispose: IDisposable; + if (formulaEditorContainerRef.current) { + dispose = editorService.register({ + autofocus: true, + editorUnitId: editorId, + isSingle: true, + initialSnapshot: { + id: editorId, + body: { dataStream: '\r\n' }, + documentStyle: {}, + }, + }, formulaEditorContainerRef.current); + const editor = editorService.getEditor(editorId)! as Editor; + editorSet(editor); + } + + return () => { + dispose?.dispose(); + }; + }, []); + + const handleFunctionSelect = (v: string) => { + const res = handlerFormulaReplace(v); + if (res) { + const selections = editor?.getSelectionRanges(); + if (selections && selections.length === 1) { + const range = selections[0]; + if (range.collapsed) { + const offset = res.offset; + setTimeout(() => { + editor?.setSelectionRanges([{ startOffset: range.startOffset - offset, endOffset: range.endOffset - offset }]); + }, 30); + } + } + resetFormulaSearch(); + focus(); + highligh(res.text); + } + }; + + const handleMouseUp = () => { + // 在进行多个 input 切换的时候,失焦必须快于获得焦点. + // 即使失焦是 mousedown 事件, + // 聚焦是 mouseup 事件, + // 但是 react 的 useEffect 无法保证顺序,无法确保失焦在聚焦之前. + if (isSelecting !== FormulaSelectingType.NEED_ADD) { + setShouldMoveRefSelection(false); + } + }; + return ( +
+
+
+
+
+ { + reset(); + focus(); + }} + > + + + +
+ ) + ; +} diff --git a/packages/sheets-formula-ui/src/views/formula-editor/hooks/useSheetSelectionChange.ts b/packages/sheets-formula-ui/src/views/formula-editor/hooks/useSheetSelectionChange.ts index 68a5f188f35..90aafdca4e2 100644 --- a/packages/sheets-formula-ui/src/views/formula-editor/hooks/useSheetSelectionChange.ts +++ b/packages/sheets-formula-ui/src/views/formula-editor/hooks/useSheetSelectionChange.ts @@ -19,11 +19,13 @@ import type { Workbook } from '@univerjs/core'; import type { Editor } from '@univerjs/docs-ui'; +import type { ISelectionWithCoord, ISetSelectionsOperationParams } from '@univerjs/sheets'; import type { INode } from '../../range-selector/utils/filterReferenceNode'; -import { DisposableCollection, IUniverInstanceService, useDependency, useObservable } from '@univerjs/core'; +import { DisposableCollection, ICommandService, IUniverInstanceService, useDependency, useObservable } from '@univerjs/core'; import { deserializeRangeWithSheet, sequenceNodeType, serializeRange, serializeRangeWithSheet } from '@univerjs/engine-formula'; import { IRenderManagerService } from '@univerjs/engine-render'; -import { IRefSelectionsService, type ISelectionWithCoord } from '@univerjs/sheets'; +import { SetSelectionsOperation } from '@univerjs/sheets'; +import { ExpandSelectionCommand, MoveSelectionCommand, MoveSelectionEnterAndTabCommand } from '@univerjs/sheets-ui'; import { useEffect, useMemo, useRef } from 'react'; import { merge } from 'rxjs'; import { debounceTime, distinctUntilChanged, map } from 'rxjs/operators'; @@ -42,12 +44,13 @@ export const useSheetSelectionChange = ( subUnitId: string, sequenceNodes: INode[], isSupportAcrossSheet: boolean, + listenSelectionSet: boolean, editor?: Editor, handleRangeChange: ((refString: string, offset: number, isEnd: boolean) => void) = noop ) => { const renderManagerService = useDependency(IRenderManagerService); const univerInstanceService = useDependency(IUniverInstanceService); - const refSelectionService = useDependency(IRefSelectionsService); + const commandService = useDependency(ICommandService); const sequenceNodesRef = useStateRef(sequenceNodes); const { getIsNeedAddSelection } = useSelectionAdd(unitId, sequenceNodes, editor); @@ -169,7 +172,8 @@ export const useSheetSelectionChange = ( handleRangeChange(result, result.length, true); } }; - const d1 = refSelectionsRenderService.selectionMoveEnd$.subscribe((selections) => { + const disposableCollection = new DisposableCollection(); + disposableCollection.add(refSelectionsRenderService.selectionMoveEnd$.subscribe((selections) => { handleSelectionsChange(selections); isScalingRef.current = false; if (scalingOptionRef.current) { @@ -177,23 +181,10 @@ export const useSheetSelectionChange = ( handleRangeChange(result, offset || -1, true); scalingOptionRef.current = undefined; } - }); - - const d3 = refSelectionService.selectionSet$.subscribe((selections) => { - handleSelectionsChange(selections.map((item) => ({ - ...item, - rangeWithCoord: item.range!, - primaryWithCoord: item.primary!, - } as any))); - }); - // const d2 = refSelectionsRenderService.selectionMoving$.subscribe((selections) => { - // handleSelectionsChange(selections); - // }); + })); return () => { - d1.unsubscribe(); - // d2.unsubscribe(); - d3.unsubscribe(); + disposableCollection.dispose(); }; } }, [refSelectionsRenderService, editor, isSupportAcrossSheet, isNeed]); @@ -286,4 +277,62 @@ export const useSheetSelectionChange = ( }; } }, [isNeed, refSelectionsRenderService, editor]); + + useEffect(() => { + if (listenSelectionSet) { + const d = commandService.onCommandExecuted((commandInfo) => { + if (commandInfo.id !== SetSelectionsOperation.id) { + return; + } + + const params = commandInfo.params as ISetSelectionsOperationParams & { trigger: string }; + if ( + params.trigger !== MoveSelectionCommand.id && + params.trigger !== MoveSelectionEnterAndTabCommand.id && + params.trigger !== ExpandSelectionCommand.id + ) { + return; + } + const { unitId, subUnitId, selections } = params; + + if (selections.length) { + const last = selections[selections.length - 1]; + if (last) { + const range = last.range; + const sheetId = range.sheetId ?? subUnitId; + const unitRangeName = { + range, + unitId: range.unitId ?? unitId, + sheetName: getSheetNameById(sheetId), + }; + const sequenceNodes = [...sequenceNodesRef.current]; + const refRanges = unitRangesToText([unitRangeName], isSupportAcrossSheet); + const result = refRanges[0]; + const lastNode = sequenceNodes[sequenceNodes.length - 1]; + if (typeof lastNode === 'object' && lastNode.nodeType === sequenceNodeType.REFERENCE) { + lastNode.token = result; + lastNode.endIndex = lastNode.startIndex + result.length; + const refStr = sequenceNodeToText(sequenceNodes); + handleRangeChange(refStr, getOffsetFromSequenceNodes(sequenceNodes), true); + } else { + const start = getOffsetFromSequenceNodes(sequenceNodes); + sequenceNodes.push({ + nodeType: sequenceNodeType.REFERENCE, + token: result, + startIndex: start, + endIndex: start + result.length, + }); + + const refStr = sequenceNodeToText(sequenceNodes); + handleRangeChange(refStr, getOffsetFromSequenceNodes(sequenceNodes), true); + } + } + } + }); + + return () => { + d.dispose(); + }; + } + }, [commandService, getSheetNameById, handleRangeChange, isSupportAcrossSheet, listenSelectionSet, sequenceNodesRef]); }; diff --git a/packages/sheets-formula-ui/src/views/range-selector/hooks/useFocus.ts b/packages/sheets-formula-ui/src/views/range-selector/hooks/useFocus.ts index 60a25bb957b..2cf60c7b1e1 100644 --- a/packages/sheets-formula-ui/src/views/range-selector/hooks/useFocus.ts +++ b/packages/sheets-formula-ui/src/views/range-selector/hooks/useFocus.ts @@ -15,26 +15,25 @@ */ import type { Editor } from '@univerjs/docs-ui'; -import { useMemo } from 'react'; +import { useCallback } from 'react'; export const useFocus = (editor?: Editor) => { - const focus = useMemo(() => { - return () => { - if (editor) { - editor.focus(); - const selections = [...editor.getSelectionRanges()]; - if (selections.length) { - editor.setSelectionRanges(selections); - } + const focus = useCallback(() => { + if (editor) { + editor.focus(); + const selections = [...editor.getSelectionRanges()]; + if (selections.length) { + editor.setSelectionRanges(selections); + } // end - if (!selections.length) { - const body = editor.getDocumentData().body?.dataStream ?? '\r\n'; - const offset = Math.max(body.length - 2, 0); - editor.setSelectionRanges([{ startOffset: offset, endOffset: offset }]); - } + if (!selections.length) { + const body = editor.getDocumentData().body?.dataStream ?? '\r\n'; + const offset = Math.max(body.length - 2, 0); + editor.setSelectionRanges([{ startOffset: offset, endOffset: offset }]); } }; }, [editor]); + return focus; }; diff --git a/packages/sheets-formula-ui/src/views/range-selector/hooks/useRefactorEffect.ts b/packages/sheets-formula-ui/src/views/range-selector/hooks/useRefactorEffect.ts index d6e745424be..4948aa2849e 100644 --- a/packages/sheets-formula-ui/src/views/range-selector/hooks/useRefactorEffect.ts +++ b/packages/sheets-formula-ui/src/views/range-selector/hooks/useRefactorEffect.ts @@ -34,10 +34,8 @@ export const useRefactorEffect = (isNeed: boolean, selecting: boolean, unitId: s if (isNeed && selecting) { const d1 = refSelectionsRenderService?.enableSelectionChanging(); contextService.setContextValue(DISABLE_NORMAL_SELECTIONS, true); - contextService.setContextValue(EDITOR_ACTIVATED, true); return () => { - contextService.setContextValue(EDITOR_ACTIVATED, false); contextService.setContextValue(DISABLE_NORMAL_SELECTIONS, false); d1?.dispose(); }; @@ -55,8 +53,10 @@ export const useRefactorEffect = (isNeed: boolean, selecting: boolean, unitId: s //right context controller useEffect(() => { if (isNeed) { + contextService.setContextValue(EDITOR_ACTIVATED, true); contextMenuService.disable(); return () => { + contextService.setContextValue(EDITOR_ACTIVATED, false); contextMenuService.enable(); }; } diff --git a/packages/sheets-ui/src/commands/commands/set-selection.command.ts b/packages/sheets-ui/src/commands/commands/set-selection.command.ts index dd45967dab3..f97b5ab28fd 100644 --- a/packages/sheets-ui/src/commands/commands/set-selection.command.ts +++ b/packages/sheets-ui/src/commands/commands/set-selection.command.ts @@ -67,7 +67,7 @@ export interface IMoveSelectionEnterAndTabCommandParams { export const MoveSelectionCommand: ICommand = { id: 'sheet.command.move-selection', type: CommandType.COMMAND, - handler: async (accessor, params) => { + handler: (accessor, params) => { if (!params) { return false; } @@ -121,11 +121,13 @@ export const MoveSelectionCommand: ICommand = { }, ]; - const rs = accessor.get(ICommandService).executeCommand(SetSelectionsOperation.id, { + const rs = accessor.get(ICommandService).syncExecuteCommand(SetSelectionsOperation.id, { unitId: workbook.getUnitId(), subUnitId: worksheet.getSheetId(), selections, + trigger: MoveSelectionCommand.id, }); + const renderManagerService = accessor.get(IRenderManagerService); const selectionService = renderManagerService.getRenderById(unitId)?.with(ISheetSelectionRenderService); selectionService?.refreshSelectionMoveEnd(); @@ -140,7 +142,7 @@ export const MoveSelectionEnterAndTabCommand: ICommand { + handler: (accessor, params) => { if (!params) { return false; } @@ -267,11 +269,11 @@ export const MoveSelectionEnterAndTabCommand: ICommand = { id: 'sheet.command.expand-selection', type: CommandType.COMMAND, - handler: async (accessor, params) => { + handler: (accessor, params) => { if (!params) return false; const target = getSheetCommandTarget(accessor.get(IUniverInstanceService)); @@ -323,16 +325,16 @@ export const ExpandSelectionCommand: ICommand = { return false; } - return accessor.get(ICommandService).executeCommand(SetSelectionsOperation.id, { + return accessor.get(ICommandService).syncExecuteCommand(SetSelectionsOperation.id, { unitId, subUnitId, - selections: [ { range: destRange, primary, // this remains unchanged }, ], + trigger: ExpandSelectionCommand.id, }); }, }; diff --git a/packages/sheets-ui/src/common/keys.ts b/packages/sheets-ui/src/common/keys.ts index 1b46f3b0567..d88bd6cdb63 100644 --- a/packages/sheets-ui/src/common/keys.ts +++ b/packages/sheets-ui/src/common/keys.ts @@ -21,6 +21,7 @@ export const SHEET_ZOOM_RANGE = [10, 400]; */ export const RANGE_SELECTOR_COMPONENT_KEY = 'RANGE_SELECTOR_COMPONENT_KEY'; export const EMBEDDING_FORMULA_EDITOR_COMPONENT_KEY = 'EMBEDDING_FORMULA_EDITOR_COMPONENT_KEY'; +export const EMBEDDING_CELL_EDITOR_COMPONENT_KEY = 'EMBEDDING_CELL_EDITOR_COMPONENT_KEY'; // end export enum SHEET_VIEW_KEY { diff --git a/packages/sheets-ui/src/index.ts b/packages/sheets-ui/src/index.ts index 4a3fbf81998..2e1a1f93953 100644 --- a/packages/sheets-ui/src/index.ts +++ b/packages/sheets-ui/src/index.ts @@ -62,9 +62,9 @@ export { export { MarkSelectionService } from './services/mark-selection/mark-selection.service'; export { IMarkSelectionService } from './services/mark-selection/mark-selection.service'; export { SheetSelectionRenderService } from './services/selection/selection-render.service'; -export { genSelectionByRange, getTopLeftSelectionOfCurrSheet, selectionDataForSelectAll as getAllSelection } from './services/selection/base-selection-render.service'; +export { genSelectionByRange, selectionDataForSelectAll as getAllSelection, getTopLeftSelectionOfCurrSheet } from './services/selection/base-selection-render.service'; export { BaseSelectionRenderService, ISheetSelectionRenderService } from './services/selection/base-selection-render.service'; -export { SelectionControl as SelectionShape, SelectionControl } from './services/selection/selection-control'; +export { SelectionControl, SelectionControl as SelectionShape } from './services/selection/selection-control'; export { SelectionShapeExtension } from './services/selection/selection-shape-extension'; export { genNormalSelectionStyle } from './services/selection/const'; export type { ISheetSkeletonManagerParam } from './services/sheet-skeleton-manager.service'; @@ -77,7 +77,7 @@ export { DragManagerService } from './services/drag-manager.service'; export { CellAlertManagerService, CellAlertType, type ICellAlert } from './services/cell-alert-manager.service'; export { HoverRenderController } from './controllers/hover-render.controller'; export { DragRenderController } from './controllers/drag-render.controller'; -export { EMBEDDING_FORMULA_EDITOR_COMPONENT_KEY, RANGE_SELECTOR_COMPONENT_KEY, SHEET_VIEW_KEY } from './common/keys'; +export { EMBEDDING_CELL_EDITOR_COMPONENT_KEY, EMBEDDING_FORMULA_EDITOR_COMPONENT_KEY, RANGE_SELECTOR_COMPONENT_KEY, SHEET_VIEW_KEY } from './common/keys'; export { type ICanvasPopup, SheetCanvasPopManagerService } from './services/canvas-pop-manager.service'; export { mergeSetRangeValues } from './services/clipboard/utils'; export type { IAutoFillLocation } from './services/auto-fill/type'; diff --git a/packages/sheets-ui/src/views/editor-container/EditorContainer.tsx b/packages/sheets-ui/src/views/editor-container/EditorContainer.tsx index 5f00b786ede..74b32840cd7 100644 --- a/packages/sheets-ui/src/views/editor-container/EditorContainer.tsx +++ b/packages/sheets-ui/src/views/editor-container/EditorContainer.tsx @@ -14,11 +14,13 @@ * limitations under the License. */ -import { DOCS_NORMAL_EDITOR_UNIT_ID_KEY, IContextService, useDependency } from '@univerjs/core'; +import type { KeyCode } from '@univerjs/ui'; +import { DOCS_NORMAL_EDITOR_UNIT_ID_KEY, ICommandService, IContextService, useDependency } from '@univerjs/core'; import { IEditorService } from '@univerjs/docs-ui'; -import { FIX_ONE_PIXEL_BLUR_OFFSET } from '@univerjs/engine-render'; -import { ComponentManager, DISABLE_AUTO_FOCUS_KEY, useObservable } from '@univerjs/ui'; +import { DeviceInputEventType, FIX_ONE_PIXEL_BLUR_OFFSET } from '@univerjs/engine-render'; +import { ComponentManager, DISABLE_AUTO_FOCUS_KEY, MetaKeys, useEvent, useObservable } from '@univerjs/ui'; import React, { useEffect, useRef, useState } from 'react'; +import { SetCellEditVisibleArrowOperation } from '../../commands/operations/cell-edit.operation'; import { EMBEDDING_FORMULA_EDITOR_COMPONENT_KEY } from '../../common/keys'; import { IEditorBridgeService } from '../../services/editor-bridge.service'; import { ICellEditorManagerService } from '../../services/editor/cell-editor-manager.service'; @@ -50,6 +52,7 @@ export const EditorContainer: React.FC = () => { const componentManager = useDependency(ComponentManager); const editorBridgeService = useDependency(IEditorBridgeService); const visible = useObservable(editorBridgeService.visible$); + const commandService = useDependency(ICommandService); const isRefSelecting = useRef<0 | 1 | 2>(0); const disableAutoFocus = useObservable( () => contextService.subscribeContextValue$(DISABLE_AUTO_FOCUS_KEY), @@ -112,6 +115,16 @@ export const EditorContainer: React.FC = () => { const keyCodeConfig = useKeyEventConfig(isRefSelecting, editState?.unitId!); + const onMoveInEditor = useEvent((keycode: KeyCode, metaKey: MetaKeys) => { + commandService.executeCommand(SetCellEditVisibleArrowOperation.id, { + keycode, + visible: false, + eventType: DeviceInputEventType.Keyboard, + isShift: metaKey === MetaKeys.SHIFT || metaKey === (MetaKeys.CTRL_COMMAND | MetaKeys.SHIFT), + unitId: editState?.unitId, + }); + }); + return (
= () => { unitId={editState?.unitId} subUnitId={editState?.sheetId} keyboradEventConfig={keyCodeConfig} + onMoveInEditor={onMoveInEditor} onFormulaSelectingChange={(isSelecting: 0 | 1 | 2) => { isRefSelecting.current = isSelecting; if (isSelecting === 1) { diff --git a/packages/sheets/src/commands/operations/selection.operation.ts b/packages/sheets/src/commands/operations/selection.operation.ts index b97eac85198..6d438921382 100644 --- a/packages/sheets/src/commands/operations/selection.operation.ts +++ b/packages/sheets/src/commands/operations/selection.operation.ts @@ -28,6 +28,7 @@ export interface ISetSelectionsOperationParams { /** If should scroll to the selected range. */ reveal?: boolean; + trigger?: string; } /** From 50ff8d95d8867fced208a431f9f820962626f3b6 Mon Sep 17 00:00:00 2001 From: zhangw Date: Sat, 30 Nov 2024 00:47:30 +0800 Subject: [PATCH 017/134] feat: update --- .../hooks/useSheetSelectionChange.ts | 9 ++------- .../src/views/formula-editor/index.tsx | 17 +++++++++-------- .../views/range-selector/hooks/useHighlight.ts | 10 ++++++---- .../hooks/useLeftAndRightArrow.ts | 4 ++++ .../commands/commands/set-selection.command.ts | 15 +++++++++------ .../commands/operations/selection.operation.ts | 2 +- 6 files changed, 31 insertions(+), 26 deletions(-) diff --git a/packages/sheets-formula-ui/src/views/formula-editor/hooks/useSheetSelectionChange.ts b/packages/sheets-formula-ui/src/views/formula-editor/hooks/useSheetSelectionChange.ts index 90aafdca4e2..b7acb160ee7 100644 --- a/packages/sheets-formula-ui/src/views/formula-editor/hooks/useSheetSelectionChange.ts +++ b/packages/sheets-formula-ui/src/views/formula-editor/hooks/useSheetSelectionChange.ts @@ -25,7 +25,6 @@ import { DisposableCollection, ICommandService, IUniverInstanceService, useDepen import { deserializeRangeWithSheet, sequenceNodeType, serializeRange, serializeRangeWithSheet } from '@univerjs/engine-formula'; import { IRenderManagerService } from '@univerjs/engine-render'; import { SetSelectionsOperation } from '@univerjs/sheets'; -import { ExpandSelectionCommand, MoveSelectionCommand, MoveSelectionEnterAndTabCommand } from '@univerjs/sheets-ui'; import { useEffect, useMemo, useRef } from 'react'; import { merge } from 'rxjs'; import { debounceTime, distinctUntilChanged, map } from 'rxjs/operators'; @@ -285,12 +284,8 @@ export const useSheetSelectionChange = ( return; } - const params = commandInfo.params as ISetSelectionsOperationParams & { trigger: string }; - if ( - params.trigger !== MoveSelectionCommand.id && - params.trigger !== MoveSelectionEnterAndTabCommand.id && - params.trigger !== ExpandSelectionCommand.id - ) { + const params = commandInfo.params as ISetSelectionsOperationParams; + if (params.extra !== 'formula-editor') { return; } const { unitId, subUnitId, selections } = params; diff --git a/packages/sheets-formula-ui/src/views/formula-editor/index.tsx b/packages/sheets-formula-ui/src/views/formula-editor/index.tsx index 76e0b1b64f3..e990c8e07f0 100644 --- a/packages/sheets-formula-ui/src/views/formula-editor/index.tsx +++ b/packages/sheets-formula-ui/src/views/formula-editor/index.tsx @@ -26,7 +26,6 @@ import { useEvent } from '@univerjs/ui'; import clsx from 'clsx'; import React, { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react'; import { useEmitChange } from '../range-selector/hooks/useEmitChange'; -import { useFirstHighlightDoc } from '../range-selector/hooks/useFirstHighlightDoc'; import { useFocus } from '../range-selector/hooks/useFocus'; import { useFormulaToken } from '../range-selector/hooks/useFormulaToken'; import { useDocHight, useSheetHighlight } from '../range-selector/hooks/useHighlight'; @@ -120,6 +119,7 @@ export function FormulaEditor(props: IFormulaEditorProps) { const sequenceNodes = useMemo(() => getFormulaToken(formulaWithoutEqualSymbol), [formulaWithoutEqualSymbol, getFormulaToken]); const isSelecting = useFormulaSelecting(editorId, sequenceNodes); const [shouldMoveRefSelection, setShouldMoveRefSelection] = useState(false); + const isFormula = useRef(false); useEffect(() => { if (isSelecting === FormulaSelectingType.NEED_ADD) { @@ -136,18 +136,19 @@ export function FormulaEditor(props: IFormulaEditorProps) { const highlightDoc = useDocHight('='); const highlightSheet = useSheetHighlight(unitId); - const highligh = useCallback((text: string, isNeedResetSelection: boolean = true) => { + const highligh = useCallback((text: string, isNeedResetSelection: boolean = true, resetTextRun = true) => { if (!editor) { return; } const sequenceNodes = getFormulaToken(text); - const ranges = highlightDoc(editor, sequenceNodes, isNeedResetSelection); - highlightSheet(ranges); - }, [editor, getFormulaToken, highlightDoc, highlightSheet]); + const ranges = highlightDoc(editor, sequenceNodes, isNeedResetSelection, resetTextRun); + highlightSheet(isFocus ? ranges : []); + }, [editor, getFormulaToken, highlightDoc, highlightSheet, isFocus]); useEffect(() => { - highligh(formulaWithoutEqualSymbol, false); - }, [highligh, formulaWithoutEqualSymbol]); + highligh(formulaWithoutEqualSymbol, false, isFormula.current && !formulaWithoutEqualSymbol); + isFormula.current = Boolean(formulaWithoutEqualSymbol); + }, [highligh, formulaWithoutEqualSymbol, isFocus]); useVerify(isFocus, onVerify, formulaText); const focus = useFocus(editor); @@ -208,7 +209,7 @@ export function FormulaEditor(props: IFormulaEditorProps) { const { searchList, searchText, handlerFormulaReplace, reset: resetFormulaSearch } = useFormulaSearch(isFocus, sequenceNodes, editor); const { functionInfo, paramIndex, reset } = useFormulaDescribe(isFocus, formulaText, editor); - useFirstHighlightDoc(formulaWithoutEqualSymbol, '=', isFocus, highlightDoc, highlightSheet, editor); + // useFirstHighlightDoc(formulaWithoutEqualSymbol, '=', isFocus, highlightDoc, highlightSheet, editor); useLayoutEffect(() => { let dispose: IDisposable; diff --git a/packages/sheets-formula-ui/src/views/range-selector/hooks/useHighlight.ts b/packages/sheets-formula-ui/src/views/range-selector/hooks/useHighlight.ts index 5659f7fd9cf..2f9e78a9c3e 100644 --- a/packages/sheets-formula-ui/src/views/range-selector/hooks/useHighlight.ts +++ b/packages/sheets-formula-ui/src/views/range-selector/hooks/useHighlight.ts @@ -103,7 +103,7 @@ export function useDocHight(_leadingCharacter: string = '') { const colorMap = useColor(); const leadingCharacterLength = useMemo(() => _leadingCharacter.length, [_leadingCharacter]); - const highlightDoc = useCallback((editor: Editor, sequenceNodes: INode[], isNeedResetSelection = true) => { + const highlightDoc = useCallback((editor: Editor, sequenceNodes: INode[], isNeedResetSelection = true, clearTextRun = true) => { const data = editor.getDocumentData(); if (!data) { return []; @@ -114,9 +114,11 @@ export function useDocHight(_leadingCharacter: string = '') { } const cloneBody = { dataStream: '', ...data.body }; if (sequenceNodes == null || sequenceNodes.length === 0) { - // cloneBody.textRuns = []; - // const cloneData = { ...data, body: cloneBody }; - // editor.setDocumentData(cloneData); + if (clearTextRun) { + cloneBody.textRuns = []; + const cloneData = { ...data, body: cloneBody }; + editor.setDocumentData(cloneData); + } return []; } else { const { textRuns, refSelections } = buildTextRuns(descriptionService, colorMap, sequenceNodes); diff --git a/packages/sheets-formula-ui/src/views/range-selector/hooks/useLeftAndRightArrow.ts b/packages/sheets-formula-ui/src/views/range-selector/hooks/useLeftAndRightArrow.ts index 4faf597302a..78253e22aa4 100644 --- a/packages/sheets-formula-ui/src/views/range-selector/hooks/useLeftAndRightArrow.ts +++ b/packages/sheets-formula-ui/src/views/range-selector/hooks/useLeftAndRightArrow.ts @@ -80,19 +80,23 @@ export const useLeftAndRightArrow = (isNeed: boolean, shouldMoveSelection: boole commandService.executeCommand(MoveSelectionCommand.id, { direction, jumpOver: JumpOver.moveGap, + extra: 'formula-editor', }); } else if (metaKey === MetaKeys.SHIFT) { commandService.executeCommand(ExpandSelectionCommand.id, { direction, + extra: 'formula-editor', }); } else if (metaKey === (MetaKeys.CTRL_COMMAND | MetaKeys.SHIFT)) { commandService.executeCommand(ExpandSelectionCommand.id, { direction, jumpOver: JumpOver.moveGap, + extra: 'formula-editor', }); } else { commandService.executeCommand(MoveSelectionCommand.id, { direction, + extra: 'formula-editor', }); } } else { diff --git a/packages/sheets-ui/src/commands/commands/set-selection.command.ts b/packages/sheets-ui/src/commands/commands/set-selection.command.ts index f97b5ab28fd..d61e7e5a562 100644 --- a/packages/sheets-ui/src/commands/commands/set-selection.command.ts +++ b/packages/sheets-ui/src/commands/commands/set-selection.command.ts @@ -54,11 +54,13 @@ export interface IMoveSelectionCommandParams { direction: Direction; jumpOver?: JumpOver; nextStep?: number; + extra?: string; } export interface IMoveSelectionEnterAndTabCommandParams { direction: Direction; keycode: KeyCode; + extra?: string; } /** @@ -81,7 +83,7 @@ export const MoveSelectionCommand: ICommand = { return false; } - const { direction, jumpOver } = params; + const { direction, jumpOver, extra } = params; const { range, primary } = selection; const startRange = getStartRange(range, primary, direction); @@ -125,7 +127,7 @@ export const MoveSelectionCommand: ICommand = { unitId: workbook.getUnitId(), subUnitId: worksheet.getSheetId(), selections, - trigger: MoveSelectionCommand.id, + extra, }); const renderManagerService = accessor.get(IRenderManagerService); @@ -163,7 +165,7 @@ export const MoveSelectionEnterAndTabCommand: ICommand = { if (!selection) return false; const { range: startRange, primary } = selection; - const { jumpOver, direction } = params; + const { jumpOver, direction, extra } = params; const isShrink = checkIfShrink(selection, direction, worksheet); const destRange = !isShrink @@ -334,7 +337,7 @@ export const ExpandSelectionCommand: ICommand = { primary, // this remains unchanged }, ], - trigger: ExpandSelectionCommand.id, + extra, }); }, }; diff --git a/packages/sheets/src/commands/operations/selection.operation.ts b/packages/sheets/src/commands/operations/selection.operation.ts index 6d438921382..c6ffa7e70cc 100644 --- a/packages/sheets/src/commands/operations/selection.operation.ts +++ b/packages/sheets/src/commands/operations/selection.operation.ts @@ -28,7 +28,7 @@ export interface ISetSelectionsOperationParams { /** If should scroll to the selected range. */ reveal?: boolean; - trigger?: string; + extra?: string; } /** From 0b8b5915126791398b6d8ffefc258e4f4b7ccf3b Mon Sep 17 00:00:00 2001 From: zhangw Date: Sat, 30 Nov 2024 11:17:11 +0800 Subject: [PATCH 018/134] feat: update --- .../sheets-formula-ui/src/views/formula-editor/index.tsx | 5 ++++- .../sheets-ui/src/views/editor-container/EditorContainer.tsx | 1 + packages/sheets-ui/src/views/formula-bar/FormulaBar.tsx | 1 + 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/sheets-formula-ui/src/views/formula-editor/index.tsx b/packages/sheets-formula-ui/src/views/formula-editor/index.tsx index e990c8e07f0..e00c0c3afbc 100644 --- a/packages/sheets-formula-ui/src/views/formula-editor/index.tsx +++ b/packages/sheets-formula-ui/src/views/formula-editor/index.tsx @@ -66,6 +66,7 @@ export interface IFormulaEditorProps { onFormulaSelectingChange?: (isSelecting: FormulaSelectingType) => void; keyboradEventConfig?: IKeyboardEventConfig; onMoveInEditor?: (keyCode: KeyCode, metaKey?: MetaKeys) => void; + modifyRangeByPointer?: boolean; } const noop = () => { }; @@ -88,6 +89,7 @@ export function FormulaEditor(props: IFormulaEditorProps) { onFormulaSelectingChange: propOnFormulaSelectingChange, keyboradEventConfig, onMoveInEditor, + modifyRangeByPointer = true, } = props; const editorService = useDependency(IEditorService); @@ -179,7 +181,8 @@ export function FormulaEditor(props: IFormulaEditorProps) { }, [_isFocus, focus]); const { checkScrollBar } = useResize(editor); - useRefactorEffect(isFocus, Boolean(isSelecting), unitId); + const shouldUseRefSelection = modifyRangeByPointer ? Boolean(isSelecting) : isSelecting === FormulaSelectingType.NEED_ADD; + useRefactorEffect(isFocus, shouldUseRefSelection, unitId); useLeftAndRightArrow(isFocus && moveCursor, shouldMoveRefSelection, editor, onMoveInEditor); const handleSelectionChange = useEvent((refString: string, offset: number, isEnd: boolean) => { diff --git a/packages/sheets-ui/src/views/editor-container/EditorContainer.tsx b/packages/sheets-ui/src/views/editor-container/EditorContainer.tsx index 74b32840cd7..be4e6fdf275 100644 --- a/packages/sheets-ui/src/views/editor-container/EditorContainer.tsx +++ b/packages/sheets-ui/src/views/editor-container/EditorContainer.tsx @@ -155,6 +155,7 @@ export const EditorContainer: React.FC = () => { editorBridgeService.disableForceKeepVisible(); } }} + modifyRangeByPointer={false} /> )}
diff --git a/packages/sheets-ui/src/views/formula-bar/FormulaBar.tsx b/packages/sheets-ui/src/views/formula-bar/FormulaBar.tsx index accfde9d795..2803b469c72 100644 --- a/packages/sheets-ui/src/views/formula-bar/FormulaBar.tsx +++ b/packages/sheets-ui/src/views/formula-bar/FormulaBar.tsx @@ -214,6 +214,7 @@ export function FormulaBar() { unitId={editState?.unitId} subUnitId={editState?.sheetId} keyboradEventConfig={keyCodeConfig} + modifyRangeByPointer={false} onFormulaSelectingChange={(isSelecting: 0 | 1 | 2) => { isRefSelecting.current = isSelecting; if (isSelecting === 1) { From c64296e68c897fffc90b144d16df1cea91015711 Mon Sep 17 00:00:00 2001 From: zhangw Date: Sat, 30 Nov 2024 11:40:13 +0800 Subject: [PATCH 019/134] feat: update --- .../sheets-formula-ui/src/views/formula-editor/index.tsx | 5 +---- .../sheets-ui/src/views/editor-container/EditorContainer.tsx | 3 +-- packages/sheets-ui/src/views/formula-bar/FormulaBar.tsx | 3 +-- 3 files changed, 3 insertions(+), 8 deletions(-) diff --git a/packages/sheets-formula-ui/src/views/formula-editor/index.tsx b/packages/sheets-formula-ui/src/views/formula-editor/index.tsx index e00c0c3afbc..e990c8e07f0 100644 --- a/packages/sheets-formula-ui/src/views/formula-editor/index.tsx +++ b/packages/sheets-formula-ui/src/views/formula-editor/index.tsx @@ -66,7 +66,6 @@ export interface IFormulaEditorProps { onFormulaSelectingChange?: (isSelecting: FormulaSelectingType) => void; keyboradEventConfig?: IKeyboardEventConfig; onMoveInEditor?: (keyCode: KeyCode, metaKey?: MetaKeys) => void; - modifyRangeByPointer?: boolean; } const noop = () => { }; @@ -89,7 +88,6 @@ export function FormulaEditor(props: IFormulaEditorProps) { onFormulaSelectingChange: propOnFormulaSelectingChange, keyboradEventConfig, onMoveInEditor, - modifyRangeByPointer = true, } = props; const editorService = useDependency(IEditorService); @@ -181,8 +179,7 @@ export function FormulaEditor(props: IFormulaEditorProps) { }, [_isFocus, focus]); const { checkScrollBar } = useResize(editor); - const shouldUseRefSelection = modifyRangeByPointer ? Boolean(isSelecting) : isSelecting === FormulaSelectingType.NEED_ADD; - useRefactorEffect(isFocus, shouldUseRefSelection, unitId); + useRefactorEffect(isFocus, Boolean(isSelecting), unitId); useLeftAndRightArrow(isFocus && moveCursor, shouldMoveRefSelection, editor, onMoveInEditor); const handleSelectionChange = useEvent((refString: string, offset: number, isEnd: boolean) => { diff --git a/packages/sheets-ui/src/views/editor-container/EditorContainer.tsx b/packages/sheets-ui/src/views/editor-container/EditorContainer.tsx index be4e6fdf275..7345ed630e3 100644 --- a/packages/sheets-ui/src/views/editor-container/EditorContainer.tsx +++ b/packages/sheets-ui/src/views/editor-container/EditorContainer.tsx @@ -149,13 +149,12 @@ export const EditorContainer: React.FC = () => { onMoveInEditor={onMoveInEditor} onFormulaSelectingChange={(isSelecting: 0 | 1 | 2) => { isRefSelecting.current = isSelecting; - if (isSelecting === 1) { + if (isSelecting) { editorBridgeService.enableForceKeepVisible(); } else { editorBridgeService.disableForceKeepVisible(); } }} - modifyRangeByPointer={false} /> )}
diff --git a/packages/sheets-ui/src/views/formula-bar/FormulaBar.tsx b/packages/sheets-ui/src/views/formula-bar/FormulaBar.tsx index 2803b469c72..b252325fef3 100644 --- a/packages/sheets-ui/src/views/formula-bar/FormulaBar.tsx +++ b/packages/sheets-ui/src/views/formula-bar/FormulaBar.tsx @@ -214,10 +214,9 @@ export function FormulaBar() { unitId={editState?.unitId} subUnitId={editState?.sheetId} keyboradEventConfig={keyCodeConfig} - modifyRangeByPointer={false} onFormulaSelectingChange={(isSelecting: 0 | 1 | 2) => { isRefSelecting.current = isSelecting; - if (isSelecting === 1) { + if (isSelecting) { editorBridgeService.enableForceKeepVisible(); } else { editorBridgeService.disableForceKeepVisible(); From af8612537ff853bb221660a1e9da492a797b57a7 Mon Sep 17 00:00:00 2001 From: zhangw Date: Sat, 30 Nov 2024 14:40:15 +0800 Subject: [PATCH 020/134] feat: update --- .../formula-editor/hooks/useSheetSelectionChange.ts | 12 +++++++----- .../controllers/editor/editing.render-controller.ts | 10 ++++++---- .../src/views/editor-container/EditorContainer.tsx | 1 + .../sheets-ui/src/views/editor-container/hooks.ts | 9 +-------- .../sheets-ui/src/views/formula-bar/FormulaBar.tsx | 1 + 5 files changed, 16 insertions(+), 17 deletions(-) diff --git a/packages/sheets-formula-ui/src/views/formula-editor/hooks/useSheetSelectionChange.ts b/packages/sheets-formula-ui/src/views/formula-editor/hooks/useSheetSelectionChange.ts index b7acb160ee7..d5fa6a73410 100644 --- a/packages/sheets-formula-ui/src/views/formula-editor/hooks/useSheetSelectionChange.ts +++ b/packages/sheets-formula-ui/src/views/formula-editor/hooks/useSheetSelectionChange.ts @@ -288,25 +288,27 @@ export const useSheetSelectionChange = ( if (params.extra !== 'formula-editor') { return; } - const { unitId, subUnitId, selections } = params; + const { selections } = params; if (selections.length) { const last = selections[selections.length - 1]; if (last) { const range = last.range; - const sheetId = range.sheetId ?? subUnitId; + const sheetId = subUnitId; const unitRangeName = { range, - unitId: range.unitId ?? unitId, - sheetName: getSheetNameById(sheetId), + unitId: params.unitId === unitId ? '' : params.unitId, + sheetName: params.subUnitId === sheetId ? '' : getSheetNameById(sheetId), }; const sequenceNodes = [...sequenceNodesRef.current]; const refRanges = unitRangesToText([unitRangeName], isSupportAcrossSheet); const result = refRanges[0]; - const lastNode = sequenceNodes[sequenceNodes.length - 1]; + let lastNode = sequenceNodes[sequenceNodes.length - 1]; if (typeof lastNode === 'object' && lastNode.nodeType === sequenceNodeType.REFERENCE) { + lastNode = { ...lastNode }; lastNode.token = result; lastNode.endIndex = lastNode.startIndex + result.length; + sequenceNodes[sequenceNodes.length - 1] = lastNode; const refStr = sequenceNodeToText(sequenceNodes); handleRangeChange(refStr, getOffsetFromSequenceNodes(sequenceNodes), true); } else { 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 aba83ca71dc..b1993725d2a 100644 --- a/packages/sheets-ui/src/controllers/editor/editing.render-controller.ts +++ b/packages/sheets-ui/src/controllers/editor/editing.render-controller.ts @@ -46,7 +46,7 @@ import { DocSkeletonManagerService, RichTextEditingMutation, } from '@univerjs/docs'; -import { VIEWPORT_KEY as DOC_VIEWPORT_KEY, DocSelectionRenderService, IEditorService, MoveCursorOperation, MoveSelectionOperation } from '@univerjs/docs-ui'; +import { VIEWPORT_KEY as DOC_VIEWPORT_KEY, DocSelectionRenderService, IEditorService, MoveCursorOperation, MoveSelectionOperation, ReplaceSnapshotCommand } from '@univerjs/docs-ui'; import { IFunctionService, LexerTreeBuilder, matchToken } from '@univerjs/engine-formula'; import { DEFAULT_TEXT_FORMAT } from '@univerjs/engine-numfmt'; @@ -246,7 +246,6 @@ export class EditingRenderController extends Disposable implements IRenderModule if (editCellState == null || this._editorBridgeService.isForceKeepVisible()) { return; } - const state = this._editorBridgeService.getEditCellState(); if (state == null) { return; @@ -274,7 +273,10 @@ export class EditingRenderController extends Disposable implements IRenderModule documentModel!.updateDocumentDataPageSize((endX - startX) / scaleX); } - this._instanceSrv.changeDoc(editorUnitId, documentModel!); + this._commandService.syncExecuteCommand(ReplaceSnapshotCommand.id, { + unitId: editorUnitId, + snapshot: Tools.deepClone(documentModel!.getSnapshot()), + }); this._contextService.setContextValue(FOCUSING_EDITOR_BUT_HIDDEN, true); this._textSelectionManagerService.replaceTextRanges([{ startOffset: 0, @@ -497,7 +499,7 @@ export class EditingRenderController extends Disposable implements IRenderModule }); } - const documentDataModel = editCellState.documentLayoutObject.documentModel; + const documentDataModel = this._univerInstanceService.getUnit(DOCS_NORMAL_EDITOR_UNIT_ID_KEY); if (documentDataModel) { await this._submitCellData(documentDataModel); diff --git a/packages/sheets-ui/src/views/editor-container/EditorContainer.tsx b/packages/sheets-ui/src/views/editor-container/EditorContainer.tsx index 7345ed630e3..762a2129291 100644 --- a/packages/sheets-ui/src/views/editor-container/EditorContainer.tsx +++ b/packages/sheets-ui/src/views/editor-container/EditorContainer.tsx @@ -147,6 +147,7 @@ export const EditorContainer: React.FC = () => { subUnitId={editState?.sheetId} keyboradEventConfig={keyCodeConfig} onMoveInEditor={onMoveInEditor} + isSupportAcrossSheet onFormulaSelectingChange={(isSelecting: 0 | 1 | 2) => { isRefSelecting.current = isSelecting; if (isSelecting) { diff --git a/packages/sheets-ui/src/views/editor-container/hooks.ts b/packages/sheets-ui/src/views/editor-container/hooks.ts index 5ff62b11b81..95ed3f5e0ef 100644 --- a/packages/sheets-ui/src/views/editor-container/hooks.ts +++ b/packages/sheets-ui/src/views/editor-container/hooks.ts @@ -20,14 +20,6 @@ import { KeyCode } from '@univerjs/ui'; import { useMemo } from 'react'; import { IEditorBridgeService } from '../../services/editor-bridge.service'; -// commandService.executeCommand(SetCellEditVisibleArrowOperation.id, { -// keycode, -// visible: false, -// eventType: DeviceInputEventType.Keyboard, -// isShift: metaKey === MetaKeys.SHIFT || metaKey === (MetaKeys.CTRL_COMMAND | MetaKeys.SHIFT), -// unitId, -// }); - export function useKeyEventConfig(isRefSelecting: React.MutableRefObject<0 | 1 | 2>, unitId: string) { const editorBridgeService = useDependency(IEditorBridgeService); @@ -39,6 +31,7 @@ export function useKeyEventConfig(isRefSelecting: React.MutableRefObject<0 | 1 | ], handler: (keycode: KeyCode) => { if (keycode === KeyCode.ENTER || keycode === KeyCode.ESC || keycode === KeyCode.TAB) { + editorBridgeService.disableForceKeepVisible(); editorBridgeService.changeVisible({ visible: false, eventType: DeviceInputEventType.Keyboard, diff --git a/packages/sheets-ui/src/views/formula-bar/FormulaBar.tsx b/packages/sheets-ui/src/views/formula-bar/FormulaBar.tsx index b252325fef3..d146da4ff72 100644 --- a/packages/sheets-ui/src/views/formula-bar/FormulaBar.tsx +++ b/packages/sheets-ui/src/views/formula-bar/FormulaBar.tsx @@ -213,6 +213,7 @@ export function FormulaBar() { className={styles.formulaContent} unitId={editState?.unitId} subUnitId={editState?.sheetId} + isSupportAcrossSheet keyboradEventConfig={keyCodeConfig} onFormulaSelectingChange={(isSelecting: 0 | 1 | 2) => { isRefSelecting.current = isSelecting; From d78902c88c0928cb04bcd8627d7f7c2660e87bca Mon Sep 17 00:00:00 2001 From: zhangw Date: Sat, 30 Nov 2024 14:44:10 +0800 Subject: [PATCH 021/134] feat: update --- .../docs-ui/src/commands/commands/replace-content.command.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 ecbe96c5a85..f10b8744488 100644 --- a/packages/docs-ui/src/commands/commands/replace-content.command.ts +++ b/packages/docs-ui/src/commands/commands/replace-content.command.ts @@ -33,7 +33,7 @@ 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) => { + handler: (accessor, params: IReplaceSnapshotCommandParams) => { const { unitId, snapshot, textRanges, segmentId = '', options } = params; const univerInstanceService = accessor.get(IUniverInstanceService); const commandService = accessor.get(ICommandService); From 0b7cfdc718cc6ff4bd8a744d008b9737045764e3 Mon Sep 17 00:00:00 2001 From: zhangw Date: Sat, 30 Nov 2024 14:46:40 +0800 Subject: [PATCH 022/134] feat: update --- .../render-controllers/scroll.render-controller.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/sheets-ui/src/controllers/render-controllers/scroll.render-controller.ts b/packages/sheets-ui/src/controllers/render-controllers/scroll.render-controller.ts index 6e67d0001b1..251b81b7de1 100644 --- a/packages/sheets-ui/src/controllers/render-controllers/scroll.render-controller.ts +++ b/packages/sheets-ui/src/controllers/render-controllers/scroll.render-controller.ts @@ -421,9 +421,9 @@ export class SheetsScrollRenderController extends Disposable implements IRenderM const selection = this._getSelectionsService().getCurrentLastSelection(); if (!selection) return; - const { startRow, startColumn, actualRow, actualColumn } = selection.primary; - const selectionStartRow = targetIsActualRowAndColumn ? actualRow : startRow; - const selectionStartColumn = targetIsActualRowAndColumn ? actualColumn : startColumn; + const { startRow, startColumn, actualRow, actualColumn } = selection.primary ?? selection.range; + const selectionStartRow = targetIsActualRowAndColumn ? actualRow ?? startRow : startRow; + const selectionStartColumn = targetIsActualRowAndColumn ? actualColumn ?? startColumn : startColumn; this._scrollToCell(selectionStartRow, selectionStartColumn); } From dfd394aee759c7769bd40c12cc067a4d237d9b10 Mon Sep 17 00:00:00 2001 From: zhangw Date: Sat, 30 Nov 2024 14:58:39 +0800 Subject: [PATCH 023/134] feat: update --- .../src/views/formula-editor/hooks/useFormulaSelection.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/sheets-formula-ui/src/views/formula-editor/hooks/useFormulaSelection.ts b/packages/sheets-formula-ui/src/views/formula-editor/hooks/useFormulaSelection.ts index b6d836845fe..3c1ba785735 100644 --- a/packages/sheets-formula-ui/src/views/formula-editor/hooks/useFormulaSelection.ts +++ b/packages/sheets-formula-ui/src/views/formula-editor/hooks/useFormulaSelection.ts @@ -79,8 +79,8 @@ export function useFormulaSelecting(editorId: string, nodes: (string | ISequence const activeRange = docSelectionRenderService?.getActiveTextRange(); const index = activeRange?.collapsed ? activeRange.startOffset! : -1; const lastNode = nodesRef.current[nodesRef.current.length - 1]; - const isFocusingLastNode = typeof lastNode === 'object' && lastNode.nodeType === sequenceNodeType.REFERENCE && lastNode.startIndex <= index - 1 && lastNode.endIndex >= index - 2; const dataStream = getCurrentBodyDataStreamAndOffset(injector)?.dataStream; + const isFocusingLastNode = typeof lastNode === 'object' && lastNode.nodeType === sequenceNodeType.REFERENCE && index === (dataStream?.length ?? 2) - 2; if (dataStream?.substring(0, 1) === '=' && ((char && matchRefDrawToken(char)) || isFocusingLastNode)) { if (isFocusingLastNode) { setIsSelecting(FormulaSelectingType.CAN_EDIT); From 60f1311cd9ae1136be5b282db848e42fb33ac005 Mon Sep 17 00:00:00 2001 From: zhangw Date: Sat, 30 Nov 2024 17:25:37 +0800 Subject: [PATCH 024/134] feat: update --- .../sheets-formula-ui/src/views/formula-editor/index.tsx | 8 ++++++-- .../src/views/editor-container/EditorContainer.tsx | 1 + packages/sheets-ui/src/views/formula-bar/FormulaBar.tsx | 1 + 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/sheets-formula-ui/src/views/formula-editor/index.tsx b/packages/sheets-formula-ui/src/views/formula-editor/index.tsx index e990c8e07f0..9fd98161880 100644 --- a/packages/sheets-formula-ui/src/views/formula-editor/index.tsx +++ b/packages/sheets-formula-ui/src/views/formula-editor/index.tsx @@ -66,6 +66,7 @@ export interface IFormulaEditorProps { onFormulaSelectingChange?: (isSelecting: FormulaSelectingType) => void; keyboradEventConfig?: IKeyboardEventConfig; onMoveInEditor?: (keyCode: KeyCode, metaKey?: MetaKeys) => void; + resetSelectionOnBlur?: boolean; } const noop = () => { }; @@ -88,6 +89,7 @@ export function FormulaEditor(props: IFormulaEditorProps) { onFormulaSelectingChange: propOnFormulaSelectingChange, keyboradEventConfig, onMoveInEditor, + resetSelectionOnBlur = true, } = props; const editorService = useDependency(IEditorService); @@ -173,10 +175,12 @@ export function FormulaEditor(props: IFormulaEditorProps) { clearTimeout(time); }; } else { - resetSelection(); + if (resetSelectionOnBlur) { + resetSelection(); + } isFocusSet(_isFocus); } - }, [_isFocus, focus]); + }, [_isFocus, focus, resetSelectionOnBlur]); const { checkScrollBar } = useResize(editor); useRefactorEffect(isFocus, Boolean(isSelecting), unitId); diff --git a/packages/sheets-ui/src/views/editor-container/EditorContainer.tsx b/packages/sheets-ui/src/views/editor-container/EditorContainer.tsx index 762a2129291..bb818a47f94 100644 --- a/packages/sheets-ui/src/views/editor-container/EditorContainer.tsx +++ b/packages/sheets-ui/src/views/editor-container/EditorContainer.tsx @@ -148,6 +148,7 @@ export const EditorContainer: React.FC = () => { keyboradEventConfig={keyCodeConfig} onMoveInEditor={onMoveInEditor} isSupportAcrossSheet + resetSelectionOnBlur={false} onFormulaSelectingChange={(isSelecting: 0 | 1 | 2) => { isRefSelecting.current = isSelecting; if (isSelecting) { diff --git a/packages/sheets-ui/src/views/formula-bar/FormulaBar.tsx b/packages/sheets-ui/src/views/formula-bar/FormulaBar.tsx index d146da4ff72..b1302041a0e 100644 --- a/packages/sheets-ui/src/views/formula-bar/FormulaBar.tsx +++ b/packages/sheets-ui/src/views/formula-bar/FormulaBar.tsx @@ -214,6 +214,7 @@ export function FormulaBar() { unitId={editState?.unitId} subUnitId={editState?.sheetId} isSupportAcrossSheet + resetSelectionOnBlur={false} keyboradEventConfig={keyCodeConfig} onFormulaSelectingChange={(isSelecting: 0 | 1 | 2) => { isRefSelecting.current = isSelecting; From a4bcc4d7f43f7f08ff69359107305838e468ef3f Mon Sep 17 00:00:00 2001 From: zhangw Date: Sat, 30 Nov 2024 18:02:44 +0800 Subject: [PATCH 025/134] feat: update --- .../views/formula-editor/hooks/useSheetSelectionChange.ts | 8 ++++---- .../src/views/range-selector/utils/unitRangesToText.ts | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/sheets-formula-ui/src/views/formula-editor/hooks/useSheetSelectionChange.ts b/packages/sheets-formula-ui/src/views/formula-editor/hooks/useSheetSelectionChange.ts index d5fa6a73410..99737a44884 100644 --- a/packages/sheets-formula-ui/src/views/formula-editor/hooks/useSheetSelectionChange.ts +++ b/packages/sheets-formula-ui/src/views/formula-editor/hooks/useSheetSelectionChange.ts @@ -98,7 +98,7 @@ export const useSheetSelectionChange = ( sheetName: getSheetNameById(rangeSheetId), }; const isAcrossSheet = rangeSheetId !== subUnitId; - const refRanges = unitRangesToText([unitRangeName], isSupportAcrossSheet && isAcrossSheet); + const refRanges = unitRangesToText([unitRangeName], isSupportAcrossSheet && isAcrossSheet, sheetName); sequenceNodes.push({ token: refRanges[0], nodeType: sequenceNodeType.REFERENCE } as any); const newSequenceNodes = [...sequenceNodes, ...lastNodes]; const result = sequenceNodeToText(newSequenceNodes); @@ -147,7 +147,7 @@ export const useSheetSelectionChange = ( unitId: selection.rangeWithCoord.unitId ?? unitId, sheetName: getSheetNameById(rangeSheetId), }; - const refRanges = unitRangesToText([unitRangeName], isSupportAcrossSheet); + const refRanges = unitRangesToText([unitRangeName], isSupportAcrossSheet, sheetName); return refRanges[0]; } return item.token; @@ -162,7 +162,7 @@ export const useSheetSelectionChange = ( sheetName: getSheetNameById(rangeSheetId), }; const isAcrossSheet = rangeSheetId !== subUnitId; - const refRanges = unitRangesToText([unitRangeName], isSupportAcrossSheet && isAcrossSheet); + const refRanges = unitRangesToText([unitRangeName], isSupportAcrossSheet && isAcrossSheet, sheetName); theLastList.push(refRanges[0]); } const preNode = sequenceNodes[sequenceNodes.length - 1]; @@ -301,7 +301,7 @@ export const useSheetSelectionChange = ( sheetName: params.subUnitId === sheetId ? '' : getSheetNameById(sheetId), }; const sequenceNodes = [...sequenceNodesRef.current]; - const refRanges = unitRangesToText([unitRangeName], isSupportAcrossSheet); + const refRanges = unitRangesToText([unitRangeName], isSupportAcrossSheet, sheetName); const result = refRanges[0]; let lastNode = sequenceNodes[sequenceNodes.length - 1]; if (typeof lastNode === 'object' && lastNode.nodeType === sequenceNodeType.REFERENCE) { diff --git a/packages/sheets-formula-ui/src/views/range-selector/utils/unitRangesToText.ts b/packages/sheets-formula-ui/src/views/range-selector/utils/unitRangesToText.ts index 3a46145ef82..96fc0445680 100644 --- a/packages/sheets-formula-ui/src/views/range-selector/utils/unitRangesToText.ts +++ b/packages/sheets-formula-ui/src/views/range-selector/utils/unitRangesToText.ts @@ -24,12 +24,12 @@ export function getSheetNameById(univerInstanceService: IUniverInstanceService, return univerInstanceService.getUnit(unitId)?.getSheetBySheetId(sheetId)?.getName() || ''; } -export const unitRangesToText = (ranges: IUnitRangeName[], isNeedSheetName: boolean = false) => { +export const unitRangesToText = (ranges: IUnitRangeName[], isNeedSheetName: boolean = false, originSheetName = '') => { if (!isNeedSheetName) { return ranges.map((item) => serializeRange(item.range)); } else { return ranges.map((item) => { - if (item.sheetName !== '') { + if (item.sheetName !== '' && item.sheetName !== originSheetName) { return serializeRangeWithSheet(item.sheetName, item.range); } return serializeRange(item.range); From 9e7c359f1d833a266d64de8b23abfd2b865b89cd Mon Sep 17 00:00:00 2001 From: zhangw Date: Mon, 2 Dec 2024 19:33:38 +0800 Subject: [PATCH 026/134] feat: update --- .../data-model/text-x/build-utils/index.ts | 3 +- .../text-x/build-utils/text-x-utils.ts | 74 +++++++++++++++++++ .../src/services/undoredo/undoredo.service.ts | 8 +- .../commands/replace-content.command.ts | 53 +++++++++++++ packages/docs-ui/src/docs-ui-plugin.ts | 5 +- packages/docs-ui/src/index.ts | 2 +- .../docs-ui/src/services/editor/editor.ts | 2 +- .../selection/doc-selection-render.service.ts | 7 +- .../range-selector/hooks/useHighlight.ts | 24 ++++-- .../editor-container/EditorContainer.tsx | 3 +- .../src/views/editor-container/hooks.ts | 21 +++++- 11 files changed, 182 insertions(+), 20 deletions(-) diff --git a/packages/core/src/docs/data-model/text-x/build-utils/index.ts b/packages/core/src/docs/data-model/text-x/build-utils/index.ts index 5e6664d7eba..9a92ddf858c 100644 --- a/packages/core/src/docs/data-model/text-x/build-utils/index.ts +++ b/packages/core/src/docs/data-model/text-x/build-utils/index.ts @@ -19,7 +19,7 @@ import { copyCustomRange, getCustomRangesInterestsWithSelection, isIntersecting import { changeParagraphBulletNestLevel, setParagraphBullet, switchParagraphBullet, toggleChecklistParagraph } from './paragraph'; import { fromPlainText, getPlainText, isEmptyDocument } from './parse'; import { isSegmentIntersects, makeSelection, normalizeSelection } from './selection'; -import { addCustomRangeTextX, deleteCustomRangeTextX, deleteSelectionTextX, replaceSelectionTextX } from './text-x-utils'; +import { addCustomRangeTextX, deleteCustomRangeTextX, deleteSelectionTextX, replaceSelectionTextRuns, replaceSelectionTextX } from './text-x-utils'; export class BuildTextUtils { static customRange = { @@ -40,6 +40,7 @@ export class BuildTextUtils { makeSelection, normalizeSelection, delete: deleteSelectionTextX, + replaceTextRuns: replaceSelectionTextRuns, }; static range = { diff --git a/packages/core/src/docs/data-model/text-x/build-utils/text-x-utils.ts b/packages/core/src/docs/data-model/text-x/build-utils/text-x-utils.ts index f28ce073289..acc5267d4c0 100644 --- a/packages/core/src/docs/data-model/text-x/build-utils/text-x-utils.ts +++ b/packages/core/src/docs/data-model/text-x/build-utils/text-x-utils.ts @@ -293,6 +293,80 @@ export const replaceSelectionTextX = (params: IReplaceSelectionTextXParams) => { } }); + if (actions.every((action) => action.t === TextXActionType.RETAIN && !action.body)) { + return false; + } + + const textX = new TextX(); + textX.push({ + t: TextXActionType.RETAIN, + len: selection.startOffset, + }); + textX.push(...actions); + return textX; +}; + +function isTextRunsEqual(body: IDocumentBody, oldBody: IDocumentBody) { + const { textRuns } = body; + const { textRuns: oldTextRuns } = oldBody; + + if (textRuns?.length === oldTextRuns?.length && textRuns?.every((textRun, index) => JSON.stringify(textRun) === JSON.stringify(oldTextRuns?.[index]))) { + return true; + } + + return false; +} + +export const replaceSelectionTextRuns = (params: IReplaceSelectionTextXParams) => { + const { selection, body: insertBody, doc } = params; + const segmentId = selection.segmentId; + const body = doc.getSelfOrHeaderFooterModel(segmentId)?.getBody(); + if (!body) return false; + + const oldBody = selection.collapsed ? null : getBodySlice(body, selection.startOffset, selection.endOffset); + const diffs = textDiff(oldBody ? oldBody.dataStream : '', insertBody.dataStream); + let cursor = 0; + const actions = diffs.map(([type, text]) => { + switch (type) { + // retain + case 0: { + const sliceBody = getBodySlice(insertBody, cursor, cursor + text.length, false); + const oldBodySlice = getBodySlice(oldBody!, cursor, cursor + text.length, false); + + const action: TextXAction = { + t: TextXActionType.RETAIN, + body: isTextRunsEqual(sliceBody, oldBodySlice) + ? { + ...sliceBody, + dataStream: '', + } + : undefined, + len: text.length, + }; + cursor += text.length; + return action; + } + // insert + case 1: { + const action: TextXAction = { + t: TextXActionType.INSERT, + body: getBodySlice(insertBody, cursor, cursor + text.length), + len: text.length, + }; + cursor += text.length; + return action; + } + // delete + default: { + const action: TextXAction = { + t: TextXActionType.DELETE, + len: text.length, + }; + return action; + } + } + }); + const textX = new TextX(); textX.push({ t: TextXActionType.RETAIN, diff --git a/packages/core/src/services/undoredo/undoredo.service.ts b/packages/core/src/services/undoredo/undoredo.service.ts index 32c76475dc9..0f69d57ea1d 100644 --- a/packages/core/src/services/undoredo/undoredo.service.ts +++ b/packages/core/src/services/undoredo/undoredo.service.ts @@ -15,18 +15,18 @@ */ import type { Observable } from 'rxjs'; -import { BehaviorSubject } from 'rxjs'; import type { IAccessor, IDisposable } from '../../common/di'; -import { createIdentifier } from '../../common/di'; +import type { Nullable } from '../../shared/types'; +import type { ICommand, IMutationInfo } from '../command/command.service'; +import { BehaviorSubject } from 'rxjs'; import { DOCS_FORMULA_BAR_EDITOR_UNIT_ID_KEY, DOCS_NORMAL_EDITOR_UNIT_ID_KEY } from '../../common/const'; +import { createIdentifier } from '../../common/di'; import { Disposable, toDisposable } from '../../shared/lifecycle'; -import type { ICommand, IMutationInfo } from '../command/command.service'; import { CommandType, ICommandService, sequenceExecute } from '../command/command.service'; import { EDITOR_ACTIVATED, FOCUSING_FX_BAR_EDITOR, FOCUSING_SHEET } from '../context/context'; import { IContextService } from '../context/context.service'; import { IUniverInstanceService } from '../instance/instance.service'; -import type { Nullable } from '../../shared/types'; export interface IUndoRedoItem { /** unitID maps to unitId for UniverSheet / UniverDoc / UniverSlide */ 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 f10b8744488..d5cdaddedd0 100644 --- a/packages/docs-ui/src/commands/commands/replace-content.command.ts +++ b/packages/docs-ui/src/commands/commands/replace-content.command.ts @@ -340,3 +340,56 @@ export const ReplaceSelectionCommand: ICommand = return true; }, }; + +export const ReplaceTextRunsCommand: ICommand = { + id: 'doc.command.replace-text-runs', + type: CommandType.COMMAND, + + handler: (accessor, params: IReplaceContentCommandParams) => { + const { unitId, body, 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 prevBody = docDataModel?.getSelfOrHeaderFooterModel(segmentId).getSnapshot().body; + + if (docDataModel == null || prevBody == null) { + return false; + } + + const textX = BuildTextUtils.selection.replaceTextRuns({ + doc: docDataModel, + body, + selection: { + startOffset: 0, + endOffset: prevBody.dataStream.length - 2, + collapsed: false, + }, + }); + + if (!textX) { + return false; + } + + const doMutation = { + id: RichTextEditingMutation.id, + params: { + unitId, + actions: [], + textRanges, + noHistory: true, + } as IRichTextEditingMutationParams, + }; + const jsonX = JSONX.getInstance(); + const path = getRichTextEditPath(docDataModel, segmentId); + doMutation.params.actions = jsonX.editOp(textX.serialize(), path); + doMutation.params.textRanges = textRanges; + if (options) { + doMutation.params.options = options; + } + + const result = commandService.syncExecuteCommand(doMutation.id, doMutation.params); + return Boolean(result); + }, +}; diff --git a/packages/docs-ui/src/docs-ui-plugin.ts b/packages/docs-ui/src/docs-ui-plugin.ts index 00c8e5a06ac..784f5087177 100644 --- a/packages/docs-ui/src/docs-ui-plugin.ts +++ b/packages/docs-ui/src/docs-ui-plugin.ts @@ -43,7 +43,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, ReplaceSnapshotCommand } from './commands/commands/replace-content.command'; +import { CoverContentCommand, ReplaceContentCommand, ReplaceSnapshotCommand, ReplaceTextRunsCommand } from './commands/commands/replace-content.command'; import { SetDocZoomRatioCommand } from './commands/commands/set-doc-zoom-ratio.command'; import { SwitchDocModeCommand } from './commands/commands/switch-doc-mode.command'; import { CreateDocTableCommand } from './commands/commands/table/doc-table-create.command'; @@ -62,7 +62,6 @@ import { DocParagraphSettingController } from './controllers/doc-paragraph-setti import { DocTableController } from './controllers/doc-table.controller'; import { DocUIController } from './controllers/doc-ui.controller'; import { DocBackScrollRenderController } from './controllers/render-controllers/back-scroll.render-controller'; -import { DocRenderController } from './controllers/render-controllers/doc.render-controller'; import { DocChecklistRenderController } from './controllers/render-controllers/doc-checklist.render-controller'; import { DocClipboardController } from './controllers/render-controllers/doc-clipboard.controller'; import { DocContextMenuRenderController } from './controllers/render-controllers/doc-contextmenu.render-controller'; @@ -71,6 +70,7 @@ import { DocIMEInputController } from './controllers/render-controllers/doc-ime- import { DocInputController } from './controllers/render-controllers/doc-input.controller'; import { DocResizeRenderController } from './controllers/render-controllers/doc-resize.render-controller'; import { DocSelectionRenderController } from './controllers/render-controllers/doc-selection-render.controller'; +import { DocRenderController } from './controllers/render-controllers/doc.render-controller'; import { DocZoomRenderController } from './controllers/render-controllers/zoom.render-controller'; import { DocClipboardService, IDocClipboardService } from './services/clipboard/clipboard.service'; import { DocAutoFormatService } from './services/doc-auto-format.service'; @@ -215,6 +215,7 @@ export class UniverDocsUIPlugin extends Plugin { DocParagraphSettingPanelOperation, MoveCursorOperation, MoveSelectionOperation, + ReplaceTextRunsCommand, ].forEach((e) => { this._commandService.registerCommand(e); }); diff --git a/packages/docs-ui/src/index.ts b/packages/docs-ui/src/index.ts index 50aff6b1f6b..6e9c021bd03 100644 --- a/packages/docs-ui/src/index.ts +++ b/packages/docs-ui/src/index.ts @@ -112,7 +112,7 @@ export { AlignOperationCommand, AlignRightCommand, } from './commands/commands/paragraph-align.command'; -export { CoverContentCommand, ReplaceContentCommand, ReplaceSnapshotCommand } from './commands/commands/replace-content.command'; +export { CoverContentCommand, ReplaceContentCommand, ReplaceSnapshotCommand, ReplaceTextRunsCommand } 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/editor/editor.ts b/packages/docs-ui/src/services/editor/editor.ts index 5694bd35d68..5836342b96a 100644 --- a/packages/docs-ui/src/services/editor/editor.ts +++ b/packages/docs-ui/src/services/editor/editor.ts @@ -352,7 +352,7 @@ export class Editor extends Disposable implements IEditor { setDocumentData(data: IDocumentData, textRanges: Nullable) { const { id } = data; - this._commandService.executeCommand(ReplaceSnapshotCommand.id, { + this._commandService.syncExecuteCommand(ReplaceSnapshotCommand.id, { unitId: id, snapshot: data, textRanges, diff --git a/packages/docs-ui/src/services/selection/doc-selection-render.service.ts b/packages/docs-ui/src/services/selection/doc-selection-render.service.ts index 8c2989d53ff..5de37fc3db2 100644 --- a/packages/docs-ui/src/services/selection/doc-selection-render.service.ts +++ b/packages/docs-ui/src/services/selection/doc-selection-render.service.ts @@ -103,6 +103,10 @@ export class DocSelectionRenderService extends RxDisposable implements IRenderMo // When the user switches editors, whether to clear the doc ranges. private _reserveRanges = false; + get isFocusing() { + return this._input === document.activeElement || document.activeElement === document.body || document.activeElement === null; + } + constructor( private readonly _context: IRenderContext, @ILayoutService private readonly _layoutService: ILayoutService, @@ -305,12 +309,11 @@ export class DocSelectionRenderService extends RxDisposable implements IRenderMo * @deprecated */ activate(x: number, y: number, force = false) { - const isFocusing = this._input === document.activeElement || document.activeElement === document.body || document.activeElement === null; this._container.style.left = `${x}px`; this._container.style.top = `${y}px`; this._container.style.zIndex = '1000'; - if (isFocusing || force) { + if (this.isFocusing || force) { this.focus(); } } diff --git a/packages/sheets-formula-ui/src/views/range-selector/hooks/useHighlight.ts b/packages/sheets-formula-ui/src/views/range-selector/hooks/useHighlight.ts index 2f9e78a9c3e..24b05a548d4 100644 --- a/packages/sheets-formula-ui/src/views/range-selector/hooks/useHighlight.ts +++ b/packages/sheets-formula-ui/src/views/range-selector/hooks/useHighlight.ts @@ -19,7 +19,8 @@ import type { Editor } from '@univerjs/docs-ui'; import type { ISequenceNode } from '@univerjs/engine-formula'; import type { ISelectionWithStyle } from '@univerjs/sheets'; import type { INode } from './useFormulaToken'; -import { IUniverInstanceService, ThemeService, useDependency } from '@univerjs/core'; +import { ICommandService, IUniverInstanceService, ThemeService, useDependency } from '@univerjs/core'; +import { ReplaceTextRunsCommand } from '@univerjs/docs-ui'; import { deserializeRangeWithSheet, sequenceNodeType } from '@univerjs/engine-formula'; import { IRenderManagerService } from '@univerjs/engine-render'; import { IRefSelectionsService, setEndForRange } from '@univerjs/sheets'; @@ -101,10 +102,12 @@ export function useSheetHighlight(unitId: string) { export function useDocHight(_leadingCharacter: string = '') { const descriptionService = useDependency(IDescriptionService); const colorMap = useColor(); + const commandService = useDependency(ICommandService); const leadingCharacterLength = useMemo(() => _leadingCharacter.length, [_leadingCharacter]); const highlightDoc = useCallback((editor: Editor, sequenceNodes: INode[], isNeedResetSelection = true, clearTextRun = true) => { const data = editor.getDocumentData(); + const editorId = editor.getEditorId(); if (!data) { return []; } @@ -116,8 +119,12 @@ export function useDocHight(_leadingCharacter: string = '') { if (sequenceNodes == null || sequenceNodes.length === 0) { if (clearTextRun) { cloneBody.textRuns = []; - const cloneData = { ...data, body: cloneBody }; - editor.setDocumentData(cloneData); + // const cloneData = { ...data, body: cloneBody }; + // editor.setDocumentData(cloneData); + commandService.syncExecuteCommand(ReplaceTextRunsCommand.id, { + unitId: editorId, + body: cloneBody, + }); } return []; } else { @@ -148,11 +155,16 @@ export function useDocHight(_leadingCharacter: string = '') { }); } - const cloneData = { ...data, body: cloneBody }; - editor.setDocumentData(cloneData, selections); + // const cloneData = { ...data, body: cloneBody }; + // editor.setDocumentData(cloneData, selections); + commandService.syncExecuteCommand(ReplaceTextRunsCommand.id, { + unitId: editorId, + body: cloneBody, + textRanges: selections, + }); return refSelections; } - }, [descriptionService, colorMap, leadingCharacterLength, _leadingCharacter]); + }, [commandService, descriptionService, colorMap, leadingCharacterLength, _leadingCharacter]); return highlightDoc; } diff --git a/packages/sheets-ui/src/views/editor-container/EditorContainer.tsx b/packages/sheets-ui/src/views/editor-container/EditorContainer.tsx index bb818a47f94..5742a1afe94 100644 --- a/packages/sheets-ui/src/views/editor-container/EditorContainer.tsx +++ b/packages/sheets-ui/src/views/editor-container/EditorContainer.tsx @@ -24,7 +24,7 @@ import { SetCellEditVisibleArrowOperation } from '../../commands/operations/cell import { EMBEDDING_FORMULA_EDITOR_COMPONENT_KEY } from '../../common/keys'; import { IEditorBridgeService } from '../../services/editor-bridge.service'; import { ICellEditorManagerService } from '../../services/editor/cell-editor-manager.service'; -import { useKeyEventConfig } from './hooks'; +import { useIsFocusing, useKeyEventConfig } from './hooks'; import styles from './index.module.less'; interface ICellIEditorProps { } @@ -62,6 +62,7 @@ export const EditorContainer: React.FC = () => { ); const FormulaEditor = componentManager.get(EMBEDDING_FORMULA_EDITOR_COMPONENT_KEY); const editState = editorBridgeService.getEditLocation(); + const isFocusing = useIsFocusing(DOCS_NORMAL_EDITOR_UNIT_ID_KEY); useEffect(() => { const sub = cellEditorManagerService.state$.subscribe((param) => { diff --git a/packages/sheets-ui/src/views/editor-container/hooks.ts b/packages/sheets-ui/src/views/editor-container/hooks.ts index 95ed3f5e0ef..37ecbb871e0 100644 --- a/packages/sheets-ui/src/views/editor-container/hooks.ts +++ b/packages/sheets-ui/src/views/editor-container/hooks.ts @@ -14,8 +14,9 @@ * limitations under the License. */ -import { useDependency } from '@univerjs/core'; -import { DeviceInputEventType } from '@univerjs/engine-render'; +import { IUniverInstanceService, useDependency, useObservable } from '@univerjs/core'; +import { DocSelectionRenderService } from '@univerjs/docs-ui'; +import { DeviceInputEventType, IRenderManagerService } from '@univerjs/engine-render'; import { KeyCode } from '@univerjs/ui'; import { useMemo } from 'react'; import { IEditorBridgeService } from '../../services/editor-bridge.service'; @@ -44,3 +45,19 @@ export function useKeyEventConfig(isRefSelecting: React.MutableRefObject<0 | 1 | return keyCodeConfig; } + +export function useIsFocusing(editorId: string) { + const univerInstanceService = useDependency(IUniverInstanceService); + const renderManagerService = useDependency(IRenderManagerService); + const docSelectionRenderService = renderManagerService.getRenderById(editorId)?.with(DocSelectionRenderService); + useObservable(docSelectionRenderService?.onBlur$); + useObservable(docSelectionRenderService?.onFocus$); + + // useEffect(() => { + // if (docSelectionRenderService?.isFocusing) { + // univerInstanceService.focusUnit(editorId); + // } + // }, [docSelectionRenderService?.isFocusing, editorId, univerInstanceService]); + + return docSelectionRenderService?.isFocusing; +} From 0cf295904272ebb0b6f8d1fe0bbab5b0f798ff7d Mon Sep 17 00:00:00 2001 From: zhangw Date: Mon, 2 Dec 2024 20:31:51 +0800 Subject: [PATCH 027/134] feat: update --- .../editor/editing.render-controller.ts | 4 +--- .../src/services/editor-bridge.service.ts | 1 - packages/ui/src/controllers/menus/menus.ts | 16 ++++++++++++---- 3 files changed, 13 insertions(+), 8 deletions(-) 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 b1993725d2a..82ed94b56b7 100644 --- a/packages/sheets-ui/src/controllers/editor/editing.render-controller.ts +++ b/packages/sheets-ui/src/controllers/editor/editing.render-controller.ts @@ -28,7 +28,6 @@ import { FOCUSING_EDITOR_INPUT_FORMULA, FOCUSING_EDITOR_STANDALONE, FOCUSING_FX_BAR_EDITOR, - FOCUSING_UNIVER_EDITOR_STANDALONE_SINGLE_MODE, ICommandService, IContextService, Inject, @@ -254,8 +253,7 @@ export class EditingRenderController extends Disposable implements IRenderModule const { position, documentLayoutObject, scaleX, editorUnitId } = state; if ( - this._contextService.getContextValue(FOCUSING_EDITOR_STANDALONE) || - this._contextService.getContextValue(FOCUSING_UNIVER_EDITOR_STANDALONE_SINGLE_MODE) + this._contextService.getContextValue(FOCUSING_EDITOR_STANDALONE) ) { return; } diff --git a/packages/sheets-ui/src/services/editor-bridge.service.ts b/packages/sheets-ui/src/services/editor-bridge.service.ts index 6d9cb42ef99..a16de258d03 100644 --- a/packages/sheets-ui/src/services/editor-bridge.service.ts +++ b/packages/sheets-ui/src/services/editor-bridge.service.ts @@ -250,7 +250,6 @@ export class EditorBridgeService extends Disposable implements IEditorBridgeServ */ this._contextService.setContextValue(EDITOR_ACTIVATED, false); this._contextService.setContextValue(FOCUSING_EDITOR_STANDALONE, false); - // this._contextService.setContextValue(FOCUSING_UNIVER_EDITOR_STANDALONE_SINGLE_MODE, false); } const editCellState = this.getLatestEditCellState(); diff --git a/packages/ui/src/controllers/menus/menus.ts b/packages/ui/src/controllers/menus/menus.ts index e14ec922221..64d90ed8b0b 100644 --- a/packages/ui/src/controllers/menus/menus.ts +++ b/packages/ui/src/controllers/menus/menus.ts @@ -14,15 +14,17 @@ * limitations under the License. */ -import { IUndoRedoService, RedoCommand, UndoCommand } from '@univerjs/core'; import type { IAccessor } from '@univerjs/core'; -import { map } from 'rxjs/operators'; - import type { IMenuButtonItem } from '../../services/menu/menu'; +import { FOCUSING_FX_BAR_EDITOR, FOCUSING_UNIVER_EDITOR, IContextService, IUndoRedoService, RedoCommand, UndoCommand } from '@univerjs/core'; + +import { combineLatest } from 'rxjs'; +import { filter, map } from 'rxjs/operators'; import { MenuItemType } from '../../services/menu/menu'; export function UndoMenuItemFactory(accessor: IAccessor): IMenuButtonItem { const undoRedoService = accessor.get(IUndoRedoService); + const contextService = accessor.get(IContextService); return { id: UndoCommand.id, @@ -30,7 +32,13 @@ export function UndoMenuItemFactory(accessor: IAccessor): IMenuButtonItem { icon: 'UndoSingle', title: 'Undo', tooltip: 'toolbar.undo', - disabled$: undoRedoService.undoRedoStatus$.pipe(map((v) => v.undos <= 0)), + disabled$: combineLatest([ + undoRedoService.undoRedoStatus$.pipe(map((v) => v.undos <= 0)), + contextService.contextChanged$.pipe(filter((key) => Object.hasOwnProperty.call(key, FOCUSING_UNIVER_EDITOR) || Object.hasOwnProperty.call(key, FOCUSING_FX_BAR_EDITOR))), + ]).pipe(map(([undoDisable]) => { + // console.log('===undo', undoDisable, contextService, contextService.getContextValue(FOCUSING_UNIVER_EDITOR), contextService.getContextValue(FOCUSING_FX_BAR_EDITOR)); + return undoDisable || contextService.getContextValue(FOCUSING_UNIVER_EDITOR) || contextService.getContextValue(FOCUSING_FX_BAR_EDITOR); + })), }; } From 1a92b30c5782a4e0828a697ed249d608b58f723c Mon Sep 17 00:00:00 2001 From: zhangw Date: Mon, 2 Dec 2024 21:01:21 +0800 Subject: [PATCH 028/134] feat: update --- packages/ui/src/controllers/menus/menus.ts | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/packages/ui/src/controllers/menus/menus.ts b/packages/ui/src/controllers/menus/menus.ts index 64d90ed8b0b..22bcc2eee04 100644 --- a/packages/ui/src/controllers/menus/menus.ts +++ b/packages/ui/src/controllers/menus/menus.ts @@ -16,7 +16,7 @@ import type { IAccessor } from '@univerjs/core'; import type { IMenuButtonItem } from '../../services/menu/menu'; -import { FOCUSING_FX_BAR_EDITOR, FOCUSING_UNIVER_EDITOR, IContextService, IUndoRedoService, RedoCommand, UndoCommand } from '@univerjs/core'; +import { EDITOR_ACTIVATED, FOCUSING_FX_BAR_EDITOR, IContextService, IUndoRedoService, RedoCommand, UndoCommand } from '@univerjs/core'; import { combineLatest } from 'rxjs'; import { filter, map } from 'rxjs/operators'; @@ -34,16 +34,16 @@ export function UndoMenuItemFactory(accessor: IAccessor): IMenuButtonItem { tooltip: 'toolbar.undo', disabled$: combineLatest([ undoRedoService.undoRedoStatus$.pipe(map((v) => v.undos <= 0)), - contextService.contextChanged$.pipe(filter((key) => Object.hasOwnProperty.call(key, FOCUSING_UNIVER_EDITOR) || Object.hasOwnProperty.call(key, FOCUSING_FX_BAR_EDITOR))), + contextService.contextChanged$.pipe(filter((key) => Object.hasOwnProperty.call(key, EDITOR_ACTIVATED) || Object.hasOwnProperty.call(key, FOCUSING_FX_BAR_EDITOR))), ]).pipe(map(([undoDisable]) => { - // console.log('===undo', undoDisable, contextService, contextService.getContextValue(FOCUSING_UNIVER_EDITOR), contextService.getContextValue(FOCUSING_FX_BAR_EDITOR)); - return undoDisable || contextService.getContextValue(FOCUSING_UNIVER_EDITOR) || contextService.getContextValue(FOCUSING_FX_BAR_EDITOR); + return undoDisable || contextService.getContextValue(EDITOR_ACTIVATED) || contextService.getContextValue(FOCUSING_FX_BAR_EDITOR); })), }; } export function RedoMenuItemFactory(accessor: IAccessor): IMenuButtonItem { const undoRedoService = accessor.get(IUndoRedoService); + const contextService = accessor.get(IContextService); return { id: RedoCommand.id, @@ -51,6 +51,11 @@ export function RedoMenuItemFactory(accessor: IAccessor): IMenuButtonItem { icon: 'RedoSingle', title: 'Redo', tooltip: 'toolbar.redo', - disabled$: undoRedoService.undoRedoStatus$.pipe(map((v) => v.redos <= 0)), + disabled$: combineLatest([ + undoRedoService.undoRedoStatus$.pipe(map((v) => v.undos <= 0)), + contextService.contextChanged$.pipe(filter((key) => Object.hasOwnProperty.call(key, EDITOR_ACTIVATED) || Object.hasOwnProperty.call(key, FOCUSING_FX_BAR_EDITOR))), + ]).pipe(map(([undoDisable]) => { + return undoDisable || contextService.getContextValue(EDITOR_ACTIVATED) || contextService.getContextValue(FOCUSING_FX_BAR_EDITOR); + })), }; } From 5c65608d6b8c0bc81a0f5b902349cb5adf237026 Mon Sep 17 00:00:00 2001 From: zhangw Date: Mon, 2 Dec 2024 21:10:33 +0800 Subject: [PATCH 029/134] feat: update --- .../src/controllers/dv-alert.controller.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/sheets-data-validation-ui/src/controllers/dv-alert.controller.ts b/packages/sheets-data-validation-ui/src/controllers/dv-alert.controller.ts index 5cfd626fd0d..145ffa90f90 100644 --- a/packages/sheets-data-validation-ui/src/controllers/dv-alert.controller.ts +++ b/packages/sheets-data-validation-ui/src/controllers/dv-alert.controller.ts @@ -49,7 +49,10 @@ export class DataValidationAlertController extends Disposable { const worksheet = workbook.getActiveSheet(); if (!worksheet) return; const rule = this._dataValidationModel.getRuleByLocation(cellPos.location.unitId, cellPos.location.subUnitId, cellPos.location.row, cellPos.location.col); - if (!rule) return; + if (!rule) { + this._cellAlertManagerService.removeAlert(ALERT_KEY); + return; + } const validStatus = this._dataValidationModel.validator(rule, cellPos.location); if (validStatus === DataValidationStatus.INVALID) { @@ -62,11 +65,13 @@ export class DataValidationAlertController extends Disposable { currentLoc.subUnitId === cellPos.location.subUnitId && currentLoc.unitId === cellPos.location.unitId ) { + this._cellAlertManagerService.removeAlert(ALERT_KEY); return; } const validator = this._dataValidationModel.getValidator(rule.type) as BaseDataValidator; if (!validator) { + this._cellAlertManagerService.removeAlert(ALERT_KEY); return; } this._cellAlertManagerService.showAlert({ From 5834818111d0c25031b58405c9f243d1f47c4876 Mon Sep 17 00:00:00 2001 From: zhangw Date: Mon, 2 Dec 2024 21:27:45 +0800 Subject: [PATCH 030/134] feat: update --- .../text-x/build-utils/text-x-utils.ts | 6 +- .../src/sheets-formula-ui.plugin.ts | 4 +- .../src/views/formula-editor/cell-editor.tsx | 250 ------------------ 3 files changed, 4 insertions(+), 256 deletions(-) delete mode 100644 packages/sheets-formula-ui/src/views/formula-editor/cell-editor.tsx diff --git a/packages/core/src/docs/data-model/text-x/build-utils/text-x-utils.ts b/packages/core/src/docs/data-model/text-x/build-utils/text-x-utils.ts index acc5267d4c0..4ef7b2144a2 100644 --- a/packages/core/src/docs/data-model/text-x/build-utils/text-x-utils.ts +++ b/packages/core/src/docs/data-model/text-x/build-utils/text-x-utils.ts @@ -336,11 +336,11 @@ export const replaceSelectionTextRuns = (params: IReplaceSelectionTextXParams) = const action: TextXAction = { t: TextXActionType.RETAIN, body: isTextRunsEqual(sliceBody, oldBodySlice) - ? { + ? undefined + : { ...sliceBody, dataStream: '', - } - : undefined, + }, len: text.length, }; cursor += text.length; diff --git a/packages/sheets-formula-ui/src/sheets-formula-ui.plugin.ts b/packages/sheets-formula-ui/src/sheets-formula-ui.plugin.ts index a0ecbbbebf6..9e097a76622 100644 --- a/packages/sheets-formula-ui/src/sheets-formula-ui.plugin.ts +++ b/packages/sheets-formula-ui/src/sheets-formula-ui.plugin.ts @@ -20,7 +20,7 @@ import { DependentOn, IConfigService, Inject, Injector, Plugin, touchDependencie import { UniverFormulaEnginePlugin } from '@univerjs/engine-formula'; import { IRenderManagerService } from '@univerjs/engine-render'; import { UniverSheetsFormulaPlugin } from '@univerjs/sheets-formula'; -import { EMBEDDING_CELL_EDITOR_COMPONENT_KEY, EMBEDDING_FORMULA_EDITOR_COMPONENT_KEY, RANGE_SELECTOR_COMPONENT_KEY } from '@univerjs/sheets-ui'; +import { EMBEDDING_FORMULA_EDITOR_COMPONENT_KEY, RANGE_SELECTOR_COMPONENT_KEY } from '@univerjs/sheets-ui'; import { ComponentManager } from '@univerjs/ui'; import { FORMULA_UI_PLUGIN_NAME } from './common/plugin-name'; import { @@ -36,7 +36,6 @@ import { FormulaUIController } from './controllers/formula-ui.controller'; import { PromptController } from './controllers/prompt.controller'; import { FormulaPromptService, IFormulaPromptService } from './services/prompt.service'; import { RefSelectionsRenderService } from './services/render-services/ref-selections.render-service'; -import { CellEditor } from './views/formula-editor/cell-editor'; import { FormulaEditor } from './views/formula-editor/index'; import { RangeSelector } from './views/range-selector'; @@ -97,7 +96,6 @@ export class UniverSheetsFormulaUIPlugin extends Plugin { componentManager.register(RANGE_SELECTOR_COMPONENT_KEY, RangeSelector); componentManager.register(EMBEDDING_FORMULA_EDITOR_COMPONENT_KEY, FormulaEditor); - componentManager.register(EMBEDDING_CELL_EDITOR_COMPONENT_KEY, CellEditor); } override onSteady(): void { diff --git a/packages/sheets-formula-ui/src/views/formula-editor/cell-editor.tsx b/packages/sheets-formula-ui/src/views/formula-editor/cell-editor.tsx deleted file mode 100644 index 3c9a003283a..00000000000 --- a/packages/sheets-formula-ui/src/views/formula-editor/cell-editor.tsx +++ /dev/null @@ -1,250 +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 type { DocumentDataModel, IDisposable } from '@univerjs/core'; -import type { Editor } from '@univerjs/docs-ui'; -import type { KeyCode, MetaKeys } from '@univerjs/ui'; -import type { IKeyboardEventConfig } from '../range-selector/hooks/useKeyboardEvent'; -import { BuildTextUtils, IUniverInstanceService, useDependency, useObservable } from '@univerjs/core'; -import { DocBackScrollRenderController, IEditorService } from '@univerjs/docs-ui'; -import { useEvent } from '@univerjs/ui'; -import clsx from 'clsx'; -import React, { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react'; -import { useEmitChange } from '../range-selector/hooks/useEmitChange'; -import { useFirstHighlightDoc } from '../range-selector/hooks/useFirstHighlightDoc'; -import { useFocus } from '../range-selector/hooks/useFocus'; -import { useFormulaToken } from '../range-selector/hooks/useFormulaToken'; -import { useDocHight, useSheetHighlight } from '../range-selector/hooks/useHighlight'; -import { useKeyboardEvent } from '../range-selector/hooks/useKeyboardEvent'; -import { useLeftAndRightArrow } from '../range-selector/hooks/useLeftAndRightArrow'; -import { useRefactorEffect } from '../range-selector/hooks/useRefactorEffect'; -import { useRefocus } from '../range-selector/hooks/useRefocus'; -import { useResize } from '../range-selector/hooks/useResize'; -import { useSwitchSheet } from '../range-selector/hooks/useSwitchSheet'; -import { HelpFunction } from './help-function/HelpFunction'; -import { useFormulaDescribe } from './hooks/useFormulaDescribe'; -import { useFormulaSearch } from './hooks/useFormulaSearch'; -import { FormulaSelectingType, useFormulaSelecting } from './hooks/useFormulaSelection'; -import { useSheetSelectionChange } from './hooks/useSheetSelectionChange'; -import styles from './index.module.less'; -import { SearchFunction } from './search-function/SearchFunction'; -import { getFormulaText } from './utils/getFormulaText'; - -export interface IFormulaEditorProps { - unitId: string; - subUnitId: string; - onChange: (text: string) => void; - isFocus?: boolean; - onFocus?: () => void; - onBlur?: () => void; - isSupportAcrossSheet?: boolean; - className?: string; - editorId: string; - moveCursor?: boolean; - onFormulaSelectingChange?: (isSelecting: FormulaSelectingType) => void; - keyboradEventConfig?: IKeyboardEventConfig; - onMoveInEditor?: (keyCode: KeyCode, metaKey?: MetaKeys) => void; -} - -const noop = () => { }; -export function CellEditor(props: IFormulaEditorProps) { - const { - unitId, - subUnitId, - isFocus: _isFocus = true, - isSupportAcrossSheet = false, - onFocus = noop, - onBlur = noop, - onChange, - className, - editorId, - moveCursor = true, - onFormulaSelectingChange: propOnFormulaSelectingChange, - keyboradEventConfig, - onMoveInEditor, - } = props; - - const editorService = useDependency(IEditorService); - const sheetEmbeddingRef = useRef(null); - const onFormulaSelectingChange = useEvent(propOnFormulaSelectingChange); - const searchFunctionRef = useRef(null); - const [editor, editorSet] = useState(); - const [isFocus, isFocusSet] = useState(_isFocus); - const formulaEditorContainerRef = useRef(null); - const univerInstanceService = useDependency(IUniverInstanceService); - const document = univerInstanceService.getUnit(editorId); - useObservable(document?.change$); - const getFormulaToken = useFormulaToken(); - const formulaText = BuildTextUtils.transform.getPlainText(document?.getBody()?.dataStream ?? ''); - const formulaWithoutEqualSymbol = useMemo(() => getFormulaText(formulaText), [formulaText]); - const sequenceNodes = useMemo(() => getFormulaToken(formulaWithoutEqualSymbol), [formulaWithoutEqualSymbol, getFormulaToken]); - const isSelecting = useFormulaSelecting(editorId, sequenceNodes); - const [shouldMoveRefSelection, setShouldMoveRefSelection] = useState(false); - - useEffect(() => { - if (isSelecting === FormulaSelectingType.NEED_ADD) { - setShouldMoveRefSelection(true); - } - if (isSelecting === FormulaSelectingType.NOT_SELECT) { - setShouldMoveRefSelection(false); - } - }, [isSelecting]); - - const needEmit = useEmitChange(sequenceNodes, (text: string) => { - onChange(`=${text}`); - }, editor); - - const highlightDoc = useDocHight('='); - const highlightSheet = useSheetHighlight(unitId); - const highligh = useCallback((text: string, isNeedResetSelection: boolean = true) => { - if (!editor) { - return; - } - const sequenceNodes = getFormulaToken(text); - const ranges = highlightDoc(editor, sequenceNodes, isNeedResetSelection); - highlightSheet(ranges); - }, [editor, getFormulaToken, highlightDoc, highlightSheet]); - - useEffect(() => { - highligh(formulaWithoutEqualSymbol, false); - }, [highligh, formulaWithoutEqualSymbol]); - - const focus = useFocus(editor); - - useEffect(() => { - onFormulaSelectingChange(isSelecting); - }, [onFormulaSelectingChange, isSelecting]); - - useKeyboardEvent(isFocus, keyboradEventConfig, editor); - - const { checkScrollBar } = useResize(editor); - useRefactorEffect(isFocus, Boolean(isSelecting), unitId); - useLeftAndRightArrow(isFocus && moveCursor, shouldMoveRefSelection, editor, onMoveInEditor); - - const handleSelectionChange = useEvent((refString: string, offset: number, isEnd: boolean) => { - needEmit(); - if (refString !== formulaText) { - highligh(refString); - } - if (isEnd) { - focus(); - if (offset !== -1) { - // 在渲染结束之后再设置选区 - setTimeout(() => { - const range = { startOffset: offset + 1, endOffset: offset + 1 }; - editor?.setSelectionRanges([range]); - const docBackScrollRenderController = editor?.render.with(DocBackScrollRenderController); - docBackScrollRenderController?.scrollToRange({ ...range, collapsed: true }); - }, 50); - } - checkScrollBar(); - } - }); - useSheetSelectionChange(isFocus, unitId, subUnitId, sequenceNodes, isSupportAcrossSheet, shouldMoveRefSelection, editor, handleSelectionChange); - - useRefocus(); - useSwitchSheet(isFocus, unitId, isSupportAcrossSheet, isFocusSet, onBlur, noop); - - const { searchList, searchText, handlerFormulaReplace, reset: resetFormulaSearch } = useFormulaSearch(isFocus, sequenceNodes, editor); - const { functionInfo, paramIndex, reset } = useFormulaDescribe(isFocus, formulaText, editor); - - useFirstHighlightDoc(formulaWithoutEqualSymbol, '=', isFocus, highlightDoc, highlightSheet, editor); - - useLayoutEffect(() => { - let dispose: IDisposable; - if (formulaEditorContainerRef.current) { - dispose = editorService.register({ - autofocus: true, - editorUnitId: editorId, - isSingle: true, - initialSnapshot: { - id: editorId, - body: { dataStream: '\r\n' }, - documentStyle: {}, - }, - }, formulaEditorContainerRef.current); - const editor = editorService.getEditor(editorId)! as Editor; - editorSet(editor); - } - - return () => { - dispose?.dispose(); - }; - }, []); - - const handleFunctionSelect = (v: string) => { - const res = handlerFormulaReplace(v); - if (res) { - const selections = editor?.getSelectionRanges(); - if (selections && selections.length === 1) { - const range = selections[0]; - if (range.collapsed) { - const offset = res.offset; - setTimeout(() => { - editor?.setSelectionRanges([{ startOffset: range.startOffset - offset, endOffset: range.endOffset - offset }]); - }, 30); - } - } - resetFormulaSearch(); - focus(); - highligh(res.text); - } - }; - - const handleMouseUp = () => { - // 在进行多个 input 切换的时候,失焦必须快于获得焦点. - // 即使失焦是 mousedown 事件, - // 聚焦是 mouseup 事件, - // 但是 react 的 useEffect 无法保证顺序,无法确保失焦在聚焦之前. - if (isSelecting !== FormulaSelectingType.NEED_ADD) { - setShouldMoveRefSelection(false); - } - }; - return ( -
-
-
-
-
- { - reset(); - focus(); - }} - > - - - -
- ) - ; -} From 32f3d27a8afb81cd3d960ec374a9c4b97860f989 Mon Sep 17 00:00:00 2001 From: zhangw Date: Mon, 2 Dec 2024 21:44:52 +0800 Subject: [PATCH 031/134] feat: update --- .../sheets-formula-ui/src/views/formula-editor/index.tsx | 4 +--- .../src/controllers/editor/editing.render-controller.ts | 2 +- .../src/services/editor/cell-editor-resize.service.ts | 9 +++++---- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/packages/sheets-formula-ui/src/views/formula-editor/index.tsx b/packages/sheets-formula-ui/src/views/formula-editor/index.tsx index 9fd98161880..3224498ce5e 100644 --- a/packages/sheets-formula-ui/src/views/formula-editor/index.tsx +++ b/packages/sheets-formula-ui/src/views/formula-editor/index.tsx @@ -188,9 +188,7 @@ export function FormulaEditor(props: IFormulaEditorProps) { const handleSelectionChange = useEvent((refString: string, offset: number, isEnd: boolean) => { needEmit(); - if (refString !== formulaText) { - highligh(refString); - } + highligh(refString); if (isEnd) { focus(); if (offset !== -1) { 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 82ed94b56b7..1818d1a2138 100644 --- a/packages/sheets-ui/src/controllers/editor/editing.render-controller.ts +++ b/packages/sheets-ui/src/controllers/editor/editing.render-controller.ts @@ -198,7 +198,7 @@ export class EditingRenderController extends Disposable implements IRenderModule const param = this._editorBridgeService.getEditCellState(); const editorId = this._editorBridgeService.getCurrentEditorId(); - if (!param || !editorId || !this._editorService.isSheetEditor(editorId)) { + if (!param || !editorId) { return; } diff --git a/packages/sheets-ui/src/services/editor/cell-editor-resize.service.ts b/packages/sheets-ui/src/services/editor/cell-editor-resize.service.ts index 6292614e7a0..ae313d594b2 100644 --- a/packages/sheets-ui/src/services/editor/cell-editor-resize.service.ts +++ b/packages/sheets-ui/src/services/editor/cell-editor-resize.service.ts @@ -14,9 +14,9 @@ * limitations under the License. */ -import type { IPosition, Nullable, Workbook } from '@univerjs/core'; +import type { DocumentDataModel, IPosition, Nullable, Workbook } from '@univerjs/core'; import type { DocumentSkeleton, IDocumentLayoutObject, IRenderContext, IRenderModule, Scene } from '@univerjs/engine-render'; -import { Disposable, DOCS_NORMAL_EDITOR_UNIT_ID_KEY, HorizontalAlign, Inject, VerticalAlign, WrapStrategy } from '@univerjs/core'; +import { Disposable, DOCS_NORMAL_EDITOR_UNIT_ID_KEY, HorizontalAlign, Inject, IUniverInstanceService, UniverInstanceType, VerticalAlign, WrapStrategy } from '@univerjs/core'; import { DocSkeletonManagerService } from '@univerjs/docs'; import { VIEWPORT_KEY as DOC_VIEWPORT_KEY, DOCS_COMPONENT_MAIN_LAYER_INDEX } from '@univerjs/docs-ui'; import { convertTextRotation, FIX_ONE_PIXEL_BLUR_OFFSET, fixLineWidthByScale, IRenderManagerService, Rect, ScrollBar } from '@univerjs/engine-render'; @@ -43,7 +43,8 @@ export class SheetCellEditorResizeService extends Disposable implements IRenderM @ICellEditorManagerService private readonly _cellEditorManagerService: ICellEditorManagerService, @IEditorBridgeService private readonly _editorBridgeService: IEditorBridgeService, @IRenderManagerService private readonly _renderManagerService: IRenderManagerService, - @Inject(SheetSkeletonManagerService) private readonly _sheetSkeletonManagerService: SheetSkeletonManagerService + @Inject(SheetSkeletonManagerService) private readonly _sheetSkeletonManagerService: SheetSkeletonManagerService, + @IUniverInstanceService private readonly _univerInstanceService: IUniverInstanceService ) { super(); } @@ -54,7 +55,7 @@ export class SheetCellEditorResizeService extends Disposable implements IRenderM const { position, documentLayoutObject, canvasOffset, scaleX, scaleY } = param; const { startX, startY, endX, endY } = position; - const documentDataModel = documentLayoutObject.documentModel; + const documentDataModel = this._univerInstanceService.getUnit(DOCS_NORMAL_EDITOR_UNIT_ID_KEY, UniverInstanceType.UNIVER_DOC); if (documentDataModel == null) { return; From fa2f8f3620c133e618f861aadde771ef82d74bf0 Mon Sep 17 00:00:00 2001 From: zhangw Date: Mon, 2 Dec 2024 22:24:17 +0800 Subject: [PATCH 032/134] feat: update --- .../operations/show-comment-panel.operation.ts | 2 +- .../selection/doc-selection-render.service.ts | 16 ---------------- .../commands/mutations/core-editing.mutation.ts | 2 +- 3 files changed, 2 insertions(+), 18 deletions(-) diff --git a/packages/docs-thread-comment-ui/src/commands/operations/show-comment-panel.operation.ts b/packages/docs-thread-comment-ui/src/commands/operations/show-comment-panel.operation.ts index 3d8cbe31133..a584f7483e9 100644 --- a/packages/docs-thread-comment-ui/src/commands/operations/show-comment-panel.operation.ts +++ b/packages/docs-thread-comment-ui/src/commands/operations/show-comment-panel.operation.ts @@ -132,7 +132,7 @@ export const StartAddCommentOperation: ICommand = { threadId: commentId, }; - docSelectionRenderManager?.blurEditor(); + docSelectionRenderManager?.blur(); docCommentService.startAdd(comment); panelService.setActiveComment({ unitId, diff --git a/packages/docs-ui/src/services/selection/doc-selection-render.service.ts b/packages/docs-ui/src/services/selection/doc-selection-render.service.ts index 5de37fc3db2..67444dfe121 100644 --- a/packages/docs-ui/src/services/selection/doc-selection-render.service.ts +++ b/packages/docs-ui/src/services/selection/doc-selection-render.service.ts @@ -333,22 +333,6 @@ export class DocSelectionRenderService extends RxDisposable implements IRenderMo this._input.blur(); } - /** - * @deprecated - */ - focusEditor(): void { - this._editorFocusing = true; - this.focus(); - } - - /** - * @deprecated - */ - blurEditor(): void { - this._editorFocusing = false; - this.blur(); - } - // FIXME: for editor cell editor we don't need to blur the input element /** * @deprecated diff --git a/packages/docs/src/commands/mutations/core-editing.mutation.ts b/packages/docs/src/commands/mutations/core-editing.mutation.ts index afd42631d57..fec9393e426 100644 --- a/packages/docs/src/commands/mutations/core-editing.mutation.ts +++ b/packages/docs/src/commands/mutations/core-editing.mutation.ts @@ -101,7 +101,7 @@ export const RichTextEditingMutation: IMutation { docSelectionManagerService.replaceDocRanges(textRanges, { unitId, subUnitId: unitId }, isEditing, params.options); }); From 84cc13f5472f78244611e2ecb58d3388bb058975 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Mon, 2 Dec 2024 14:55:50 +0000 Subject: [PATCH 033/134] chore(snapshots): update snapshots --- .../default-doc-ci-chromium-linux.png | Bin 126439 -> 126529 bytes ...fault-sheet-fullpage-ci-chromium-linux.png | Bin 72968 -> 73223 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/e2e/visual-comparison/docs/docs-visual-comparison.spec.ts-snapshots/default-doc-ci-chromium-linux.png b/e2e/visual-comparison/docs/docs-visual-comparison.spec.ts-snapshots/default-doc-ci-chromium-linux.png index e91f38cdd2f579abdf3afe47dabc882ecb2d9b15..9b80b4f69a4c908db7e779f7721d94ccfa8355cb 100644 GIT binary patch literal 126529 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

-d!IsICWJ^+wbBG%qg}SRD^` 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 z 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 diff --git a/e2e/visual-comparison/sheets/sheets-visual-comparison.spec.ts-snapshots/default-sheet-fullpage-ci-chromium-linux.png b/e2e/visual-comparison/sheets/sheets-visual-comparison.spec.ts-snapshots/default-sheet-fullpage-ci-chromium-linux.png index 6917685851334768848a0ab7a7f60f62d4cf0e6c..45f6c82c3afb4d9c88aae6070a1f9fa95b3bbfc0 100644 GIT binary patch literal 73223 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

<{w8p60rAqCJcwVlq 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 literal 72968 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 From 6e1f1d68145ef92b83b869d15cf3c2ffd0c4c66c Mon Sep 17 00:00:00 2001 From: zhangw Date: Mon, 2 Dec 2024 23:05:32 +0800 Subject: [PATCH 034/134] feat: update --- .../docs/data-model/text-x/build-utils/text-x-utils.ts | 8 ++++---- .../sheets-formula-ui/src/views/formula-editor/index.tsx | 2 -- .../src/views/range-selector/hooks/useHighlight.ts | 6 +++--- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/packages/core/src/docs/data-model/text-x/build-utils/text-x-utils.ts b/packages/core/src/docs/data-model/text-x/build-utils/text-x-utils.ts index 4ef7b2144a2..bc2dfb58772 100644 --- a/packages/core/src/docs/data-model/text-x/build-utils/text-x-utils.ts +++ b/packages/core/src/docs/data-model/text-x/build-utils/text-x-utils.ts @@ -293,10 +293,6 @@ export const replaceSelectionTextX = (params: IReplaceSelectionTextXParams) => { } }); - if (actions.every((action) => action.t === TextXActionType.RETAIN && !action.body)) { - return false; - } - const textX = new TextX(); textX.push({ t: TextXActionType.RETAIN, @@ -367,6 +363,10 @@ export const replaceSelectionTextRuns = (params: IReplaceSelectionTextXParams) = } }); + if (actions.every((action) => action.t === TextXActionType.RETAIN && !action.body)) { + return false; + } + const textX = new TextX(); textX.push({ t: TextXActionType.RETAIN, diff --git a/packages/sheets-formula-ui/src/views/formula-editor/index.tsx b/packages/sheets-formula-ui/src/views/formula-editor/index.tsx index 3224498ce5e..9d6ac6973be 100644 --- a/packages/sheets-formula-ui/src/views/formula-editor/index.tsx +++ b/packages/sheets-formula-ui/src/views/formula-editor/index.tsx @@ -211,8 +211,6 @@ export function FormulaEditor(props: IFormulaEditorProps) { const { searchList, searchText, handlerFormulaReplace, reset: resetFormulaSearch } = useFormulaSearch(isFocus, sequenceNodes, editor); const { functionInfo, paramIndex, reset } = useFormulaDescribe(isFocus, formulaText, editor); - // useFirstHighlightDoc(formulaWithoutEqualSymbol, '=', isFocus, highlightDoc, highlightSheet, editor); - useLayoutEffect(() => { let dispose: IDisposable; if (formulaEditorContainerRef.current) { diff --git a/packages/sheets-formula-ui/src/views/range-selector/hooks/useHighlight.ts b/packages/sheets-formula-ui/src/views/range-selector/hooks/useHighlight.ts index 24b05a548d4..84757a58c46 100644 --- a/packages/sheets-formula-ui/src/views/range-selector/hooks/useHighlight.ts +++ b/packages/sheets-formula-ui/src/views/range-selector/hooks/useHighlight.ts @@ -19,7 +19,7 @@ import type { Editor } from '@univerjs/docs-ui'; import type { ISequenceNode } from '@univerjs/engine-formula'; import type { ISelectionWithStyle } from '@univerjs/sheets'; import type { INode } from './useFormulaToken'; -import { ICommandService, IUniverInstanceService, ThemeService, useDependency } from '@univerjs/core'; +import { getBodySlice, ICommandService, IUniverInstanceService, ThemeService, useDependency } from '@univerjs/core'; import { ReplaceTextRunsCommand } from '@univerjs/docs-ui'; import { deserializeRangeWithSheet, sequenceNodeType } from '@univerjs/engine-formula'; import { IRenderManagerService } from '@univerjs/engine-render'; @@ -123,7 +123,7 @@ export function useDocHight(_leadingCharacter: string = '') { // editor.setDocumentData(cloneData); commandService.syncExecuteCommand(ReplaceTextRunsCommand.id, { unitId: editorId, - body: cloneBody, + body: getBodySlice(cloneBody, 0, cloneBody.dataStream.length - 2), }); } return []; @@ -159,7 +159,7 @@ export function useDocHight(_leadingCharacter: string = '') { // editor.setDocumentData(cloneData, selections); commandService.syncExecuteCommand(ReplaceTextRunsCommand.id, { unitId: editorId, - body: cloneBody, + body: getBodySlice(cloneBody, 0, cloneBody.dataStream.length - 2), textRanges: selections, }); return refSelections; From 88efb89ed1d034b4015bc86d8369e5c349c0037e Mon Sep 17 00:00:00 2001 From: zhangw Date: Mon, 2 Dec 2024 23:18:32 +0800 Subject: [PATCH 035/134] feat: update --- .../text-x/build-utils/text-x-utils.ts | 18 ++- .../core/src/docs/data-model/text-x/utils.ts | 113 ++++++++++++------ packages/core/src/index.ts | 15 ++- 3 files changed, 98 insertions(+), 48 deletions(-) diff --git a/packages/core/src/docs/data-model/text-x/build-utils/text-x-utils.ts b/packages/core/src/docs/data-model/text-x/build-utils/text-x-utils.ts index bc2dfb58772..b21584bde49 100644 --- a/packages/core/src/docs/data-model/text-x/build-utils/text-x-utils.ts +++ b/packages/core/src/docs/data-model/text-x/build-utils/text-x-utils.ts @@ -16,7 +16,7 @@ import type { IAccessor } from '@wendellhu/redi'; import type { ITextRange, ITextRangeParam } from '../../../../sheets/typedef'; -import type { CustomRangeType, IDocumentBody } from '../../../../types/interfaces'; +import type { CustomRangeType, IDocumentBody, ITextRun } from '../../../../types/interfaces'; import type { DocumentDataModel } from '../../document-data-model'; import type { TextXAction } from '../action-types'; import type { TextXSelection } from '../text-x'; @@ -24,7 +24,7 @@ import { type Nullable, UpdateDocsAttributeType } from '../../../../shared'; import { textDiff } from '../../../../shared/text-diff'; import { TextXActionType } from '../action-types'; import { TextX } from '../text-x'; -import { getBodySlice } from '../utils'; +import { getBodySlice, getTextRunSlice } from '../utils'; import { excludePointsFromRange, getIntersectingCustomRanges, getSelectionForAddCustomRange } from './custom-range'; export interface IDeleteCustomRangeParam { @@ -302,10 +302,7 @@ export const replaceSelectionTextX = (params: IReplaceSelectionTextXParams) => { return textX; }; -function isTextRunsEqual(body: IDocumentBody, oldBody: IDocumentBody) { - const { textRuns } = body; - const { textRuns: oldTextRuns } = oldBody; - +function isTextRunsEqual(textRuns: ITextRun[] | undefined, oldTextRuns: ITextRun[] | undefined) { if (textRuns?.length === oldTextRuns?.length && textRuns?.every((textRun, index) => JSON.stringify(textRun) === JSON.stringify(oldTextRuns?.[index]))) { return true; } @@ -326,15 +323,14 @@ export const replaceSelectionTextRuns = (params: IReplaceSelectionTextXParams) = switch (type) { // retain case 0: { - const sliceBody = getBodySlice(insertBody, cursor, cursor + text.length, false); - const oldBodySlice = getBodySlice(oldBody!, cursor, cursor + text.length, false); - + const textRunsSlice = getTextRunSlice(insertBody, cursor, cursor + text.length, false); + const oldTextRunsSlice = getTextRunSlice(oldBody!, cursor, cursor + text.length, false); const action: TextXAction = { t: TextXActionType.RETAIN, - body: isTextRunsEqual(sliceBody, oldBodySlice) + body: isTextRunsEqual(textRunsSlice, oldTextRunsSlice) ? undefined : { - ...sliceBody, + textRuns: textRunsSlice, dataStream: '', }, len: text.length, diff --git a/packages/core/src/docs/data-model/text-x/utils.ts b/packages/core/src/docs/data-model/text-x/utils.ts index 9b083e5a591..32022eb0e4d 100644 --- a/packages/core/src/docs/data-model/text-x/utils.ts +++ b/packages/core/src/docs/data-model/text-x/utils.ts @@ -26,19 +26,13 @@ export enum SliceBodyType { cut, } -// eslint-disable-next-line max-lines-per-function, complexity -export function getBodySlice( +export function getTextRunSlice( body: IDocumentBody, startOffset: number, endOffset: number, - returnEmptyArray = true, - type = SliceBodyType.cut -): IDocumentBody { - const { dataStream, textRuns, paragraphs = [], customBlocks = [], tables = [], sectionBreaks = [] } = body; - - const docBody: IDocumentBody = { - dataStream: dataStream.slice(startOffset, endOffset), - }; + returnEmptyTextRuns = true +) { + const { textRuns } = body; if (textRuns) { const newTextRuns: ITextRun[] = []; @@ -66,7 +60,7 @@ export function getBodySlice( } } - docBody.textRuns = normalizeTextRuns( + return normalizeTextRuns( newTextRuns.map((tr) => { const { st, ed } = tr; return { @@ -76,18 +70,24 @@ export function getBodySlice( }; }) ); - } else if (returnEmptyArray) { + } else if (returnEmptyTextRuns) { // In the case of no style before, add the style, removeTextRuns will be empty, // in this case, you need to add an empty textRun for undo. - docBody.textRuns = [{ + return [{ st: 0, ed: endOffset - startOffset, ts: {}, }]; } +} +export function getTableSlice( + body: IDocumentBody, + startOffset: number, + endOffset: number +) { + const { tables = [] } = body; const newTables = []; - for (const table of tables) { const clonedTable = Tools.deepClone(table); const { startIndex, endIndex } = clonedTable; @@ -100,11 +100,15 @@ export function getBodySlice( }); } } + return newTables; +} - if (newTables.length) { - docBody.tables = newTables; - } - +export function getParagraphsSlice( + body: IDocumentBody, + startOffset: number, + endOffset: number +) { + const { paragraphs = [] } = body; const newParagraphs: IParagraph[] = []; for (const paragraph of paragraphs) { @@ -115,12 +119,19 @@ export function getBodySlice( } if (newParagraphs.length) { - docBody.paragraphs = newParagraphs.map((p) => ({ + return newParagraphs.map((p) => ({ ...p, startIndex: p.startIndex - startOffset, })); } +} +export function getSectionBreakSlice( + body: IDocumentBody, + startOffset: number, + endOffset: number +) { + const { sectionBreaks = [] } = body; const newSectionBreaks: ISectionBreak[] = []; for (const sectionBreak of sectionBreaks) { @@ -131,11 +142,57 @@ export function getBodySlice( } if (newSectionBreaks.length) { - docBody.sectionBreaks = newSectionBreaks.map((sb) => ({ + return newSectionBreaks.map((sb) => ({ ...sb, startIndex: sb.startIndex - startOffset, })); } +} + +export function getCustomBlockSlice( + body: IDocumentBody, + startOffset: number, + endOffset: number +) { + const { customBlocks = [] } = body; + const newCustomBlocks: ICustomBlock[] = []; + + for (const block of customBlocks) { + const { startIndex } = block; + if (startIndex >= startOffset && startIndex <= endOffset) { + newCustomBlocks.push(Tools.deepClone(block)); + } + } + + if (newCustomBlocks.length) { + return newCustomBlocks.map((b) => ({ + ...b, + startIndex: b.startIndex - startOffset, + })); + } +} + +export function getBodySlice( + body: IDocumentBody, + startOffset: number, + endOffset: number, + returnEmptyArray = true, + type = SliceBodyType.cut +): IDocumentBody { + const { dataStream } = body; + + const docBody: IDocumentBody = { + dataStream: dataStream.slice(startOffset, endOffset), + }; + + docBody.textRuns = getTextRunSlice(body, startOffset, endOffset, returnEmptyArray); + + const newTables = getTableSlice(body, startOffset, endOffset); + if (newTables.length) { + docBody.tables = newTables; + } + + docBody.paragraphs = getParagraphsSlice(body, startOffset, endOffset); if (type === SliceBodyType.cut) { const customDecorations = getCustomDecorationSlice(body, startOffset, endOffset); @@ -152,21 +209,7 @@ export function getBodySlice( docBody.customRanges = []; } - const newCustomBlocks: ICustomBlock[] = []; - - for (const block of customBlocks) { - const { startIndex } = block; - if (startIndex >= startOffset && startIndex <= endOffset) { - newCustomBlocks.push(Tools.deepClone(block)); - } - } - - if (newCustomBlocks.length) { - docBody.customBlocks = newCustomBlocks.map((b) => ({ - ...b, - startIndex: b.startIndex - startOffset, - })); - } + docBody.customBlocks = getCustomBlockSlice(body, startOffset, endOffset); return docBody; } diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 09fdd142cab..eeeb883b486 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -67,8 +67,19 @@ export { updateAttributeByDelete } from './docs/data-model/text-x/apply-utils/de export { updateAttributeByInsert } from './docs/data-model/text-x/apply-utils/insert-apply'; export { TextX } from './docs/data-model/text-x/text-x'; export type { TPriority } from './docs/data-model/text-x/text-x'; -export { composeBody, getBodySlice, SliceBodyType } from './docs/data-model/text-x/utils'; -export { getCustomDecorationSlice, getCustomRangeSlice, normalizeBody } from './docs/data-model/text-x/utils'; +export { + composeBody, + getBodySlice, + getCustomBlockSlice, + getCustomDecorationSlice, + getCustomRangeSlice, + getParagraphsSlice, + getSectionBreakSlice, + getTableSlice, + getTextRunSlice, + normalizeBody, + SliceBodyType, +} from './docs/data-model/text-x/utils'; export { EventState, EventSubject, fromEventSubject, type IEventObserver } from './observer/observable'; export { AuthzIoLocalService } from './services/authz-io/authz-io-local.service'; export { IAuthzIoService } from './services/authz-io/type'; From e27eb8129d358b643cf2aa0d0fbcb8aa994e1379 Mon Sep 17 00:00:00 2001 From: zhangw Date: Mon, 2 Dec 2024 23:32:57 +0800 Subject: [PATCH 036/134] feat: update --- .../src/views/formula-editor/index.tsx | 28 +++++++++++++------ 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/packages/sheets-formula-ui/src/views/formula-editor/index.tsx b/packages/sheets-formula-ui/src/views/formula-editor/index.tsx index 9d6ac6973be..1fe266c2091 100644 --- a/packages/sheets-formula-ui/src/views/formula-editor/index.tsx +++ b/packages/sheets-formula-ui/src/views/formula-editor/index.tsx @@ -24,7 +24,7 @@ import { DocBackScrollRenderController, IEditorService } from '@univerjs/docs-ui import { EMBEDDING_FORMULA_EDITOR } from '@univerjs/sheets-ui'; import { useEvent } from '@univerjs/ui'; import clsx from 'clsx'; -import React, { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react'; +import React, { useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react'; import { useEmitChange } from '../range-selector/hooks/useEmitChange'; import { useFocus } from '../range-selector/hooks/useFocus'; import { useFormulaToken } from '../range-selector/hooks/useFormulaToken'; @@ -122,6 +122,7 @@ export function FormulaEditor(props: IFormulaEditorProps) { const isSelecting = useFormulaSelecting(editorId, sequenceNodes); const [shouldMoveRefSelection, setShouldMoveRefSelection] = useState(false); const isFormula = useRef(false); + const highTextRef = useRef(''); useEffect(() => { if (isSelecting === FormulaSelectingType.NEED_ADD) { @@ -138,19 +139,28 @@ export function FormulaEditor(props: IFormulaEditorProps) { const highlightDoc = useDocHight('='); const highlightSheet = useSheetHighlight(unitId); - const highligh = useCallback((text: string, isNeedResetSelection: boolean = true, resetTextRun = true) => { + const highligh = useEvent((text: string, isNeedResetSelection: boolean = true) => { if (!editor) { return; } - const sequenceNodes = getFormulaToken(text); - const ranges = highlightDoc(editor, sequenceNodes, isNeedResetSelection, resetTextRun); + const preText = highTextRef.current; + highTextRef.current = text; + const formulaBody = text.slice(1); + const sequenceNodes = getFormulaToken(text[0] === '=' ? formulaBody : ''); + const ranges = highlightDoc( + editor, + sequenceNodes, + isNeedResetSelection, + // remove equals need to remove highlight style + preText.slice(1) === text && preText[0] === '=' + ); highlightSheet(isFocus ? ranges : []); - }, [editor, getFormulaToken, highlightDoc, highlightSheet, isFocus]); + }); useEffect(() => { - highligh(formulaWithoutEqualSymbol, false, isFormula.current && !formulaWithoutEqualSymbol); + highligh(formulaText, false, isFormula.current && !formulaWithoutEqualSymbol); isFormula.current = Boolean(formulaWithoutEqualSymbol); - }, [highligh, formulaWithoutEqualSymbol, isFocus]); + }, [highligh, formulaText, isFocus, formulaWithoutEqualSymbol]); useVerify(isFocus, onVerify, formulaText); const focus = useFocus(editor); @@ -188,7 +198,7 @@ export function FormulaEditor(props: IFormulaEditorProps) { const handleSelectionChange = useEvent((refString: string, offset: number, isEnd: boolean) => { needEmit(); - highligh(refString); + highligh(`=${refString}`); if (isEnd) { focus(); if (offset !== -1) { @@ -248,7 +258,7 @@ export function FormulaEditor(props: IFormulaEditorProps) { } resetFormulaSearch(); focus(); - highligh(res.text); + highligh(`=${res.text}`); } }; From fbfa9d054223643c702d061764c449162b58e04f Mon Sep 17 00:00:00 2001 From: zhangw Date: Mon, 2 Dec 2024 23:34:58 +0800 Subject: [PATCH 037/134] feat: update --- .../sheets-formula-ui/src/views/formula-editor/index.tsx | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/packages/sheets-formula-ui/src/views/formula-editor/index.tsx b/packages/sheets-formula-ui/src/views/formula-editor/index.tsx index 1fe266c2091..5e49cc6e0d3 100644 --- a/packages/sheets-formula-ui/src/views/formula-editor/index.tsx +++ b/packages/sheets-formula-ui/src/views/formula-editor/index.tsx @@ -121,7 +121,6 @@ export function FormulaEditor(props: IFormulaEditorProps) { const sequenceNodes = useMemo(() => getFormulaToken(formulaWithoutEqualSymbol), [formulaWithoutEqualSymbol, getFormulaToken]); const isSelecting = useFormulaSelecting(editorId, sequenceNodes); const [shouldMoveRefSelection, setShouldMoveRefSelection] = useState(false); - const isFormula = useRef(false); const highTextRef = useRef(''); useEffect(() => { @@ -145,8 +144,7 @@ export function FormulaEditor(props: IFormulaEditorProps) { } const preText = highTextRef.current; highTextRef.current = text; - const formulaBody = text.slice(1); - const sequenceNodes = getFormulaToken(text[0] === '=' ? formulaBody : ''); + const sequenceNodes = getFormulaToken(text[0] === '=' ? text.slice(1) : ''); const ranges = highlightDoc( editor, sequenceNodes, @@ -158,9 +156,8 @@ export function FormulaEditor(props: IFormulaEditorProps) { }); useEffect(() => { - highligh(formulaText, false, isFormula.current && !formulaWithoutEqualSymbol); - isFormula.current = Boolean(formulaWithoutEqualSymbol); - }, [highligh, formulaText, isFocus, formulaWithoutEqualSymbol]); + highligh(formulaText, false); + }, [highligh, formulaText, isFocus]); useVerify(isFocus, onVerify, formulaText); const focus = useFocus(editor); From b700251adb23c1fafec3229c1ea2e99b14f4b10c Mon Sep 17 00:00:00 2001 From: zhangw Date: Tue, 3 Dec 2024 15:03:22 +0800 Subject: [PATCH 038/134] feat: update --- packages/ui/src/controllers/menus/menus.ts | 32 ++++++++++------------ 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/packages/ui/src/controllers/menus/menus.ts b/packages/ui/src/controllers/menus/menus.ts index 22bcc2eee04..eef9b76740f 100644 --- a/packages/ui/src/controllers/menus/menus.ts +++ b/packages/ui/src/controllers/menus/menus.ts @@ -18,44 +18,40 @@ import type { IAccessor } from '@univerjs/core'; import type { IMenuButtonItem } from '../../services/menu/menu'; import { EDITOR_ACTIVATED, FOCUSING_FX_BAR_EDITOR, IContextService, IUndoRedoService, RedoCommand, UndoCommand } from '@univerjs/core'; -import { combineLatest } from 'rxjs'; -import { filter, map } from 'rxjs/operators'; +import { combineLatest, merge, of } from 'rxjs'; +import { map } from 'rxjs/operators'; import { MenuItemType } from '../../services/menu/menu'; -export function UndoMenuItemFactory(accessor: IAccessor): IMenuButtonItem { +const undoRedoDisableFactory$ = (accessor: IAccessor) => { const undoRedoService = accessor.get(IUndoRedoService); const contextService = accessor.get(IContextService); + return combineLatest([ + undoRedoService.undoRedoStatus$.pipe(map((v) => v.undos <= 0)), + merge([of({}), contextService.contextChanged$]), + ]).pipe(map(([undoDisable]) => { + return undoDisable || contextService.getContextValue(EDITOR_ACTIVATED) || contextService.getContextValue(FOCUSING_FX_BAR_EDITOR); + })); +}; + +export function UndoMenuItemFactory(accessor: IAccessor): IMenuButtonItem { return { id: UndoCommand.id, type: MenuItemType.BUTTON, icon: 'UndoSingle', title: 'Undo', tooltip: 'toolbar.undo', - disabled$: combineLatest([ - undoRedoService.undoRedoStatus$.pipe(map((v) => v.undos <= 0)), - contextService.contextChanged$.pipe(filter((key) => Object.hasOwnProperty.call(key, EDITOR_ACTIVATED) || Object.hasOwnProperty.call(key, FOCUSING_FX_BAR_EDITOR))), - ]).pipe(map(([undoDisable]) => { - return undoDisable || contextService.getContextValue(EDITOR_ACTIVATED) || contextService.getContextValue(FOCUSING_FX_BAR_EDITOR); - })), + disabled$: undoRedoDisableFactory$(accessor), }; } export function RedoMenuItemFactory(accessor: IAccessor): IMenuButtonItem { - const undoRedoService = accessor.get(IUndoRedoService); - const contextService = accessor.get(IContextService); - return { id: RedoCommand.id, type: MenuItemType.BUTTON, icon: 'RedoSingle', title: 'Redo', tooltip: 'toolbar.redo', - disabled$: combineLatest([ - undoRedoService.undoRedoStatus$.pipe(map((v) => v.undos <= 0)), - contextService.contextChanged$.pipe(filter((key) => Object.hasOwnProperty.call(key, EDITOR_ACTIVATED) || Object.hasOwnProperty.call(key, FOCUSING_FX_BAR_EDITOR))), - ]).pipe(map(([undoDisable]) => { - return undoDisable || contextService.getContextValue(EDITOR_ACTIVATED) || contextService.getContextValue(FOCUSING_FX_BAR_EDITOR); - })), + disabled$: undoRedoDisableFactory$(accessor), }; } From 9093fcd395c086e12b542f8d8e407283d776ef93 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Tue, 3 Dec 2024 07:11:03 +0000 Subject: [PATCH 039/134] chore(snapshots): update snapshots --- .../default-doc-ci-chromium-linux.png | Bin 126529 -> 126832 bytes ...fault-sheet-fullpage-ci-chromium-linux.png | Bin 73223 -> 72968 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/e2e/visual-comparison/docs/docs-visual-comparison.spec.ts-snapshots/default-doc-ci-chromium-linux.png b/e2e/visual-comparison/docs/docs-visual-comparison.spec.ts-snapshots/default-doc-ci-chromium-linux.png index 9b80b4f69a4c908db7e779f7721d94ccfa8355cb..e2284a2859cb9ca807205e9de2747724cc328a77 100644 GIT binary patch literal 126832 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
i$_7Nt;1fxyO?)TjDy@wZMD052637(AdG=o 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 literal 126529 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
<{w8p60rAqCJcwVlq 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 From 47967aeac7bae2f363049c85cb06ec9766595aff Mon Sep 17 00:00:00 2001 From: zhangw Date: Tue, 3 Dec 2024 16:03:11 +0800 Subject: [PATCH 040/134] feat: update --- .../src/controllers/editor/editing.render-controller.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 b4944419e40..1beff446a6e 100644 --- a/packages/sheets-ui/src/controllers/editor/editing.render-controller.ts +++ b/packages/sheets-ui/src/controllers/editor/editing.render-controller.ts @@ -642,8 +642,8 @@ export class EditingRenderController extends Disposable implements IRenderModule * The logic here predicts the user's first cursor movement behavior based on this rule */ private _cursorStateListener(d: DisposableCollection) { - const editorObject = this._getEditorObject()!; - if (!editorObject.document) return; + const editorObject = this._getEditorObject(); + if (!editorObject?.document) return; const { document: documentComponent } = editorObject; d.add(toDisposable(documentComponent.onPointerDown$.subscribeEvent(() => { From 06687cb1fa312dd5f6a0503da117eab797a29158 Mon Sep 17 00:00:00 2001 From: zhangw Date: Tue, 3 Dec 2024 19:46:12 +0800 Subject: [PATCH 041/134] feat: update --- packages/core/src/docs/data-model/document-data-model.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/core/src/docs/data-model/document-data-model.ts b/packages/core/src/docs/data-model/document-data-model.ts index b463640aeae..4f2ea59bfa9 100644 --- a/packages/core/src/docs/data-model/document-data-model.ts +++ b/packages/core/src/docs/data-model/document-data-model.ts @@ -25,7 +25,7 @@ import type { } from '../../types/interfaces/i-document-data'; import type { IPaddingData } from '../../types/interfaces/i-style-data'; import type { JSONXActions } from './json-x/json-x'; -import { BehaviorSubject, Subject } from 'rxjs'; +import { BehaviorSubject } from 'rxjs'; import { UnitModel, UniverInstanceType } from '../../common/unit'; import { Tools } from '../../shared/tools'; import { getEmptySnapshot } from './empty-snapshot'; @@ -228,7 +228,7 @@ export class DocumentDataModel extends DocumentDataModelSimple { headerModelMap: Map = new Map(); footerModelMap: Map = new Map(); - change$ = new Subject(); + change$ = new BehaviorSubject(0); constructor(snapshot: Partial) { super(Tools.isEmptyObject(snapshot) ? getEmptySnapshot() : snapshot); @@ -282,7 +282,7 @@ export class DocumentDataModel extends DocumentDataModelSimple { this.snapshot = { ...DEFAULT_DOC, ...snapshot }; this._initializeHeaderFooterModel(); - this.change$.next(Date.now()); + this.change$.next(this.change$.value + 1); } getSelfOrHeaderFooterModel(segmentId?: string) { @@ -317,7 +317,7 @@ export class DocumentDataModel extends DocumentDataModelSimple { this._initializeHeaderFooterModel(); } - this.change$.next(Date.now()); + this.change$.next(this.change$.value + 1); return this.snapshot; } From 3f92f36f36e542aa1bb89210a7a50bac662cef32 Mon Sep 17 00:00:00 2001 From: zhangw Date: Wed, 4 Dec 2024 01:02:25 +0800 Subject: [PATCH 042/134] feat: update --- .../editor/editing.render-controller.ts | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) 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 1beff446a6e..a7a23618ef6 100644 --- a/packages/sheets-ui/src/controllers/editor/editing.render-controller.ts +++ b/packages/sheets-ui/src/controllers/editor/editing.render-controller.ts @@ -690,21 +690,9 @@ export class EditingRenderController extends Disposable implements IRenderModule } private _emptyDocumentDataModel(removeStyle: boolean) { - const editCellState = this._editorBridgeService.getEditCellState(); - if (editCellState == null) { - return; - } - - const { documentLayoutObject } = editCellState; - const documentDataModel = documentLayoutObject.documentModel; - if (documentDataModel == null) { - return; - } - const empty = (documentDataModel: DocumentDataModel) => { const snapshot = Tools.deepClone(documentDataModel.getSnapshot()); const documentViewModel = this._getEditorViewModel(documentDataModel.getUnitId()); - if (documentViewModel == null) { return; } @@ -716,7 +704,8 @@ export class EditingRenderController extends Disposable implements IRenderModule documentViewModel.reset(documentDataModel); }; - empty(documentDataModel); + const documentDataModel = this._univerInstanceService.getUnit(DOCS_NORMAL_EDITOR_UNIT_ID_KEY, UniverInstanceType.UNIVER_DOC); + documentDataModel && empty(documentDataModel); const formulaDocument = this._univerInstanceService.getUnit(DOCS_FORMULA_BAR_EDITOR_UNIT_ID_KEY, UniverInstanceType.UNIVER_DOC); formulaDocument && empty(formulaDocument); } From eeebad6b7c75dbff59093102723f7d8cc2a5d032 Mon Sep 17 00:00:00 2001 From: zhangw Date: Wed, 4 Dec 2024 01:16:42 +0800 Subject: [PATCH 043/134] feat: update --- .../src/views/editor-container/EditorContainer.tsx | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/packages/sheets-ui/src/views/editor-container/EditorContainer.tsx b/packages/sheets-ui/src/views/editor-container/EditorContainer.tsx index c060bb5a9de..e4aa8cb52fe 100644 --- a/packages/sheets-ui/src/views/editor-container/EditorContainer.tsx +++ b/packages/sheets-ui/src/views/editor-container/EditorContainer.tsx @@ -18,7 +18,7 @@ import type { KeyCode } from '@univerjs/ui'; import { DOCS_NORMAL_EDITOR_UNIT_ID_KEY, ICommandService, IContextService, useDependency } from '@univerjs/core'; import { IEditorService } from '@univerjs/docs-ui'; import { DeviceInputEventType, FIX_ONE_PIXEL_BLUR_OFFSET } from '@univerjs/engine-render'; -import { ComponentManager, DISABLE_AUTO_FOCUS_KEY, MetaKeys, useEvent, useObservable } from '@univerjs/ui'; +import { ComponentManager, DISABLE_AUTO_FOCUS_KEY, MetaKeys, useEvent, useObservable, useSidebarClick } from '@univerjs/ui'; import React, { useEffect, useRef, useState } from 'react'; import { SetCellEditVisibleArrowOperation } from '../../commands/operations/cell-edit.operation'; import { EMBEDDING_FORMULA_EDITOR_COMPONENT_KEY } from '../../common/keys'; @@ -113,6 +113,16 @@ export const EditorContainer: React.FC = () => { // eslint-disable-next-line react-hooks/exhaustive-deps }, [disableAutoFocus, state]); + const handleClickSideBar = useEvent(() => { + editorBridgeService.changeVisible({ + visible: false, + eventType: DeviceInputEventType.PointerUp, + unitId: editState!.unitId, + }); + }); + + useSidebarClick(handleClickSideBar); + const keyCodeConfig = useKeyEventConfig(isRefSelecting, editState?.unitId!); const onMoveInEditor = useEvent((keycode: KeyCode, metaKey: MetaKeys) => { From 3cf1c55dec0baa423ba10a20fb592e5d87825bc5 Mon Sep 17 00:00:00 2001 From: zhangw Date: Wed, 4 Dec 2024 01:30:29 +0800 Subject: [PATCH 044/134] feat: update --- .../sheets-ui/src/commands/commands/inline-format.command.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/sheets-ui/src/commands/commands/inline-format.command.ts b/packages/sheets-ui/src/commands/commands/inline-format.command.ts index 55a84c03e85..2c0e06816b2 100644 --- a/packages/sheets-ui/src/commands/commands/inline-format.command.ts +++ b/packages/sheets-ui/src/commands/commands/inline-format.command.ts @@ -15,7 +15,7 @@ */ import type { ICommand } from '@univerjs/core'; -import { CommandType, EDITOR_ACTIVATED, ICommandService, IContextService } from '@univerjs/core'; +import { CommandType, EDITOR_ACTIVATED, ICommandService, IContextService, ThemeService } from '@univerjs/core'; import { SetInlineFormatBoldCommand, SetInlineFormatFontFamilyCommand, SetInlineFormatFontSizeCommand, SetInlineFormatItalicCommand, SetInlineFormatStrikethroughCommand, SetInlineFormatSubscriptCommand, SetInlineFormatSuperscriptCommand, SetInlineFormatTextColorCommand, SetInlineFormatUnderlineCommand } from '@univerjs/docs-ui'; import { SetBoldCommand, @@ -184,11 +184,12 @@ export const ResetRangeTextColorCommand: ICommand = { const commandService = accessor.get(ICommandService); const contextService = accessor.get(IContextService); const isCellEditorFocus = contextService.getContextValue(EDITOR_ACTIVATED); + const themeService = accessor.get(ThemeService); if (isCellEditorFocus) { return commandService.executeCommand(SetInlineFormatTextColorCommand.id, { value: null }); } - return commandService.executeCommand(SetTextColorCommand.id, { value: null }); + return commandService.executeCommand(SetTextColorCommand.id, { value: themeService.getCurrentTheme().textColor }); }, }; From a4c136fb0095b8b7726fb1190d15db5cbac0281d Mon Sep 17 00:00:00 2001 From: zhangw Date: Wed, 4 Dec 2024 14:49:50 +0800 Subject: [PATCH 045/134] feat: update --- .../src/views/formula-editor/index.tsx | 8 +++++++- .../src/views/editor-container/EditorContainer.tsx | 12 +++++++----- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/packages/sheets-formula-ui/src/views/formula-editor/index.tsx b/packages/sheets-formula-ui/src/views/formula-editor/index.tsx index 5e49cc6e0d3..c5c29372c10 100644 --- a/packages/sheets-formula-ui/src/views/formula-editor/index.tsx +++ b/packages/sheets-formula-ui/src/views/formula-editor/index.tsx @@ -227,7 +227,13 @@ export function FormulaEditor(props: IFormulaEditorProps) { isSingle: true, initialSnapshot: { id: editorId, - body: { dataStream: `${initValue}\r\n` }, + body: { + dataStream: `${initValue}\r\n`, + textRuns: [], + customBlocks: [], + customDecorations: [], + customRanges: [], + }, documentStyle: {}, }, }, formulaEditorContainerRef.current); diff --git a/packages/sheets-ui/src/views/editor-container/EditorContainer.tsx b/packages/sheets-ui/src/views/editor-container/EditorContainer.tsx index e4aa8cb52fe..ad04af4b5c9 100644 --- a/packages/sheets-ui/src/views/editor-container/EditorContainer.tsx +++ b/packages/sheets-ui/src/views/editor-container/EditorContainer.tsx @@ -114,11 +114,13 @@ export const EditorContainer: React.FC = () => { }, [disableAutoFocus, state]); const handleClickSideBar = useEvent(() => { - editorBridgeService.changeVisible({ - visible: false, - eventType: DeviceInputEventType.PointerUp, - unitId: editState!.unitId, - }); + if (editorBridgeService.isVisible().visible) { + editorBridgeService.changeVisible({ + visible: false, + eventType: DeviceInputEventType.PointerUp, + unitId: editState!.unitId, + }); + } }); useSidebarClick(handleClickSideBar); From 5b5716a846a9f4616e322cd198dfc738accc1b6a Mon Sep 17 00:00:00 2001 From: zhangw Date: Thu, 5 Dec 2024 02:07:29 +0800 Subject: [PATCH 046/134] feat: update --- .../src/components/panel/rule-edit/colorScale.tsx | 3 +++ .../src/components/panel/rule-edit/dataBar.tsx | 3 +++ .../src/components/panel/rule-edit/formula.tsx | 3 +++ .../src/components/panel/rule-edit/iconSet.tsx | 3 +++ .../src/components/panel/rule-edit/index.tsx | 3 +++ .../src/views/components/detail/index.tsx | 3 +++ .../views/components/formula-input/custom-formula-input.tsx | 3 +++ .../views/components/formula-input/list-formula-input.tsx | 3 +++ .../sheets-ui/src/views/defined-name/DefinedNameInput.tsx | 6 ++++++ 9 files changed, 30 insertions(+) diff --git a/packages/sheets-conditional-formatting-ui/src/components/panel/rule-edit/colorScale.tsx b/packages/sheets-conditional-formatting-ui/src/components/panel/rule-edit/colorScale.tsx index a7c0d0bf843..c6a6b48d844 100644 --- a/packages/sheets-conditional-formatting-ui/src/components/panel/rule-edit/colorScale.tsx +++ b/packages/sheets-conditional-formatting-ui/src/components/panel/rule-edit/colorScale.tsx @@ -57,6 +57,9 @@ const TextInput = (props: { id: string; type: CFValueType | 'none'; value: numbe const [isFocusFormulaEditor, isFocusFormulaEditorSet] = useState(false); useSidebarClick((e: MouseEvent) => { + if ((e.target as HTMLElement).tagName.toLocaleLowerCase() === 'canvas') { + return; + } const handleOutClick = formulaEditorActionsRef.current?.handleOutClick; handleOutClick && handleOutClick(e, () => isFocusFormulaEditorSet(false)); }); diff --git a/packages/sheets-conditional-formatting-ui/src/components/panel/rule-edit/dataBar.tsx b/packages/sheets-conditional-formatting-ui/src/components/panel/rule-edit/dataBar.tsx index 45af0a30b95..57b017edfe8 100644 --- a/packages/sheets-conditional-formatting-ui/src/components/panel/rule-edit/dataBar.tsx +++ b/packages/sheets-conditional-formatting-ui/src/components/panel/rule-edit/dataBar.tsx @@ -41,6 +41,9 @@ const InputText = (props: { disabled?: boolean; id: string; className: string; t const [isFocusFormulaEditor, isFocusFormulaEditorSet] = useState(false); useSidebarClick((e: MouseEvent) => { + if ((e.target as HTMLElement).tagName.toLocaleLowerCase() === 'canvas') { + return; + } const handleOutClick = formulaEditorActionsRef.current?.handleOutClick; handleOutClick && handleOutClick(e, () => isFocusFormulaEditorSet(false)); }); diff --git a/packages/sheets-conditional-formatting-ui/src/components/panel/rule-edit/formula.tsx b/packages/sheets-conditional-formatting-ui/src/components/panel/rule-edit/formula.tsx index b617f38cb24..cdc1787cc6c 100644 --- a/packages/sheets-conditional-formatting-ui/src/components/panel/rule-edit/formula.tsx +++ b/packages/sheets-conditional-formatting-ui/src/components/panel/rule-edit/formula.tsx @@ -89,6 +89,9 @@ export const FormulaStyleEditor = (props: IStyleEditorProps) => { }; useSidebarClick((e: MouseEvent) => { + if ((e.target as HTMLElement).tagName.toLocaleLowerCase() === 'canvas') { + return; + } const handleOutClick = formulaEditorActionsRef.current?.handleOutClick; handleOutClick && handleOutClick(e, () => isFocusFormulaEditorSet(false)); }); diff --git a/packages/sheets-conditional-formatting-ui/src/components/panel/rule-edit/iconSet.tsx b/packages/sheets-conditional-formatting-ui/src/components/panel/rule-edit/iconSet.tsx index 08f7e8d1dc0..75186d127b7 100644 --- a/packages/sheets-conditional-formatting-ui/src/components/panel/rule-edit/iconSet.tsx +++ b/packages/sheets-conditional-formatting-ui/src/components/panel/rule-edit/iconSet.tsx @@ -51,6 +51,9 @@ const TextInput = (props: { id: number; type: CFValueType; value: number | strin const [isFocusFormulaEditor, isFocusFormulaEditorSet] = useState(false); useSidebarClick((e: MouseEvent) => { + if ((e.target as HTMLElement).tagName.toLocaleLowerCase() === 'canvas') { + return; + } const handleOutClick = formulaEditorActionsRef.current?.handleOutClick; handleOutClick && handleOutClick(e, () => isFocusFormulaEditorSet(false)); }); diff --git a/packages/sheets-conditional-formatting-ui/src/components/panel/rule-edit/index.tsx b/packages/sheets-conditional-formatting-ui/src/components/panel/rule-edit/index.tsx index 70b4980ad2c..6f7db4be211 100644 --- a/packages/sheets-conditional-formatting-ui/src/components/panel/rule-edit/index.tsx +++ b/packages/sheets-conditional-formatting-ui/src/components/panel/rule-edit/index.tsx @@ -240,6 +240,9 @@ export const RuleEdit = (props: IRuleEditProps) => { }; useSidebarClick((e: MouseEvent) => { + if ((e.target as HTMLElement).tagName.toLocaleLowerCase() === 'canvas') { + return; + } const handleOutClick = rangeSelectorActionsRef.current?.handleOutClick; handleOutClick && handleOutClick(e, () => isFocusRangeSelectorSet(false)); }); diff --git a/packages/sheets-data-validation-ui/src/views/components/detail/index.tsx b/packages/sheets-data-validation-ui/src/views/components/detail/index.tsx index 62bca6e8fa6..ee9c2f96355 100644 --- a/packages/sheets-data-validation-ui/src/views/components/detail/index.tsx +++ b/packages/sheets-data-validation-ui/src/views/components/detail/index.tsx @@ -248,6 +248,9 @@ export function DataValidationDetail() { }; useSidebarClick((e: MouseEvent) => { + if ((e.target as HTMLElement).tagName.toLocaleLowerCase() === 'canvas') { + return; + } const handleOutClick = rangeSelectorActionsRef.current?.handleOutClick; handleOutClick && handleOutClick(e, () => isFocusRangeSelectorSet(false)); }); diff --git a/packages/sheets-data-validation-ui/src/views/components/formula-input/custom-formula-input.tsx b/packages/sheets-data-validation-ui/src/views/components/formula-input/custom-formula-input.tsx index 3f50e0e7279..63555c4d878 100644 --- a/packages/sheets-data-validation-ui/src/views/components/formula-input/custom-formula-input.tsx +++ b/packages/sheets-data-validation-ui/src/views/components/formula-input/custom-formula-input.tsx @@ -26,6 +26,9 @@ export function CustomFormulaInput(props: IFormulaInputProps) { const [isFocusFormulaEditor, isFocusFormulaEditorSet] = useState(false); useSidebarClick((e: MouseEvent) => { + if ((e.target as HTMLElement).tagName.toLocaleLowerCase() === 'canvas') { + return; + } const handleOutClick = formulaEditorActionsRef.current?.handleOutClick; handleOutClick && handleOutClick(e, () => isFocusFormulaEditorSet(false)); }); diff --git a/packages/sheets-data-validation-ui/src/views/components/formula-input/list-formula-input.tsx b/packages/sheets-data-validation-ui/src/views/components/formula-input/list-formula-input.tsx index 9447de61217..6af753ccb8e 100644 --- a/packages/sheets-data-validation-ui/src/views/components/formula-input/list-formula-input.tsx +++ b/packages/sheets-data-validation-ui/src/views/components/formula-input/list-formula-input.tsx @@ -316,6 +316,9 @@ export function ListFormulaInput(props: IFormulaInputProps) { const [isFocusFormulaEditor, isFocusFormulaEditorSet] = useState(false); useSidebarClick((e: MouseEvent) => { + if ((e.target as HTMLElement).tagName.toLocaleLowerCase() === 'canvas') { + return; + } const handleOutClick = formulaEditorActionsRef.current?.handleOutClick; handleOutClick && handleOutClick(e, () => isFocusFormulaEditorSet(false)); }); diff --git a/packages/sheets-ui/src/views/defined-name/DefinedNameInput.tsx b/packages/sheets-ui/src/views/defined-name/DefinedNameInput.tsx index 7d920212dfe..c764fb699aa 100644 --- a/packages/sheets-ui/src/views/defined-name/DefinedNameInput.tsx +++ b/packages/sheets-ui/src/views/defined-name/DefinedNameInput.tsx @@ -211,11 +211,17 @@ export const DefinedNameInput = (props: IDefinedNameInputProps) => { const [isFocusFormulaEditor, isFocusFormulaEditorSet] = useState(false); useSidebarClick((e: MouseEvent) => { + if ((e.target as HTMLElement).tagName.toLocaleLowerCase() === 'canvas') { + return; + } const handleOutClick = rangeSelectorActionsRef.current?.handleOutClick; handleOutClick && handleOutClick(e, () => isFocusRangeSelectorSet(false)); }); useSidebarClick((e: MouseEvent) => { + if ((e.target as HTMLElement).tagName.toLocaleLowerCase() === 'canvas') { + return; + } const handleOutClick = formulaEditorActionsRef.current?.handleOutClick; handleOutClick && handleOutClick(e, () => isFocusFormulaEditorSet(false)); }); From 2f94d88532b5cdc7c60381766430569e34723746 Mon Sep 17 00:00:00 2001 From: zhangw Date: Wed, 11 Dec 2024 17:00:24 +0800 Subject: [PATCH 047/134] fix: https://github.com/dream-num/univer-pro/issues/3752 --- .../src/views/components/list-dropdown/index.tsx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/sheets-data-validation-ui/src/views/components/list-dropdown/index.tsx b/packages/sheets-data-validation-ui/src/views/components/list-dropdown/index.tsx index 8c6ddfe2ec9..a817e56c840 100644 --- a/packages/sheets-data-validation-ui/src/views/components/list-dropdown/index.tsx +++ b/packages/sheets-data-validation-ui/src/views/components/list-dropdown/index.tsx @@ -156,9 +156,7 @@ export function ListDropDown(props: IDropdownComponentProps) { const params = command.params as IRichTextEditingMutationParams; const { unitId } = params; const unit = instanceService.getUnit(unitId, UniverInstanceType.UNIVER_DOC); - if (!unit) { - return; - } + if (!unit || !editorBridgeService.isVisible().visible) return; const text = BuildTextUtils.transform.getPlainText(unit.getSnapshot().body?.dataStream ?? ''); setEditingText(text); } @@ -167,7 +165,7 @@ export function ListDropDown(props: IDropdownComponentProps) { return () => { dispose.dispose(); }; - }, [commandService, instanceService]); + }, [commandService, editorBridgeService, instanceService]); if (!worksheet) { return null; From ce24c0667c2f9707a976636c6b8186fcb8186d7a Mon Sep 17 00:00:00 2001 From: zhangw Date: Wed, 11 Dec 2024 18:48:45 +0800 Subject: [PATCH 048/134] feat: update --- .../editor/editing.render-controller.ts | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) 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 a7a23618ef6..6e472088a29 100644 --- a/packages/sheets-ui/src/controllers/editor/editing.render-controller.ts +++ b/packages/sheets-ui/src/controllers/editor/editing.render-controller.ts @@ -276,10 +276,17 @@ export class EditingRenderController extends Disposable implements IRenderModule snapshot: Tools.deepClone(documentModel!.getSnapshot()), }); this._contextService.setContextValue(FOCUSING_EDITOR_BUT_HIDDEN, true); - this._textSelectionManagerService.replaceTextRanges([{ - startOffset: 0, - endOffset: 0, - }]); + this._textSelectionManagerService.replaceDocRanges( + [{ + startOffset: 0, + endOffset: 0, + }], + { + unitId: DOCS_NORMAL_EDITOR_UNIT_ID_KEY, + subUnitId: DOCS_NORMAL_EDITOR_UNIT_ID_KEY, + } + ) + ; const docSelectionRenderManager = this._renderManagerService.getCurrentTypeOfRenderer(UniverInstanceType.UNIVER_DOC)?.with(DocSelectionRenderService); From 9ef9f63eef4d719dde9c715f0df4a467afcaa5c2 Mon Sep 17 00:00:00 2001 From: zhangw Date: Wed, 11 Dec 2024 19:40:42 +0800 Subject: [PATCH 049/134] feat: update --- .../src/controllers/editor/editing.render-controller.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) 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 6e472088a29..fd89c13bab0 100644 --- a/packages/sheets-ui/src/controllers/editor/editing.render-controller.ts +++ b/packages/sheets-ui/src/controllers/editor/editing.render-controller.ts @@ -275,6 +275,7 @@ export class EditingRenderController extends Disposable implements IRenderModule unitId: editorUnitId, snapshot: Tools.deepClone(documentModel!.getSnapshot()), }); + this._contextService.setContextValue(FOCUSING_EDITOR_BUT_HIDDEN, true); this._textSelectionManagerService.replaceDocRanges( [{ @@ -285,9 +286,9 @@ export class EditingRenderController extends Disposable implements IRenderModule unitId: DOCS_NORMAL_EDITOR_UNIT_ID_KEY, subUnitId: DOCS_NORMAL_EDITOR_UNIT_ID_KEY, } - ) - ; + ); + this._univerInstanceService.setCurrentUnitForType(DOCS_NORMAL_EDITOR_UNIT_ID_KEY); const docSelectionRenderManager = this._renderManagerService.getCurrentTypeOfRenderer(UniverInstanceType.UNIVER_DOC)?.with(DocSelectionRenderService); if (docSelectionRenderManager) { From a41e54dc6397ff43812346d345b1c42d4bffdc29 Mon Sep 17 00:00:00 2001 From: zhangw Date: Thu, 12 Dec 2024 18:05:06 +0800 Subject: [PATCH 050/134] feat: update --- .../docs-ui/src/services/editor/editor.ts | 4 +- .../selection/doc-selection-render.service.ts | 5 - .../src/controllers/prompt.controller.ts | 2035 ----------------- .../src/sheets-formula-ui.plugin.ts | 3 - 4 files changed, 2 insertions(+), 2045 deletions(-) delete mode 100644 packages/sheets-formula-ui/src/controllers/prompt.controller.ts diff --git a/packages/docs-ui/src/services/editor/editor.ts b/packages/docs-ui/src/services/editor/editor.ts index 5836342b96a..e038aa75155 100644 --- a/packages/docs-ui/src/services/editor/editor.ts +++ b/packages/docs-ui/src/services/editor/editor.ts @@ -17,7 +17,7 @@ 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 { DEFAULT_STYLES, Disposable, UniverInstanceType } from '@univerjs/core'; +import { DEFAULT_STYLES, Disposable, isInternalEditorID, UniverInstanceType } from '@univerjs/core'; import { KeyCode } from '@univerjs/ui'; import { merge, type Observable, Subject } from 'rxjs'; import { filter } from 'rxjs/operators'; @@ -471,7 +471,7 @@ export class Editor extends Disposable implements IEditor { /** @deprecated */ isSheetEditor() { - return this._param.isSheetEditor === true; + return isInternalEditorID(this._getEditorId()); } /** @deprecated */ diff --git a/packages/docs-ui/src/services/selection/doc-selection-render.service.ts b/packages/docs-ui/src/services/selection/doc-selection-render.service.ts index b3df336231d..4372db3da58 100644 --- a/packages/docs-ui/src/services/selection/doc-selection-render.service.ts +++ b/packages/docs-ui/src/services/selection/doc-selection-render.service.ts @@ -99,7 +99,6 @@ export class DocSelectionRenderService extends RxDisposable implements IRenderMo private _isIMEInputApply = false; private _scenePointerMoveSubs: Array = []; private _scenePointerUpSubs: Array = []; - private _editorFocusing = true; // When the user switches editors, whether to clear the doc ranges. private _reserveRanges = false; @@ -323,9 +322,6 @@ export class DocSelectionRenderService extends RxDisposable implements IRenderMo } focus(): void { - if (!this._editorFocusing) { - return; - } this._input.focus(); } @@ -435,7 +431,6 @@ export class DocSelectionRenderService extends RxDisposable implements IRenderMo // Handle pointer down. // eslint-disable-next-line max-lines-per-function, complexity __onPointDown(evt: IPointerEvent | IMouseEvent) { - this._editorFocusing = true; const { scene, mainComponent } = this._context; const skeleton = this._docSkeletonManagerService.getSkeleton(); diff --git a/packages/sheets-formula-ui/src/controllers/prompt.controller.ts b/packages/sheets-formula-ui/src/controllers/prompt.controller.ts deleted file mode 100644 index 67c6362d050..00000000000 --- a/packages/sheets-formula-ui/src/controllers/prompt.controller.ts +++ /dev/null @@ -1,2035 +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. - */ - -// FIXME: why so many calling to close the editor here? - -import type { - DocumentDataModel, - ICommandInfo, - IDisposable, - IRange, - IRangeWithCoord, - ITextRun, - Nullable, - Workbook, -} from '@univerjs/core'; -import type { Editor } from '@univerjs/docs-ui'; -import type { IAbsoluteRefTypeForRange, ISequenceNode } from '@univerjs/engine-formula'; -import type { - ISelectionWithStyle, -} from '@univerjs/sheets'; -import type { EditorBridgeService, SelectionControl } from '@univerjs/sheets-ui'; -import type { ISelectEditorFormulaOperationParam } from '../commands/operations/editor-formula.operation'; -import { - AbsoluteRefType, - Direction, - Disposable, - DisposableCollection, - DOCS_FORMULA_BAR_EDITOR_UNIT_ID_KEY, - DOCS_NORMAL_EDITOR_UNIT_ID_KEY, - DOCS_ZEN_EDITOR_UNIT_ID_KEY, - FOCUSING_EDITOR_INPUT_FORMULA, - FORMULA_EDITOR_ACTIVATED, - ICommandService, - IContextService, - Inject, - isFormulaString, - IUniverInstanceService, - RANGE_TYPE, - Rectangle, - ThemeService, - Tools, - UniverInstanceType, -} from '@univerjs/core'; -import { - DocSelectionManagerService, - DocSkeletonManagerService, -} from '@univerjs/docs'; -import { DocSelectionRenderService, IEditorService, MoveCursorOperation, ReplaceContentCommand } from '@univerjs/docs-ui'; -import { - compareToken, - deserializeRangeWithSheet, - generateStringWithSequence, - getAbsoluteRefTypeWitString, - LexerTreeBuilder, - matchRefDrawToken, - matchToken, - normalizeSheetName, - sequenceNodeType, - serializeRange, - serializeRangeToRefString, -} from '@univerjs/engine-formula'; -import { - DeviceInputEventType, - IRenderManagerService, -} from '@univerjs/engine-render'; -import { - convertSelectionDataToRange, - - DISABLE_NORMAL_SELECTIONS, - getPrimaryForRange, - IRefSelectionsService, - SelectionMoveType, - setEndForRange, - SheetsSelectionsService, -} from '@univerjs/sheets'; -import { IDescriptionService } from '@univerjs/sheets-formula'; - -import { - ExpandSelectionCommand, - getEditorObject, - IEditorBridgeService, - isEmbeddingFormulaEditor, - isRangeSelector, - JumpOver, - MoveSelectionCommand, - SheetCellEditorResizeService, - SheetSkeletonManagerService, -} from '@univerjs/sheets-ui'; -import { IContextMenuService, ILayoutService, KeyCode, MetaKeys, UNI_DISABLE_CHANGING_FOCUS_KEY } from '@univerjs/ui'; -import { distinctUntilChanged, distinctUntilKeyChanged, filter, merge } from 'rxjs'; -import { SelectEditorFormulaOperation } from '../commands/operations/editor-formula.operation'; -import { HelpFunctionOperation } from '../commands/operations/help-function.operation'; -import { ReferenceAbsoluteOperation } from '../commands/operations/reference-absolute.operation'; -import { SearchFunctionOperation } from '../commands/operations/search-function.operation'; -import { META_KEY_CTRL_AND_SHIFT } from '../common/prompt'; -import { genFormulaRefSelectionStyle } from '../common/selection'; -import { IFormulaPromptService } from '../services/prompt.service'; -import { RefSelectionsRenderService } from '../services/render-services/ref-selections.render-service'; - -interface IRefSelection { - refIndex: number; - themeColor: string; - token: string; -} - -enum ArrowMoveAction { - InitialState, - moveCursor, - moveRefReady, - movingRef, - exitInput, -} - -enum InputPanelState { - InitialState, - keyNormal, - keyArrow, - mouse, -} - -const sheetEditorUnitIds = [DOCS_FORMULA_BAR_EDITOR_UNIT_ID_KEY, DOCS_NORMAL_EDITOR_UNIT_ID_KEY]; - -export class PromptController extends Disposable { - private _listenInputCache: Set = new Set(); - private _formulaRefColors: string[] = []; - - private _previousSequenceNodes: Nullable>; - - private _previousRangesCount: number = 0; - - private _previousInsertRefStringIndex: Nullable; - private _currentInsertRefStringIndex: number = -1; - - private _arrowMoveActionState: ArrowMoveAction = ArrowMoveAction.InitialState; - - private _isSelectionMovingRefSelections: IRefSelection[] = []; - - private _stringColor = ''; - - private _numberColor = ''; - - private _insertSelections: ISelectionWithStyle[] = []; - - private _inputPanelState: InputPanelState = InputPanelState.InitialState; - - private _userCursorMove: boolean = false; - - private _previousEditorUnitId: Nullable; - - private _existsSequenceNode = false; - - // TODO@wzhudev: selection render service would be a render unit, we we cannot - // easily access it here. - private get _selectionRenderService(): RefSelectionsRenderService { - return this._renderManagerService.getRenderById( - this._univerInstanceService.getCurrentUnitForType(UniverInstanceType.UNIVER_SHEET)!.getUnitId() - )!.with(RefSelectionsRenderService); - } - - /** - * For multiple sheet instances. - */ - private get _allSelectionRenderServices(): RefSelectionsRenderService[] { - return this._renderManagerService.getAllRenderersOfType(UniverInstanceType.UNIVER_SHEET) - .map((renderer) => renderer.with(RefSelectionsRenderService)); - } - - constructor( - @ICommandService private readonly _commandService: ICommandService, - @IContextService private readonly _contextService: IContextService, - @Inject(IEditorBridgeService) private readonly _editorBridgeService: EditorBridgeService, - @Inject(IFormulaPromptService) private readonly _formulaPromptService: IFormulaPromptService, - @Inject(LexerTreeBuilder) private readonly _lexerTreeBuilder: LexerTreeBuilder, - @IRenderManagerService private readonly _renderManagerService: IRenderManagerService, - @Inject(ThemeService) private readonly _themeService: ThemeService, - @Inject(SheetsSelectionsService) private readonly _sheetsSelectionsService: SheetsSelectionsService, - @IRefSelectionsService private readonly _refSelectionsService: SheetsSelectionsService, - @IUniverInstanceService private readonly _univerInstanceService: IUniverInstanceService, - @Inject(IDescriptionService) private readonly _descriptionService: IDescriptionService, - @Inject(DocSelectionManagerService) private readonly _docSelectionManagerService: DocSelectionManagerService, - @IContextMenuService private readonly _contextMenuService: IContextMenuService, - @IEditorService private readonly _editorService: IEditorService, - @ILayoutService private readonly _layoutService: ILayoutService - - ) { - super(); - - // this._initialize(); - } - - override dispose(): void { - this._formulaRefColors = []; - this._resetTemp(); - } - - private _resetTemp() { - this._previousSequenceNodes = null; - - this._previousInsertRefStringIndex = null; - - this._isSelectionMovingRefSelections = []; - - this._previousRangesCount = 0; - - this._currentInsertRefStringIndex = -1; - } - - private _initialize(): void { - // this._initialCursorSync(); - // this._initAcceptFormula(); - // this._initialFormulaTheme(); - // this._initSelectionsEndListener(); - this._closeRangePromptWhenEditorInvisible(); - // this._initialEditorInputChange(); - // this._commandExecutedListener(); - // this._cursorStateListener(); - // this._inputFormulaListener(); - // this._userMouseListener(); - // this._initialChangeEditor(); - } - - private _initialFormulaTheme() { - const style = this._themeService.getCurrentTheme(); - - this._formulaRefColors = [ - style.loopColor1, - style.loopColor2, - style.loopColor3, - style.loopColor4, - style.loopColor5, - style.loopColor6, - style.loopColor7, - style.loopColor8, - style.loopColor9, - style.loopColor10, - style.loopColor11, - style.loopColor12, - ]; - - this._numberColor = style.hyacinth700; - - this._stringColor = style.verdancy800; - } - - private _initialCursorSync() { - this.disposeWithMe( - this._docSelectionManagerService.textSelection$ - .pipe( - filter((item) => { - return !isRangeSelector(item.unitId) && !isEmbeddingFormulaEditor(item.unitId); - }) - ) - .subscribe((params) => { - if (params?.unitId == null) { - return; - } - const editor = this._editorService.getEditor(params.unitId); - if (!editor - || editor.onlyInputContent() - || (editor.isSheetEditor() && !this._isFormulaEditorActivated()) - // Remove this latter. - || editor.params.scrollBar - ) { - return; - } - - const onlyInputRange = editor.onlyInputRange(); - - // @ts-ignore - if (params?.options?.fromSelection) { - return; - } else { - this._quitSelectingMode(); - } - - this._contextSwitch(); - this._checkShouldEnterSelectingMode(onlyInputRange); - - if (this._formulaPromptService.isLockedSelectionChange()) { - return; - } - - this._highlightFormula(); - - if (onlyInputRange) { - return; - } - - // TODO@Dushusir: use real text info - this._changeFunctionPanelState(); - }) - ); - } - - private _initialEditorInputChange() { - const arrows = [KeyCode.ARROW_DOWN, KeyCode.ARROW_UP, KeyCode.ARROW_LEFT, KeyCode.ARROW_RIGHT, KeyCode.CTRL, KeyCode.SHIFT]; - // TODO: @runzhe Should there be a registration mechanism, rather than a unified process here? - this._univerInstanceService.getCurrentTypeOfUnit$(UniverInstanceType.UNIVER_DOC) - .pipe(filter((documentDataModel) => { - const unitId = documentDataModel?.getUnitId() || ''; - return !isRangeSelector(unitId) && !isEmbeddingFormulaEditor(unitId); - })) - .subscribe((documentDataModel) => { - const unitId = documentDataModel?.getUnitId(); - - if (unitId == null) { - return; - } - - if (this._listenInputCache.has(unitId)) { - return; - } - - const editor = this._editorService.getEditor(unitId); - - if (editor == null) { - return; - } - - const docSelectionRenderService = this._renderManagerService.getRenderById(unitId)?.with(DocSelectionRenderService); - - if (docSelectionRenderService) { - this.disposeWithMe( - docSelectionRenderService.onInputBefore$.subscribe((param) => { - this._previousSequenceNodes = null; - this._previousInsertRefStringIndex = null; - - this._selectionRenderService.setSkipLastEnabled(true); - - const event = param?.event as KeyboardEvent; - if (!event) return; - - if (!arrows.includes(event.which)) { - if (this._arrowMoveActionState !== ArrowMoveAction.moveCursor) { - this._arrowMoveActionState = ArrowMoveAction.moveRefReady; - } - - this._inputPanelState = InputPanelState.keyNormal; - } else { - this._inputPanelState = InputPanelState.keyArrow; - } - - if (event.which !== KeyCode.F4) { - this._userCursorMove = false; - } - }) - ); - } - - this._listenInputCache.add(unitId); - }); - } - - private _closeRangePromptWhenEditorInvisible() { - // NOTE: to be refactored - - this.disposeWithMe(this._editorBridgeService.afterVisible$ - .pipe(distinctUntilKeyChanged('visible')) - .subscribe((visibleParam) => { - if (!visibleParam.visible) this._closeRangePrompt(); - }) - - ); - - this.disposeWithMe(this._contextService.subscribeContextValue$(FORMULA_EDITOR_ACTIVATED) - .pipe(distinctUntilChanged()) - .subscribe((activated) => { - if (!activated) this._closeRangePrompt(); - })); - } - - private _initialChangeEditor() { - this.disposeWithMe( - this._univerInstanceService.getCurrentTypeOfUnit$(UniverInstanceType.UNIVER_DOC) - .pipe(filter((documentDataModel) => { - const editorId = documentDataModel?.getUnitId() || ''; - return !isRangeSelector(editorId) && !isEmbeddingFormulaEditor(editorId); - })) - .subscribe((documentDataModel) => { - if (documentDataModel == null) { - return; - } - - const editorId = documentDataModel.getUnitId(); - - if (!this._editorService.isEditor(editorId) || this._previousEditorUnitId === editorId) { - return; - } - - if (!this._editorService.isSheetEditor(editorId)) { - this._closeRangePrompt(editorId); - this._previousEditorUnitId = editorId; - } - }) - ); - - this.disposeWithMe( - this._editorService.closeRangePrompt$.subscribe(() => { - if (!this._editorService.getSpreadsheetFocusState() || !this._formulaPromptService.isLockedSelectionInsert()) { - this._closeRangePrompt(); - } - }) - ); - } - - private _closeRangePrompt(editorId: Nullable) { - const docId = editorId || this._univerInstanceService.getCurrentUnitForType(UniverInstanceType.UNIVER_DOC)?.getUnitId() || ''; - if (isRangeSelector(docId) || isEmbeddingFormulaEditor(docId) || docId === DOCS_ZEN_EDITOR_UNIT_ID_KEY) { - return; - } - this._insertSelections = []; - this._refSelectionsService.clear(); - - if (editorId && this._editorService.isSheetEditor(editorId)) { - this._updateEditorModel('\r\n', []); - } - - this._contextService.setContextValue(FOCUSING_EDITOR_INPUT_FORMULA, false); - this._contextService.setContextValue(DISABLE_NORMAL_SELECTIONS, false); - this._contextService.setContextValue(UNI_DISABLE_CHANGING_FOCUS_KEY, false); - - this._quitSelectingMode(); - - this._resetTemp(); - - this._hideFunctionPanel(); - } - - private _initSelectionsEndListener() { - const d = new DisposableCollection(); - - this.disposeWithMe(this._refSelectionsService.selectionMoveEnd$.subscribe((selections) => { - d.dispose(); - - if (selections.length === 0) return; - // Theme color should be set when SelectionControl is created, it's too late to set theme color at selection End(pointerup). - // The logic below has been moved to syncToEditor. - // this._allSelectionRenderServices.forEach((r) => this._updateRefSelectionStyle(r, this._isSelectionMovingRefSelections)); - const docID = this._univerInstanceService.getCurrentUnitForType(UniverInstanceType.UNIVER_DOC)?.getUnitId() || ''; - if (isRangeSelector(docID) || isEmbeddingFormulaEditor(docID)) { - return; - } - const selectionControls = this._allSelectionRenderServices.map((s) => s.getSelectionControls()).flat(); - selectionControls.forEach((c) => { - c.disableHelperSelection(); - d.add(merge(c.selectionMoving$, c.selectionScaling$).subscribe((toRange) => { - const docID = this._univerInstanceService.getCurrentUnitForType(UniverInstanceType.UNIVER_DOC)?.getUnitId() || ''; - if (isRangeSelector(docID) || isEmbeddingFormulaEditor(docID)) { - d.dispose(); - this._formulaPromptService.disableLockedSelectionChange(); - return; - } - this._onSelectionControlChange(toRange, c); - })); - d.add(merge(c.selectionMoveEnd$, c.selectionScaled$).subscribe(() => { - this._formulaPromptService.disableLockedSelectionChange(); - })); - }); - })); - } - - /** - * For interaction with mouse & keyboard shortcuts on spreadsheet. Not in formula editor. - */ - private _updateSelecting(selectionsWithStyles: ISelectionWithStyle[], performInsertion: boolean = false) { - if (selectionsWithStyles.length === 0) return; - if (this._editorService.selectionChangingState() && !this._formulaPromptService.isLockedSelectionInsert()) return; - - this._insertControlSelections(selectionsWithStyles); - - if (performInsertion) { - const currentSelection = selectionsWithStyles[selectionsWithStyles.length - 1]; - this._insertControlSelectionReplace(currentSelection); - } - } - - private _currentlyWorkingRefRenderer: Nullable = null; - private _selectionsChangeDisposables: Nullable; - private _enableRefSelectionsRenderService() { - const d = this._selectionsChangeDisposables = new DisposableCollection(); - this._allSelectionRenderServices.forEach((renderer) => { - d.add(renderer.enableSelectionChanging()); - - // When the current selections change, the ref string is updated without touch `IRefSelectionsService`. - d.add(renderer.selectionMoving$.subscribe((selections) => { - this._updateSelecting(selections.map((s) => convertSelectionDataToRange(s))); - })); - - // When the selection change begins, if other render service has last selection, - // it should be removed. - d.add(renderer.selectionMoveStart$.subscribe((selections) => { - const performInsertion = this._checkClearingLastSelection(renderer); - this._currentlyWorkingRefRenderer = renderer; - this._updateSelecting(selections.map((s) => convertSelectionDataToRange(s)), performInsertion); - })); - }); - } - - private _checkClearingLastSelection(renderer: RefSelectionsRenderService): boolean { - if (this._currentlyWorkingRefRenderer && this._currentlyWorkingRefRenderer !== renderer) { - this._currentlyWorkingRefRenderer.clearLastSelection(); - return false; - } - - return true; - } - - private _disposeSelectionsChangeListeners(): void { - this._selectionsChangeDisposables?.dispose(); - this._selectionsChangeDisposables = null; - } - - private _insertControlSelections(selections: ISelectionWithStyle[]) { - const currentSelection = selections[selections.length - 1]; - - this._resetSequenceNodes(selections.length); - - if ( - (selections.length === this._previousRangesCount || this._previousRangesCount === 0) && - this._previousSequenceNodes != null - ) { - this._insertControlSelectionReplace(currentSelection); - } else { - // Holding down ctrl causes an addition, requiring the ref string to be increased. - let insertNodes = this._formulaPromptService.getSequenceNodes()!; - const char = this._getCurrentChar()!; - - // To reset the cursor position when resetting the editor's content. - if (insertNodes.length === 0 && this._currentInsertRefStringIndex > 0) { - this._currentInsertRefStringIndex = -1; - } - - this._previousInsertRefStringIndex = this._currentInsertRefStringIndex; - - if (!matchRefDrawToken(char) && this._focusIsOnlyRange(selections.length)) { - this._formulaPromptService.insertSequenceString(this._currentInsertRefStringIndex, matchToken.COMMA); - insertNodes = this._formulaPromptService.getSequenceNodes(); - this._previousInsertRefStringIndex += 1; - } - - this._previousSequenceNodes = Tools.deepClone(insertNodes); - this._formulaPromptService.setSequenceNodes(insertNodes); - - const refString = this._generateRefString(currentSelection); - this._formulaPromptService.insertSequenceRef(this._previousInsertRefStringIndex, refString); - - this._selectionRenderService.setSkipLastEnabled(false); - } - - this._arrowMoveActionState = ArrowMoveAction.moveRefReady; - this._previousRangesCount = selections.length; - } - - private _initAcceptFormula() { - this.disposeWithMe( - this._formulaPromptService.acceptFormulaName$.subscribe((formulaString: string) => { - const activeRange = this._docSelectionManagerService.getActiveTextRange(); - - if (activeRange == null) { - this._hideFunctionPanel(); - return; - } - - const { startOffset } = activeRange; - - const lastSequenceNodes = this._formulaPromptService.getSequenceNodes(); - - const nodeIndex = this._formulaPromptService.getCurrentSequenceNodeIndex(startOffset - 2); - - const node = lastSequenceNodes[nodeIndex]; - - if (node == null || typeof node === 'string') { - this._hideFunctionPanel(); - return; - } - - const difference = formulaString.length - node.token.length; - const newNode = { ...node }; - - newNode.token = formulaString; - - newNode.endIndex += difference; - - lastSequenceNodes[nodeIndex] = newNode; - - const isDefinedName = this._descriptionService.hasDefinedNameDescription(formulaString); - - const isFormulaDefinedName = this._descriptionService.isFormulaDefinedName(formulaString); - - const formulaStringCount = formulaString.length + 1; - - const mustAddBracket = !isDefinedName || isFormulaDefinedName; - - if (mustAddBracket) { - lastSequenceNodes.splice(nodeIndex + 1, 0, matchToken.OPEN_BRACKET); - } - - for (let i = nodeIndex + 2, len = lastSequenceNodes.length; i < len; i++) { - const node = lastSequenceNodes[i]; - if (typeof node === 'string') { - continue; - } - - const newNode = { ...node }; - - newNode.startIndex += formulaStringCount; - newNode.endIndex += formulaStringCount; - - lastSequenceNodes[i] = newNode; - } - - let selectionIndex = newNode.endIndex + 1; - if (mustAddBracket) { - selectionIndex += 1; - } - - this._syncToEditor(lastSequenceNodes, selectionIndex, undefined, true, false); - }) - ); - } - - private _changeFunctionPanelState() { - const activeRange = this._docSelectionManagerService.getActiveTextRange(); - - if (activeRange == null) { - this._hideFunctionPanel(); - return; - } - - const { startOffset } = activeRange; - - const currentSequenceNode = this._formulaPromptService.getCurrentSequenceNode(startOffset - 2); - - if (currentSequenceNode == null) { - this._hideFunctionPanel(); - return; - } - - if (typeof currentSequenceNode !== 'string' && currentSequenceNode.nodeType === sequenceNodeType.FUNCTION && !this._descriptionService.hasDefinedNameDescription(currentSequenceNode.token.trim())) { - const token = currentSequenceNode.token.toUpperCase(); - - if (this._inputPanelState === InputPanelState.keyNormal) { - // show search function panel - const searchList = this._descriptionService.getSearchListByNameFirstLetter(token); - this._hideFunctionPanel(); - if (searchList == null || searchList.length === 0) { - return; - } - this._commandService.executeCommand(SearchFunctionOperation.id, { - visible: true, - searchText: token, - searchList, - }); - } else { - // show help function panel - this._changeHelpFunctionPanelState(token, -1); - } - - return; - } - - const config = this._getCurrentBodyDataStreamAndOffset(); - - const functionAndParameter = this._lexerTreeBuilder.getFunctionAndParameter(config?.dataStream || '', startOffset - 1 + (config?.offset || 0)); - - if (!functionAndParameter) { - this._hideFunctionPanel(); - return; - } - - const { functionName, paramIndex } = functionAndParameter; - - this._changeHelpFunctionPanelState(functionName.toUpperCase(), paramIndex); - } - - private _changeHelpFunctionPanelState(token: string, paramIndex: number) { - const functionInfo = this._descriptionService.getFunctionInfo(token); - this._hideFunctionPanel(); - if (functionInfo == null) { - return; - } - - // show help function panel - this._commandService.executeCommand(HelpFunctionOperation.id, { - visible: true, - paramIndex, - functionInfo, - }); - } - - private _hideFunctionPanel() { - this._commandService.executeCommand(SearchFunctionOperation.id, { - visible: false, - searchText: '', - }); - this._commandService.executeCommand(HelpFunctionOperation.id, { - visible: false, - paramIndex: -1, - }); - } - - private _checkShouldEnterSelectingMode(isOnlyInputRangeEditor = false): void { - if (isOnlyInputRangeEditor) { - this._enterSelectingMode(); - return; - } - - const char = this._getCurrentChar(); - const dataStream = this._getCurrentDataStream(); - if (dataStream?.substring(0, 1) === '=' && char && matchRefDrawToken(char)) { - this._enterSelectingMode(); - } else { - this._quitSelectingMode(); - } - } - - /** - * - * @returns Return the character under the current cursor in the editor. - */ - private _getCurrentChar() { - const activeRange = this._docSelectionManagerService.getActiveTextRange(); - - if (activeRange == null) { - return; - } - - const { startOffset } = activeRange; - - const config = this._getCurrentBodyDataStreamAndOffset(); - - if (config == null || startOffset == null) { - return; - } - - const dataStream = config.dataStream; - - return dataStream[startOffset - 1 + config.offset]; - } - - private _getCurrentDataStream() { - const config = this._getCurrentBodyDataStreamAndOffset(); - return config?.dataStream; - } - - private _isSelectingMode = false; - private _enterSelectingMode() { - if (this._isSelectingMode) { - return; - } - - this._editorBridgeService.enableForceKeepVisible(); - this._contextMenuService.disable(); - this._formulaPromptService.enableLockedSelectionInsert(); - this._selectionRenderService.setRemainLastEnabled(true); - - // Maybe `enterSelectingMode` should be merged with `_enableRefSelectionsRenderService`. - this._enableRefSelectionsRenderService(); - this._currentlyWorkingRefRenderer = null; - - // TODO: remain last - if (this._arrowMoveActionState !== ArrowMoveAction.moveCursor) { - this._arrowMoveActionState = ArrowMoveAction.moveRefReady; - } - - this._isSelectingMode = true; - } - - /** - * Disable the ref string generation mode. In the ref string generation mode, - * users can select a certain area using the mouse and arrow keys, and convert the area into a ref string. - */ - private _quitSelectingMode() { - if (!this._isSelectingMode) { - return; - } - - this._editorBridgeService.disableForceKeepVisible(); - this._contextMenuService.enable(); - this._formulaPromptService.disableLockedSelectionInsert(); - this._currentInsertRefStringIndex = -1; - - this._disposeSelectionsChangeListeners(); - - if (this._arrowMoveActionState === ArrowMoveAction.moveRefReady) { - this._arrowMoveActionState = ArrowMoveAction.exitInput; - } - - this._isSelectingMode = false; - } - - private _getCurrentBodyDataStreamAndOffset() { - const documentModel = this._univerInstanceService.getCurrentUniverDocInstance(); - - if (!documentModel?.getBody()) { - return; - } - - const unitId = documentModel.getUnitId(); - - const editor = this._editorService.getEditor(unitId); - - const dataStream = documentModel.getBody()?.dataStream ?? ''; - - if (!editor || !editor.onlyInputRange()) { - return { dataStream, offset: 0 }; - } - - return { dataStream: compareToken.EQUALS + dataStream, offset: 1 }; - } - - private _getFormulaAndCellEditorBody(unitIds: string[]) { - return unitIds.map((unitId) => { - const dataModel = this._univerInstanceService.getUniverDocInstance(unitId); - - return dataModel?.getBody(); - }); - } - - private _editorModelUnitIds() { - const currentDocumentDataModel = this._univerInstanceService.getCurrentUniverDocInstance()!; - const unitId = currentDocumentDataModel.getUnitId(); - - if (this._editorService.isEditor(unitId) && !this._editorService.isSheetEditor(unitId)) { - return [unitId]; - } - - return sheetEditorUnitIds; - } - - /** - * Detect whether the user's input content is a formula. If it is a formula, - * serialize the current input content into a sequenceNode; - * otherwise, close the formula panel. - * @param currentInputValue The text content entered by the user in the editor. - */ - private _contextSwitch() { - const config = this._getCurrentBodyDataStreamAndOffset(); - - if (config && isFormulaString(config.dataStream)) { - this._contextService.setContextValue(FOCUSING_EDITOR_INPUT_FORMULA, true); - this._contextService.setContextValue(DISABLE_NORMAL_SELECTIONS, true); - this._contextService.setContextValue(UNI_DISABLE_CHANGING_FOCUS_KEY, true); - - const lastSequenceNodes = - this._lexerTreeBuilder.sequenceNodesBuilder(config.dataStream) || - []; - - this._formulaPromptService.setSequenceNodes(lastSequenceNodes); - - const activeRange = this._docSelectionManagerService.getActiveTextRange(); - - if (activeRange == null) { - return; - } - - const { startOffset } = activeRange; - - this._currentInsertRefStringIndex = startOffset - 1 + config.offset; - - return; - } - - this._contextService.setContextValue(FOCUSING_EDITOR_INPUT_FORMULA, false); - this._contextService.setContextValue(DISABLE_NORMAL_SELECTIONS, false); - this._contextService.setContextValue(UNI_DISABLE_CHANGING_FOCUS_KEY, false); - - this._formulaPromptService.disableLockedSelectionChange(); - - this._formulaPromptService.disableLockedSelectionInsert(); - - // this._lastSequenceNodes = []; - - this._formulaPromptService.clearSequenceNodes(); - - this._hideFunctionPanel(); - } - - private _getContextState() { - return this._contextService.getContextValue(FOCUSING_EDITOR_INPUT_FORMULA); - } - - /** - * Highlight cell editor and formula bar editor. - */ - private _highlightFormula() { - if (this._getContextState() === false) { - return; - } - - const sequenceNodes = this._formulaPromptService.getSequenceNodes(); - - const unitIds = this._editorModelUnitIds(); - - const bodyList = this._getFormulaAndCellEditorBody(unitIds).filter((b) => !!b); - - // this._refSelectionsService.clear(); - - if (sequenceNodes == null || sequenceNodes.length === 0) { - this._existsSequenceNode = false; - bodyList.forEach((body) => (body!.textRuns = [])); - } else { - // this._lastSequenceNodes = sequenceNodes; - this._existsSequenceNode = true; - const { textRuns, refSelections } = this._buildTextRuns(sequenceNodes); - bodyList.forEach((body) => (body!.textRuns = textRuns)); - this._allSelectionRenderServices.forEach((r) => this._refreshSelectionForReference(r, refSelections)); - - // No need set refSelection styles here. this._syncToEditor has same effect. - // this._allSelectionRenderServices.forEach((r) => this._updateRefSelectionStyle(r, this._isSelectionMovingRefSelections)); - } - - this._refreshFormulaAndCellEditor(unitIds); - } - - /** - * : - * # - * Generate styles for formula text, highlighting references, text, numbers, and arrays. - */ - private _buildTextRuns(sequenceNodes: Array) { - const textRuns: ITextRun[] = []; - const refSelections: IRefSelection[] = []; - const themeColorMap = new Map(); - let refColorIndex = 0; - const offset = this._getCurrentBodyDataStreamAndOffset()?.offset || 0; - for (let i = 0, len = sequenceNodes.length; i < len; i++) { - const node = sequenceNodes[i]; - if (typeof node === 'string' || this._descriptionService.hasDefinedNameDescription(node.token.trim())) { - continue; - } - - const { startIndex, endIndex, nodeType, token } = node; - let themeColor = ''; - if (nodeType === sequenceNodeType.REFERENCE) { - if (themeColorMap.has(token)) { - themeColor = themeColorMap.get(token)!; - } else { - const colorIndex = refColorIndex % this._formulaRefColors.length; - themeColor = this._formulaRefColors[colorIndex]; - themeColorMap.set(token, themeColor); - refColorIndex++; - } - - refSelections.push({ - refIndex: i, - themeColor, - token, - }); - } else if (nodeType === sequenceNodeType.NUMBER) { - themeColor = this._numberColor; - } else if (nodeType === sequenceNodeType.STRING) { - themeColor = this._stringColor; - } else if (nodeType === sequenceNodeType.ARRAY) { - themeColor = this._stringColor; - } - - if (themeColor && themeColor.length > 0) { - textRuns.push({ - st: startIndex + 1 - offset, - ed: endIndex + 2 - offset, - ts: { - cl: { - rgb: themeColor, - }, - }, - }); - } - } - - return { textRuns, refSelections }; - } - - private _exceedCurrentRange(range: IRange, rowCount: number, columnCount: number) { - const { startRow, startColumn } = range; - if (startRow > rowCount - 1) { - return true; - } - - if (startColumn > columnCount - 1) { - return true; - } - - return false; - } - - /** - * Draw the referenced selection text based on the style and token. - * @param refSelections - */ - - private _refreshSelectionForReference(refSelectionRenderService: RefSelectionsRenderService, refSelections: IRefSelection[]) { - // const [unitId, sheetId] = refSelectionRenderService.getLocation(); - const { unitId, sheetId } = this._editorBridgeService.getEditCellState()!; - const { unitId: selfUnitId, sheetId: currSheetId } = this._getCurrentUnitIdAndSheetId(); - - const isSelfSheet = sheetId === currSheetId; - - const workbook = this._univerInstanceService.getUniverSheetInstance(unitId)!; - const worksheet = workbook.getSheetBySheetId(sheetId)!; - - let lastRange: Nullable = null; - - const selectionWithStyle: ISelectionWithStyle[] = []; - for (let i = 0, len = refSelections.length; i < len; i++) { - const refSelection = refSelections[i]; - const { themeColor, token, refIndex } = refSelection; - - const gridRange = deserializeRangeWithSheet(token); - const { unitId: refUnitId, sheetName, range: rawRange } = gridRange; - - /** - * pro/issues/436 - * When the range is an entire row or column, NaN values need to be corrected. - */ - const range = setEndForRange(rawRange, worksheet.getRowCount(), worksheet.getColumnCount()); - - if (refUnitId != null && refUnitId.length > 0 && unitId !== refUnitId) continue; - - // sheet name is designed to be unique. - const refSheetId = this._getSheetIdByName(unitId, sheetName.trim()); - - // Cross sheet operation - if (!isSelfSheet && refSheetId !== currSheetId) continue; - - // Current sheet operation - if (isSelfSheet && sheetName.length !== 0 && refSheetId !== sheetId) continue; - - if (this._exceedCurrentRange(range, worksheet.getRowCount(), worksheet.getColumnCount())) continue; - - const lastRangeCopy = this._getPrimary(range, themeColor, refIndex); - if (lastRangeCopy) { - lastRange = lastRangeCopy; - selectionWithStyle.push(lastRange); - continue; - } - - const primary = getPrimaryForRange(range, worksheet); - - if ( - !Rectangle.equals(primary, range) && - range.startRow === range.endRow && - range.startColumn === range.endColumn - ) { - range.startRow = primary.startRow; - range.endRow = primary.endRow; - range.startColumn = primary.startColumn; - range.endColumn = primary.endColumn; - } - - selectionWithStyle.push({ - range, - primary, - style: genFormulaRefSelectionStyle(this._themeService, themeColor, refIndex.toString()), - }); - } - - // why add lastRange after all? that would changes selection sequence !!! why ??? - // if (lastRange) { - // selectionWithStyle.push(lastRange); - // } - - // why use sheetId not currSheetId ??? - if (selectionWithStyle.length) { - this._refSelectionsService.setSelections(unitId, sheetId, selectionWithStyle, SelectionMoveType.ONLY_SET); - } - } - - private _getPrimary(range: IRange, themeColor: string, refIndex: number) { - const matchedInsertSelection = this._insertSelections.find((selection) => { - const { startRow, startColumn, endRow, endColumn } = selection.range; - if ( - startRow === range.startRow && - startColumn === range.startColumn && - endRow === range.endRow && - endColumn === range.endColumn - ) { - return true; - } - if ( - startRow === range.startRow && - startColumn === range.startColumn && - range.startRow === range.endRow && - range.startColumn === range.endColumn - ) { - return true; - } - - return false; - }); - - if (matchedInsertSelection?.primary == null) { - return; - } - - const { - isMerged, - isMergedMainCell, - startRow: mergeStartRow, - endRow: mergeEndRow, - startColumn: mergeStartColumn, - endColumn: mergeEndColumn, - } = matchedInsertSelection.primary; - - if ( - (isMerged || isMergedMainCell) && - mergeStartRow === range.startRow && - mergeStartColumn === range.startColumn && - range.startRow === range.endRow && - range.startColumn === range.endColumn - ) { - range.endRow = mergeEndRow; - range.endColumn = mergeEndColumn; - } - - return { - range, - primary: matchedInsertSelection.primary, - style: genFormulaRefSelectionStyle(this._themeService, themeColor, refIndex.toString()), - }; - } - - private _getSheetIdByName(unitId: string, sheetName: string) { - const workbook = this._univerInstanceService.getUniverSheetInstance(unitId); - return workbook?.getSheetBySheetName(normalizeSheetName(sheetName))?.getSheetId(); - } - - private _getSheetNameById(unitId: string, sheetId: string) { - const workbook = this._univerInstanceService.getUniverSheetInstance(unitId); - const sheetName = workbook?.getSheetBySheetId(sheetId)?.getName() || ''; - return sheetName; - } - - private _getCurrentUnitIdAndSheetId() { - const workbook = this._univerInstanceService.getCurrentUnitForType(UniverInstanceType.UNIVER_SHEET)!; - const worksheet = workbook.getActiveSheet(); - const skeleton = this._renderManagerService.getRenderById(workbook.getUnitId())?.with(SheetSkeletonManagerService)?.getCurrentSkeleton(); - - return { - unitId: workbook.getUnitId(), - sheetId: worksheet?.getSheetId() || '', - skeleton, - }; - } - - // FIXME@Jocs: this internal implementations should be exposed to callers. - // This method should be moved to EditorService. - private _getEditorOpenedForSheet() { - const documentDataModel = this._univerInstanceService.getCurrentUniverDocInstance()!; - const editorUnitId = documentDataModel.getUnitId(); - const editor = this._editorService.getEditor(editorUnitId); - if (!editor) { - return { - openUnitId: null, - openSheetId: null, - }; - } - - return { - openUnitId: editor.getOpenForSheetUnitId(), - openSheetId: editor.getOpenForSheetSubUnitId(), - }; - } - - /** - * Convert the selection range to a ref string for the formula engine, such as A1:B1 - * @param currentSelection - */ - private _generateRefString(currentSelection: ISelectionWithStyle) { - let refUnitId = ''; - let refSheetName = ''; - - const { unitId, sheetId } = currentSelection.range; - const { openUnitId, openSheetId } = this._getEditorOpenedForSheet(); - - if (unitId !== openUnitId && unitId) { - refUnitId = unitId; - } - - if (sheetId !== openSheetId && unitId && sheetId) { - refSheetName = this._getSheetNameById(unitId, sheetId); - } - - const { range, primary } = currentSelection; - let { startRow, endRow, startColumn, endColumn } = range; - const { startAbsoluteRefType, endAbsoluteRefType, rangeType } = range; - - if (primary) { - const { - isMerged, - isMergedMainCell, - startRow: mergeStartRow, - endRow: mergeEndRow, - startColumn: mergeStartColumn, - endColumn: mergeEndColumn, - } = primary; - - if ( - (isMerged || isMergedMainCell) && - mergeStartRow === startRow && - mergeStartColumn === startColumn && - mergeEndRow === endRow && - mergeEndColumn === endColumn - ) { - startRow = mergeStartRow; - startColumn = mergeStartColumn; - endRow = mergeStartRow; - endColumn = mergeStartColumn; - } - } - - return serializeRangeToRefString({ - sheetName: refSheetName, - unitId: refUnitId, - range: { - startRow, - endRow, - startColumn, - endColumn, - rangeType, - startAbsoluteRefType, - endAbsoluteRefType, - }, - }); - } - - /** - * Update Editor formula text in prompt editor by current selection in spreadsheet. - * Restore the sequenceNode generated by the lexer to the text in the editor, and set the cursor position. - * - * @param sequenceNodes - * @param textSelectionOffset - */ - - private _syncToEditor( - sequenceNodes: Array, - textSelectionOffset: number, - editorUnitId?: string, - canUndo: boolean = true, - fromSelection = true - ) { - let dataStream = generateStringWithSequence(sequenceNodes); - const { textRuns, refSelections } = this._buildTextRuns(sequenceNodes); - this._isSelectionMovingRefSelections = refSelections; - - // Get theme color from prompt formula editor when creating a new selection. - this._allSelectionRenderServices.forEach((r) => this._updateRefSelectionStyle(r, this._isSelectionMovingRefSelections)); - - const activeRange = this._docSelectionManagerService.getActiveTextRange(); - if (activeRange == null) { - return; - } - - this._currentInsertRefStringIndex = textSelectionOffset; - - if (editorUnitId == null) { - editorUnitId = this._univerInstanceService.getCurrentUniverDocInstance()!.getUnitId(); - } - - this._fitEditorSize(); - - const editor = this._editorService.getEditor(editorUnitId); - - // You need to set a mode for single selection area or multiple selection areas, adapting to a rangeSelector that only has a single selection area. - if (editor?.isSingleChoice()) { - dataStream = dataStream.split(',')[0]; - this._selectionRenderService.setSingleSelectionEnabled(true); - } else { - this._selectionRenderService.setSingleSelectionEnabled(false); - } - - let formulaString = dataStream; - let offset = 1; - - if (!editor || !editor.onlyInputRange()) { - formulaString = `${compareToken.EQUALS}${dataStream}`; - offset = 0; - } - - const { collapsed, style } = activeRange; - if (canUndo) { - this._commandService.executeCommand(ReplaceContentCommand.id, { - unitId: editorUnitId, - body: { - dataStream: formulaString, - textRuns, - }, - textRanges: [ - { - startOffset: textSelectionOffset + 1 - offset, - endOffset: textSelectionOffset + 1 - offset, - collapsed, - style, - }, - ], - segmentId: null, - options: { fromSelection }, - }); - // The ReplaceContentCommand has canceled the selection operation, so it needs to be triggered externally once. - this._docSelectionManagerService.replaceTextRanges([ - { - startOffset: textSelectionOffset + 1 - offset, - endOffset: textSelectionOffset + 1 - offset, - style, - }, - ], true, { fromSelection }); - } else { - this._updateEditorModel(`${formulaString}\r\n`, textRuns); - this._docSelectionManagerService.replaceTextRanges([ - { - startOffset: textSelectionOffset + 1 - offset, - endOffset: textSelectionOffset + 1 - offset, - style, - }, - ], true, { fromSelection }); - } - - /** - * After selecting the formula, allow the editor to continue entering content. - */ - this._layoutService.focus(); - } - - private _fitEditorSize() { - const currentDocumentDataModel = this._univerInstanceService.getCurrentUniverDocInstance(); - const editorUnitId = currentDocumentDataModel!.getUnitId(); - - if (this._editorService.isEditor(editorUnitId) && !this._editorService.isSheetEditor(editorUnitId)) { - return; - } - this._editorBridgeService.changeEditorDirty(true); - if (!this._editorBridgeService.isVisible().visible) { - return; - } - - if (editorUnitId === DOCS_NORMAL_EDITOR_UNIT_ID_KEY) { - const workbook = this._univerInstanceService.getCurrentUnitForType(UniverInstanceType.UNIVER_SHEET); - const workbookUnitId = workbook?.getUnitId() ?? ''; - const render = this._renderManagerService.getRenderById(workbookUnitId); - if (!render) { - return; - } - const sheetCellEditorResizeService = render.with(SheetCellEditorResizeService); - sheetCellEditorResizeService.fitTextSize(); - } - } - - /** - * Update the editor's model value to facilitate formula updates. - * @param dataStream - * @param textRuns - */ - private _updateEditorModel(dataStream: string, textRuns: ITextRun[]) { - const documentDataModel = this._univerInstanceService.getCurrentUniverDocInstance(); - - const editorUnitId = documentDataModel!.getUnitId(); - if (!this._editorService.isEditor(editorUnitId)) { - return; - } - - const docViewModel = this._renderManagerService.getRenderById(editorUnitId)?.with(DocSkeletonManagerService).getViewModel(); - if (docViewModel == null || documentDataModel == null) { - return; - } - - const snapshot = documentDataModel?.getSnapshot(); - - if (snapshot == null) { - return; - } - - const newBody = { - dataStream, - textRuns, - }; - - snapshot.body = newBody; - - docViewModel.reset(documentDataModel); - } - - private _insertControlSelectionReplace(currentSelection: ISelectionWithStyle) { - if (this._previousSequenceNodes == null) { - this._previousSequenceNodes = this._formulaPromptService.getSequenceNodes(); - } - - if (this._previousInsertRefStringIndex == null) { - this._previousInsertRefStringIndex = this._currentInsertRefStringIndex; - } - - // No new control is added, the current ref string is still modified. - const insertNodes = Tools.deepClone(this._previousSequenceNodes); - if (insertNodes == null) { - return; - } - - const { skeleton } = this._getCurrentUnitIdAndSheetId(); - const unitId = skeleton?.worksheet.getUnitId(); - const sheetId = skeleton?.worksheet.getSheetId(); - currentSelection.range.sheetId = sheetId; - currentSelection.range.unitId = unitId; - - const refString = this._generateRefString(currentSelection); - this._formulaPromptService.setSequenceNodes(insertNodes); - this._formulaPromptService.insertSequenceRef(this._previousInsertRefStringIndex, refString); - this._syncToEditor(insertNodes, this._previousInsertRefStringIndex + refString.length); - const selectionsWithStyle = this._selectionRenderService.getSelectionDataWithStyle() || []; - this._insertSelections = []; - - // selectionsWithStyle.forEach((currentSelection) => { - // const range = convertSelectionDataToRange(currentSelection); - // this._insertSelections.push(range); - // }); - const lastSelectionWithStyle = selectionsWithStyle[selectionsWithStyle.length - 1]; - if (lastSelectionWithStyle) { - const range = convertSelectionDataToRange(lastSelectionWithStyle); - this._insertSelections.push(range); - } - } - - /** - * pro/issues/450 - * In range selection mode, certain measures are implemented to ensure that the selection behavior is processed correctly. - */ - private _focusIsOnlyRange(selectionCount: number) { - const currentEditor = this._editorService.getFocusEditor(); - if (!currentEditor) { - return true; - } - - if (!currentEditor.onlyInputRange()) { - return true; - } - - if (this._existsSequenceNode) { - return true; - } - - if (selectionCount > 1 || (this._previousSequenceNodes != null && this._previousSequenceNodes.length > 0)) { - return true; - } - - if (this._previousInsertRefStringIndex != null) { - this._previousInsertRefStringIndex += 1; - } - - return false; - } - - /** - * pro/issues/450 - * In range selection mode, certain measures are implemented to ensure that the selection behavior is processed correctly. - */ - private _resetSequenceNodes(selectionCount: number) { - const currentEditor = this._editorService.getFocusEditor(); - if (!currentEditor) { - return; - } - - if (!currentEditor.onlyInputRange()) { - return; - } - - if (selectionCount > 1) { - return; - } - - if (this._existsSequenceNode) { - this._formulaPromptService.clearSequenceNodes(); - this._previousRangesCount = 0; - this._existsSequenceNode = false; - } - } - - // FIXME: @wzhudev: this method could be merged with `_refreshSelectionForReference`. - - private _updateRefSelectionStyle(refSelectionRenderService: RefSelectionsRenderService, refSelections: IRefSelection[]) { - const controls = refSelectionRenderService.getSelectionControls(); - const [unitId, sheetId] = refSelectionRenderService.getLocation(); - - const matchedControls = new Set(); - for (let i = 0, len = refSelections.length; i < len; i++) { - const refSelection = refSelections[i]; - const { refIndex, themeColor, token } = refSelection; - const rangeWithSheet = deserializeRangeWithSheet(token); - const { unitId: refUnitId, sheetName, range } = rangeWithSheet; - - if (!refUnitId && refUnitId.length > 0 && unitId !== refUnitId) { - continue; - } - - const refSheetId = this._getSheetIdByName(unitId, sheetName.trim()); - if (refSheetId && refSheetId !== sheetId) { - continue; - } - - const control = controls.find((c) => { - // 范围相等方法又写一遍! - const { startRow, startColumn, endRow, endColumn, rangeType } = c.getRange(); - if ( - rangeType === RANGE_TYPE.COLUMN && - startColumn === range.startColumn && - endColumn === range.endColumn - ) { - return true; - } - - if (rangeType === RANGE_TYPE.ROW && startRow === range.startRow && endRow === range.endRow) { - return true; - } - - if ( - startRow === range.startRow && - startColumn === range.startColumn && - endRow === range.endRow && - endColumn === range.endColumn - ) { - return true; - } - if ( - startRow === range.startRow && - startColumn === range.startColumn && - range.startRow === range.endRow && - range.startColumn === range.endColumn - ) { - return true; - } - - return false; - }); - - if (control) { - const style = genFormulaRefSelectionStyle(this._themeService, themeColor, refIndex.toString()); - control.updateStyle(style); - matchedControls.add(control); - } - } - } - - private _onSelectionControlChange(toRange: IRangeWithCoord, selectionControl: SelectionControl) { - // FIXME: change here - const { skeleton } = this._getCurrentUnitIdAndSheetId(); - if (!skeleton) return; - // const { unitId, sheetId } = toRange; - this._formulaPromptService.enableLockedSelectionChange(); - - const id = selectionControl.currentStyle?.id; - if (!id || !Tools.isStringNumber(id)) { - return; - } - - let { startRow, endRow, startColumn, endColumn } = toRange; - const primary = skeleton - ? skeleton.worksheet.getCellInfoInMergeData(startRow, startColumn) - : { - actualRow: startRow, - actualColumn: startColumn, - isMergedMainCell: false, - isMerged: false, - endRow: startRow, - endColumn: startColumn, - startRow, - startColumn, - }; - - if (primary) { - const { - isMerged, - isMergedMainCell, - startRow: mergeStartRow, - endRow: mergeEndRow, - startColumn: mergeStartColumn, - endColumn: mergeEndColumn, - } = primary; - - if ( - (isMerged || isMergedMainCell) && mergeStartRow === startRow && mergeStartColumn === startColumn && - mergeEndRow === endRow && mergeEndColumn === endColumn - ) { - startRow = mergeStartRow; - startColumn = mergeStartColumn; - endRow = mergeStartRow; - endColumn = mergeStartColumn; - } - } - - const nodeIndex = Number(id); - - const currentNode = this._formulaPromptService.getCurrentSequenceNodeByIndex(nodeIndex); - if (!currentNode) { - return; - } - let refType: IAbsoluteRefTypeForRange = { startAbsoluteRefType: AbsoluteRefType.NONE }; - if (typeof currentNode !== 'string') { - const token = (currentNode as ISequenceNode).token; - - refType = getAbsoluteRefTypeWitString(token) as IAbsoluteRefTypeForRange; - - if (refType.endAbsoluteRefType == null) { - refType.endAbsoluteRefType = refType.startAbsoluteRefType; - } - } - - const unitId = skeleton?.worksheet.getUnitId(); - const sheetId = skeleton?.worksheet.getSheetId(); - const refString = this._generateRefString({ - range: { - startRow: Math.min(startRow, endRow), - endRow: Math.max(startRow, endRow), - startColumn: Math.min(startColumn, endColumn), - endColumn: Math.max(startColumn, endColumn), - ...refType, - sheetId, - unitId, - }, - primary, - style: null, - }); - - this._formulaPromptService.updateSequenceRef(nodeIndex, refString); - const sequenceNodes = this._formulaPromptService.getSequenceNodes(); - const node = sequenceNodes[nodeIndex]; - - if (typeof node === 'string') { - return; - } - - this._syncToEditor(sequenceNodes, node.endIndex + 1); - selectionControl.updateRange(toRange, this._selectionRenderService.attachPrimaryWithCoord(primary)); - } - - private _refreshFormulaAndCellEditor(unitIds: string[]) { - for (const unitId of unitIds) { - const editorObject = getEditorObject(unitId, this._renderManagerService); - - const documentComponent = editorObject?.document; - - if (documentComponent == null) { - continue; - } - - documentComponent.getSkeleton()?.calculate(); - documentComponent.makeDirty(); - } - } - - private _cursorStateListener() { - /** - * The user's operations follow the sequence of opening the editor and then moving the cursor. - * The logic here predicts the user's first cursor movement behavior based on this rule - */ - - const editorObject = this._getEditorObject(); - - if (editorObject == null) { - return; - } - - const { mainComponent: documentComponent } = editorObject; - if (documentComponent) { - this.disposeWithMe(documentComponent.onPointerDown$.subscribeEvent(() => { - this._arrowMoveActionState = ArrowMoveAction.moveCursor; - this._inputPanelState = InputPanelState.mouse; - })); - } - } - - private _pressEnter(params: ISelectEditorFormulaOperationParam) { - const { keycode, isSingleEditor = false } = params; - - if (this._formulaPromptService.isSearching()) { - this._formulaPromptService.accept(true); - return; - } - - if (isSingleEditor === true) { - return; - } - - // FIXME: @Jocs: lots of code duplications here - - this._editorBridgeService.changeVisible({ - visible: false, - eventType: DeviceInputEventType.Keyboard, - keycode, - unitId: '', - }); - } - - private _pressTab(params: ISelectEditorFormulaOperationParam) { - const { keycode, isSingleEditor = false } = params; - if (this._formulaPromptService.isSearching()) { - this._formulaPromptService.accept(true); - return; - } - - if (isSingleEditor === true) { - return; - } - - this._editorBridgeService.changeVisible({ - visible: false, - eventType: DeviceInputEventType.Keyboard, - keycode, - unitId: '', - }); - } - - private _pressEsc(params: ISelectEditorFormulaOperationParam) { - const { keycode } = params; - const focusEditor = this._editorService.getFocusEditor(); - if (!focusEditor || focusEditor?.isSheetEditor() === true) { - this._editorBridgeService.changeVisible({ - visible: false, - eventType: DeviceInputEventType.Keyboard, - keycode, - unitId: '', - }); - } - } - - private _pressArrowKey(params: ISelectEditorFormulaOperationParam) { - const { keycode, metaKey } = params; - let direction = Direction.DOWN; - if (keycode === KeyCode.ARROW_DOWN) { - direction = Direction.DOWN; - } else if (keycode === KeyCode.ARROW_UP) { - direction = Direction.UP; - } else if (keycode === KeyCode.ARROW_LEFT) { - direction = Direction.LEFT; - } else if (keycode === KeyCode.ARROW_RIGHT) { - direction = Direction.RIGHT; - } - - if (metaKey === MetaKeys.CTRL_COMMAND) { - this._commandService.executeCommand(MoveSelectionCommand.id, { - direction, - jumpOver: JumpOver.moveGap, - }); - } else if (metaKey === MetaKeys.SHIFT) { - this._commandService.executeCommand(ExpandSelectionCommand.id, { - direction, - }); - } else if (metaKey === META_KEY_CTRL_AND_SHIFT) { - this._commandService.executeCommand(ExpandSelectionCommand.id, { - direction, - jumpOver: JumpOver.moveGap, - }); - } else { - this._commandService.executeCommand(MoveSelectionCommand.id, { - direction, - }); - } - } - - private _commandExecutedListener() { - // Listen to document edits to refresh the size of the editor. - const updateCommandList = [SelectEditorFormulaOperation.id]; - - this.disposeWithMe( - this._commandService.onCommandExecuted((command: ICommandInfo) => { - const instance = this._univerInstanceService.getCurrentUnitForType(UniverInstanceType.UNIVER_DOC); - const unitId = instance?.getUnitId() || ''; - if (isRangeSelector(unitId) || isEmbeddingFormulaEditor(unitId)) { - return; - } - if (command.id === ReferenceAbsoluteOperation.id) { - this._changeRefString(); - } else if (updateCommandList.includes(command.id)) { - const params = command.params as ISelectEditorFormulaOperationParam; - const { keycode, isSingleEditor = false } = params; - - if (keycode === KeyCode.ENTER) { - this._pressEnter(params); - return; - } - - if (keycode === KeyCode.TAB) { - this._pressTab(params); - return; - } - - if (keycode === KeyCode.ESC) { - this._pressEsc(params); - return; - } - - if (this._formulaPromptService.isSearching()) { - if (keycode === KeyCode.ARROW_DOWN) { - this._formulaPromptService.navigate({ direction: Direction.DOWN }); - return; - } - if (keycode === KeyCode.ARROW_UP) { - this._formulaPromptService.navigate({ direction: Direction.UP }); - return; - } - } - - if (isSingleEditor === true) { - return; - } - - if (this._arrowMoveActionState === ArrowMoveAction.moveCursor) { - this._moveInEditor(keycode); - return; - } - if (this._arrowMoveActionState === ArrowMoveAction.exitInput) { - this._editorBridgeService.changeVisible({ - visible: false, - eventType: DeviceInputEventType.Keyboard, - keycode, - unitId: '', - }); - return; - } - - if (this._arrowMoveActionState === ArrowMoveAction.moveRefReady) { - this._arrowMoveActionState = ArrowMoveAction.movingRef; - } - - // If there's no current selections in the ref selections service, we should copy for - // normal selection. - const previousRanges = this._refSelectionsService.getCurrentSelections(); - if (previousRanges.length === 0) { - const selectionData = this._sheetsSelectionsService.getCurrentLastSelection(); - if (selectionData != null) { - const selectionDataNew = Tools.deepClone(selectionData); - this._refSelectionsService.setSelections([selectionDataNew]); - } - } - - this._pressArrowKey(params); - - const selectionWithStyles = this._refSelectionsService.getCurrentSelections(); - const currentSelection = selectionWithStyles[selectionWithStyles.length - 1]; - - this._insertControlSelectionReplace(currentSelection); - this._highlightFormula(); - } - }) - ); - } - - private _moveInEditor(keycode: Nullable) { - if (keycode == null) { - return; - } - let direction = Direction.LEFT; - if (keycode === KeyCode.ARROW_DOWN) { - direction = Direction.DOWN; - } else if (keycode === KeyCode.ARROW_UP) { - direction = Direction.UP; - } else if (keycode === KeyCode.ARROW_RIGHT) { - direction = Direction.RIGHT; - } - - this._commandService.executeCommand(MoveCursorOperation.id, { - direction, - }); - } - - private _userMouseListener() { - const editorObject = this._getEditorObject(); - - if (editorObject == null) { - return; - } - - const { mainComponent: documentComponent } = editorObject; - if (documentComponent) { - this.disposeWithMe(documentComponent?.onPointerDown$.subscribeEvent(() => { - this._userCursorMove = true; - })); - } - } - - private _inputFormulaListener() { - this.disposeWithMe( - this._editorService.inputFormula$.subscribe((param) => { - const { formulaString, editorUnitId } = param; - - if (formulaString.substring(0, 1) !== compareToken.EQUALS) { - return; - } - const { unitId } = this._getCurrentUnitIdAndSheetId(); - const visibleState = this._editorBridgeService.isVisible(); - if (visibleState.visible === false) { - this._editorBridgeService.changeVisible({ - visible: true, - eventType: DeviceInputEventType.Dblclick, - unitId, - }); - } - - const lastSequenceNodes = this._lexerTreeBuilder.sequenceNodesBuilder(formulaString) || []; - - this._formulaPromptService.setSequenceNodes(lastSequenceNodes); - - this._syncToEditor(lastSequenceNodes, formulaString.length - 1, editorUnitId, true, false); - }) - ); - } - - /** - * Absolute range, triggered by F4 - */ - private _changeRefString() { - const activeRange = this._docSelectionManagerService.getActiveTextRange(); - - if (activeRange == null) { - return; - } - - const { startOffset } = activeRange; - - const strIndex = startOffset - 2; - - const nodeIndex = this._formulaPromptService.getCurrentSequenceNodeIndex(strIndex); - - const node = this._formulaPromptService.getCurrentSequenceNodeByIndex(nodeIndex); - - if (node == null || typeof node === 'string' || node.nodeType !== sequenceNodeType.REFERENCE) { - return; - } - - const tokenArray = node.token.split('!'); - - let token = node.token; - - if (tokenArray.length > 1) { - token = tokenArray[tokenArray.length - 1]; - } - - let unitIDAndSheetName = ''; - - for (let i = 0, len = tokenArray.length; i < len - 1; i++) { - unitIDAndSheetName += tokenArray[i]; - } - - let finalToken = token; - if (token.indexOf(matchToken.COLON) > -1) { - if (!this._userCursorMove) { - finalToken = this._changeRangeRef(token); - } else { - const refStringSplit = token.split(matchToken.COLON); - const prefix = refStringSplit[0]; - const suffix = refStringSplit[1]; - const relativeIndex = strIndex - node.startIndex; - - if (relativeIndex <= prefix.length) { - finalToken = this._changeSingleRef(prefix) + matchToken.COLON + suffix; - } else { - finalToken = prefix + matchToken.COLON + this._changeSingleRef(suffix); - } - } - } else { - finalToken = this._changeSingleRef(token); - } - - finalToken = unitIDAndSheetName + finalToken; - - const difference = finalToken.length - node.token.length; - - this._formulaPromptService.updateSequenceRef(nodeIndex, finalToken); - - this._syncToEditor(this._formulaPromptService.getSequenceNodes(), strIndex + difference + 1); - } - - private _changeRangeRef(token: string) { - const range = deserializeRangeWithSheet(token).range; - let resultToken = ''; - if (range.startAbsoluteRefType === AbsoluteRefType.NONE || range.startAbsoluteRefType == null) { - range.startAbsoluteRefType = AbsoluteRefType.ALL; - range.endAbsoluteRefType = AbsoluteRefType.ALL; - } else { - range.startAbsoluteRefType = AbsoluteRefType.NONE; - range.endAbsoluteRefType = AbsoluteRefType.NONE; - } - resultToken = serializeRange(range); - return resultToken; - } - - private _changeSingleRef(token: string) { - const range = deserializeRangeWithSheet(token).range; - const type = range.startAbsoluteRefType; - let resultToken = ''; - if (type === AbsoluteRefType.NONE || type == null) { - range.startAbsoluteRefType = AbsoluteRefType.ALL; - range.endAbsoluteRefType = AbsoluteRefType.ALL; - } else if (type === AbsoluteRefType.ALL) { - range.startAbsoluteRefType = AbsoluteRefType.ROW; - range.endAbsoluteRefType = AbsoluteRefType.ROW; - } else if (type === AbsoluteRefType.ROW) { - range.startAbsoluteRefType = AbsoluteRefType.COLUMN; - range.endAbsoluteRefType = AbsoluteRefType.COLUMN; - } else { - range.startAbsoluteRefType = AbsoluteRefType.NONE; - range.endAbsoluteRefType = AbsoluteRefType.NONE; - } - - resultToken = serializeRange(range); - return resultToken; - } - - private _getEditorObject() { - const docInstance = this._univerInstanceService.getCurrentUniverDocInstance(); - if (!docInstance) return; - const editorUnitId = docInstance.getUnitId(); - const editor = this._editorService.getEditor(editorUnitId); - return editor?.render; - } - - private _isFormulaEditorActivated(): boolean { - // TODO: Finally we will remove 'this._editorBridgeService.isVisible().visible === true' to - // just the the context value. - return this._editorBridgeService.isVisible().visible === true || this._contextService.getContextValue(FORMULA_EDITOR_ACTIVATED); - } - - private _isSheetOrFormulaEditor(editor: Editor): boolean { - return editor.isSheetEditor() || editor.isFormulaEditor(); - } -} diff --git a/packages/sheets-formula-ui/src/sheets-formula-ui.plugin.ts b/packages/sheets-formula-ui/src/sheets-formula-ui.plugin.ts index 9e097a76622..02a390f7a6a 100644 --- a/packages/sheets-formula-ui/src/sheets-formula-ui.plugin.ts +++ b/packages/sheets-formula-ui/src/sheets-formula-ui.plugin.ts @@ -33,7 +33,6 @@ import { FormulaClipboardController } from './controllers/formula-clipboard.cont import { FormulaEditorShowController } from './controllers/formula-editor-show.controller'; import { FormulaRenderManagerController } from './controllers/formula-render.controller'; import { FormulaUIController } from './controllers/formula-ui.controller'; -import { PromptController } from './controllers/prompt.controller'; import { FormulaPromptService, IFormulaPromptService } from './services/prompt.service'; import { RefSelectionsRenderService } from './services/render-services/ref-selections.render-service'; import { FormulaEditor } from './views/formula-editor/index'; @@ -72,7 +71,6 @@ export class UniverSheetsFormulaUIPlugin extends Plugin { [FormulaClipboardController], [FormulaEditorShowController], [FormulaRenderManagerController], - [PromptController], ]; dependencies.forEach((dependency) => j.add(dependency)); @@ -100,6 +98,5 @@ export class UniverSheetsFormulaUIPlugin extends Plugin { override onSteady(): void { this._injector.get(FormulaAutoFillController); - this._injector.get(PromptController); } } From daca1a0a16a9673dfacb7e6bd468df410e2cd3bd Mon Sep 17 00:00:00 2001 From: zhangw Date: Thu, 12 Dec 2024 20:54:30 +0800 Subject: [PATCH 051/134] feat: update --- .../selection/doc-selection-render.service.ts | 5 +++-- .../editor/editing.render-controller.ts | 14 +++++++++----- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/packages/docs-ui/src/services/selection/doc-selection-render.service.ts b/packages/docs-ui/src/services/selection/doc-selection-render.service.ts index 4372db3da58..504373ca134 100644 --- a/packages/docs-ui/src/services/selection/doc-selection-render.service.ts +++ b/packages/docs-ui/src/services/selection/doc-selection-render.service.ts @@ -103,7 +103,7 @@ export class DocSelectionRenderService extends RxDisposable implements IRenderMo private _reserveRanges = false; get isFocusing() { - return this._input === document.activeElement || document.activeElement === document.body || document.activeElement === null; + return this._input === document.activeElement || document.activeElement === document.body; } constructor( @@ -312,7 +312,7 @@ export class DocSelectionRenderService extends RxDisposable implements IRenderMo this._container.style.top = `${y}px`; this._container.style.zIndex = '1000'; - if (this.isFocusing || force) { + if (this.isFocusing || document.activeElement === null || force) { this.focus(); } } @@ -682,6 +682,7 @@ export class DocSelectionRenderService extends RxDisposable implements IRenderMo this._input.contentEditable = 'true'; this._input.classList.add('univer-editor'); + this._input.id = `__editor_${this._context.unitId}`; this._input.style.cssText = ` position: absolute; overflow: hidden; 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 fd89c13bab0..ed5c57ce5c5 100644 --- a/packages/sheets-ui/src/controllers/editor/editing.render-controller.ts +++ b/packages/sheets-ui/src/controllers/editor/editing.render-controller.ts @@ -288,11 +288,15 @@ export class EditingRenderController extends Disposable implements IRenderModule } ); - this._univerInstanceService.setCurrentUnitForType(DOCS_NORMAL_EDITOR_UNIT_ID_KEY); - const docSelectionRenderManager = this._renderManagerService.getCurrentTypeOfRenderer(UniverInstanceType.UNIVER_DOC)?.with(DocSelectionRenderService); - - if (docSelectionRenderManager) { - docSelectionRenderManager.activate(HIDDEN_EDITOR_POSITION, HIDDEN_EDITOR_POSITION, !document.activeElement || document.activeElement.classList.contains('univer-editor')); + const docSelectionRenderManager = this._renderManagerService.getRenderById(DOCS_NORMAL_EDITOR_UNIT_ID_KEY)?.with(DocSelectionRenderService); + + if (docSelectionRenderManager?.isFocusing) { + this._univerInstanceService.setCurrentUnitForType(DOCS_NORMAL_EDITOR_UNIT_ID_KEY); + docSelectionRenderManager.activate( + HIDDEN_EDITOR_POSITION, + HIDDEN_EDITOR_POSITION, + true + ); } })); } From b492bda5a4aca2c50dd3c1ed971ef69bb4323495 Mon Sep 17 00:00:00 2001 From: zhangw Date: Thu, 12 Dec 2024 20:57:42 +0800 Subject: [PATCH 052/134] feat: update --- .../src/controllers/editor/editing.render-controller.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) 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 ed5c57ce5c5..2a457ffa5d2 100644 --- a/packages/sheets-ui/src/controllers/editor/editing.render-controller.ts +++ b/packages/sheets-ui/src/controllers/editor/editing.render-controller.ts @@ -288,11 +288,11 @@ export class EditingRenderController extends Disposable implements IRenderModule } ); - const docSelectionRenderManager = this._renderManagerService.getRenderById(DOCS_NORMAL_EDITOR_UNIT_ID_KEY)?.with(DocSelectionRenderService); - - if (docSelectionRenderManager?.isFocusing) { + const cellSelectionRenderManager = this._renderManagerService.getRenderById(DOCS_NORMAL_EDITOR_UNIT_ID_KEY)?.with(DocSelectionRenderService); + const formulaSelectionRenderManager = this._renderManagerService.getRenderById(DOCS_FORMULA_BAR_EDITOR_UNIT_ID_KEY)?.with(DocSelectionRenderService); + if (cellSelectionRenderManager?.isFocusing || formulaSelectionRenderManager?.isFocusing) { this._univerInstanceService.setCurrentUnitForType(DOCS_NORMAL_EDITOR_UNIT_ID_KEY); - docSelectionRenderManager.activate( + cellSelectionRenderManager?.activate( HIDDEN_EDITOR_POSITION, HIDDEN_EDITOR_POSITION, true From 7de90317305f1646285b35b8939912a96f9db5f2 Mon Sep 17 00:00:00 2001 From: zhangw Date: Thu, 12 Dec 2024 21:42:56 +0800 Subject: [PATCH 053/134] feat: update --- .../src/components/panel/rule-edit/colorScale.tsx | 3 --- .../src/components/panel/rule-edit/dataBar.tsx | 3 --- .../src/components/panel/rule-edit/formula.tsx | 3 --- .../src/components/panel/rule-edit/iconSet.tsx | 3 --- .../src/components/panel/rule-edit/index.tsx | 3 --- .../src/views/components/detail/index.tsx | 3 --- .../views/components/formula-input/custom-formula-input.tsx | 3 --- .../views/components/formula-input/list-formula-input.tsx | 3 --- .../sheets-ui/src/views/defined-name/DefinedNameInput.tsx | 6 ------ 9 files changed, 30 deletions(-) diff --git a/packages/sheets-conditional-formatting-ui/src/components/panel/rule-edit/colorScale.tsx b/packages/sheets-conditional-formatting-ui/src/components/panel/rule-edit/colorScale.tsx index c6a6b48d844..a7c0d0bf843 100644 --- a/packages/sheets-conditional-formatting-ui/src/components/panel/rule-edit/colorScale.tsx +++ b/packages/sheets-conditional-formatting-ui/src/components/panel/rule-edit/colorScale.tsx @@ -57,9 +57,6 @@ const TextInput = (props: { id: string; type: CFValueType | 'none'; value: numbe const [isFocusFormulaEditor, isFocusFormulaEditorSet] = useState(false); useSidebarClick((e: MouseEvent) => { - if ((e.target as HTMLElement).tagName.toLocaleLowerCase() === 'canvas') { - return; - } const handleOutClick = formulaEditorActionsRef.current?.handleOutClick; handleOutClick && handleOutClick(e, () => isFocusFormulaEditorSet(false)); }); diff --git a/packages/sheets-conditional-formatting-ui/src/components/panel/rule-edit/dataBar.tsx b/packages/sheets-conditional-formatting-ui/src/components/panel/rule-edit/dataBar.tsx index 57b017edfe8..45af0a30b95 100644 --- a/packages/sheets-conditional-formatting-ui/src/components/panel/rule-edit/dataBar.tsx +++ b/packages/sheets-conditional-formatting-ui/src/components/panel/rule-edit/dataBar.tsx @@ -41,9 +41,6 @@ const InputText = (props: { disabled?: boolean; id: string; className: string; t const [isFocusFormulaEditor, isFocusFormulaEditorSet] = useState(false); useSidebarClick((e: MouseEvent) => { - if ((e.target as HTMLElement).tagName.toLocaleLowerCase() === 'canvas') { - return; - } const handleOutClick = formulaEditorActionsRef.current?.handleOutClick; handleOutClick && handleOutClick(e, () => isFocusFormulaEditorSet(false)); }); diff --git a/packages/sheets-conditional-formatting-ui/src/components/panel/rule-edit/formula.tsx b/packages/sheets-conditional-formatting-ui/src/components/panel/rule-edit/formula.tsx index cdc1787cc6c..b617f38cb24 100644 --- a/packages/sheets-conditional-formatting-ui/src/components/panel/rule-edit/formula.tsx +++ b/packages/sheets-conditional-formatting-ui/src/components/panel/rule-edit/formula.tsx @@ -89,9 +89,6 @@ export const FormulaStyleEditor = (props: IStyleEditorProps) => { }; useSidebarClick((e: MouseEvent) => { - if ((e.target as HTMLElement).tagName.toLocaleLowerCase() === 'canvas') { - return; - } const handleOutClick = formulaEditorActionsRef.current?.handleOutClick; handleOutClick && handleOutClick(e, () => isFocusFormulaEditorSet(false)); }); diff --git a/packages/sheets-conditional-formatting-ui/src/components/panel/rule-edit/iconSet.tsx b/packages/sheets-conditional-formatting-ui/src/components/panel/rule-edit/iconSet.tsx index 75186d127b7..08f7e8d1dc0 100644 --- a/packages/sheets-conditional-formatting-ui/src/components/panel/rule-edit/iconSet.tsx +++ b/packages/sheets-conditional-formatting-ui/src/components/panel/rule-edit/iconSet.tsx @@ -51,9 +51,6 @@ const TextInput = (props: { id: number; type: CFValueType; value: number | strin const [isFocusFormulaEditor, isFocusFormulaEditorSet] = useState(false); useSidebarClick((e: MouseEvent) => { - if ((e.target as HTMLElement).tagName.toLocaleLowerCase() === 'canvas') { - return; - } const handleOutClick = formulaEditorActionsRef.current?.handleOutClick; handleOutClick && handleOutClick(e, () => isFocusFormulaEditorSet(false)); }); diff --git a/packages/sheets-conditional-formatting-ui/src/components/panel/rule-edit/index.tsx b/packages/sheets-conditional-formatting-ui/src/components/panel/rule-edit/index.tsx index 6f7db4be211..70b4980ad2c 100644 --- a/packages/sheets-conditional-formatting-ui/src/components/panel/rule-edit/index.tsx +++ b/packages/sheets-conditional-formatting-ui/src/components/panel/rule-edit/index.tsx @@ -240,9 +240,6 @@ export const RuleEdit = (props: IRuleEditProps) => { }; useSidebarClick((e: MouseEvent) => { - if ((e.target as HTMLElement).tagName.toLocaleLowerCase() === 'canvas') { - return; - } const handleOutClick = rangeSelectorActionsRef.current?.handleOutClick; handleOutClick && handleOutClick(e, () => isFocusRangeSelectorSet(false)); }); diff --git a/packages/sheets-data-validation-ui/src/views/components/detail/index.tsx b/packages/sheets-data-validation-ui/src/views/components/detail/index.tsx index ee9c2f96355..62bca6e8fa6 100644 --- a/packages/sheets-data-validation-ui/src/views/components/detail/index.tsx +++ b/packages/sheets-data-validation-ui/src/views/components/detail/index.tsx @@ -248,9 +248,6 @@ export function DataValidationDetail() { }; useSidebarClick((e: MouseEvent) => { - if ((e.target as HTMLElement).tagName.toLocaleLowerCase() === 'canvas') { - return; - } const handleOutClick = rangeSelectorActionsRef.current?.handleOutClick; handleOutClick && handleOutClick(e, () => isFocusRangeSelectorSet(false)); }); diff --git a/packages/sheets-data-validation-ui/src/views/components/formula-input/custom-formula-input.tsx b/packages/sheets-data-validation-ui/src/views/components/formula-input/custom-formula-input.tsx index 63555c4d878..3f50e0e7279 100644 --- a/packages/sheets-data-validation-ui/src/views/components/formula-input/custom-formula-input.tsx +++ b/packages/sheets-data-validation-ui/src/views/components/formula-input/custom-formula-input.tsx @@ -26,9 +26,6 @@ export function CustomFormulaInput(props: IFormulaInputProps) { const [isFocusFormulaEditor, isFocusFormulaEditorSet] = useState(false); useSidebarClick((e: MouseEvent) => { - if ((e.target as HTMLElement).tagName.toLocaleLowerCase() === 'canvas') { - return; - } const handleOutClick = formulaEditorActionsRef.current?.handleOutClick; handleOutClick && handleOutClick(e, () => isFocusFormulaEditorSet(false)); }); diff --git a/packages/sheets-data-validation-ui/src/views/components/formula-input/list-formula-input.tsx b/packages/sheets-data-validation-ui/src/views/components/formula-input/list-formula-input.tsx index 6af753ccb8e..9447de61217 100644 --- a/packages/sheets-data-validation-ui/src/views/components/formula-input/list-formula-input.tsx +++ b/packages/sheets-data-validation-ui/src/views/components/formula-input/list-formula-input.tsx @@ -316,9 +316,6 @@ export function ListFormulaInput(props: IFormulaInputProps) { const [isFocusFormulaEditor, isFocusFormulaEditorSet] = useState(false); useSidebarClick((e: MouseEvent) => { - if ((e.target as HTMLElement).tagName.toLocaleLowerCase() === 'canvas') { - return; - } const handleOutClick = formulaEditorActionsRef.current?.handleOutClick; handleOutClick && handleOutClick(e, () => isFocusFormulaEditorSet(false)); }); diff --git a/packages/sheets-ui/src/views/defined-name/DefinedNameInput.tsx b/packages/sheets-ui/src/views/defined-name/DefinedNameInput.tsx index c764fb699aa..7d920212dfe 100644 --- a/packages/sheets-ui/src/views/defined-name/DefinedNameInput.tsx +++ b/packages/sheets-ui/src/views/defined-name/DefinedNameInput.tsx @@ -211,17 +211,11 @@ export const DefinedNameInput = (props: IDefinedNameInputProps) => { const [isFocusFormulaEditor, isFocusFormulaEditorSet] = useState(false); useSidebarClick((e: MouseEvent) => { - if ((e.target as HTMLElement).tagName.toLocaleLowerCase() === 'canvas') { - return; - } const handleOutClick = rangeSelectorActionsRef.current?.handleOutClick; handleOutClick && handleOutClick(e, () => isFocusRangeSelectorSet(false)); }); useSidebarClick((e: MouseEvent) => { - if ((e.target as HTMLElement).tagName.toLocaleLowerCase() === 'canvas') { - return; - } const handleOutClick = formulaEditorActionsRef.current?.handleOutClick; handleOutClick && handleOutClick(e, () => isFocusFormulaEditorSet(false)); }); From 116793e57d69b9f5564786d25ae75ec5a4f5ff17 Mon Sep 17 00:00:00 2001 From: zhangw Date: Thu, 12 Dec 2024 21:54:25 +0800 Subject: [PATCH 054/134] feat: update --- .../src/views/formula-editor/index.tsx | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/sheets-formula-ui/src/views/formula-editor/index.tsx b/packages/sheets-formula-ui/src/views/formula-editor/index.tsx index c5c29372c10..a061ac178e0 100644 --- a/packages/sheets-formula-ui/src/views/formula-editor/index.tsx +++ b/packages/sheets-formula-ui/src/views/formula-editor/index.tsx @@ -20,7 +20,8 @@ import type { KeyCode, MetaKeys } from '@univerjs/ui'; import type { ReactNode } from 'react'; import type { IKeyboardEventConfig } from '../range-selector/hooks/useKeyboardEvent'; import { BuildTextUtils, createInternalEditorID, generateRandomId, IUniverInstanceService, useDependency, useObservable } from '@univerjs/core'; -import { DocBackScrollRenderController, IEditorService } from '@univerjs/docs-ui'; +import { DocBackScrollRenderController, DocSelectionRenderService, IEditorService } from '@univerjs/docs-ui'; +import { IRenderManagerService } from '@univerjs/engine-render'; import { EMBEDDING_FORMULA_EDITOR } from '@univerjs/sheets-ui'; import { useEvent } from '@univerjs/ui'; import clsx from 'clsx'; @@ -122,6 +123,10 @@ export function FormulaEditor(props: IFormulaEditorProps) { const isSelecting = useFormulaSelecting(editorId, sequenceNodes); const [shouldMoveRefSelection, setShouldMoveRefSelection] = useState(false); const highTextRef = useRef(''); + const renderManagerService = useDependency(IRenderManagerService); + const renderer = renderManagerService.getRenderById(editorId); + const docSelectionRenderService = renderer?.with(DocSelectionRenderService); + const isFocusing = docSelectionRenderService?.isFocusing; useEffect(() => { if (isSelecting === FormulaSelectingType.NEED_ADD) { @@ -194,6 +199,9 @@ export function FormulaEditor(props: IFormulaEditorProps) { useLeftAndRightArrow(isFocus && moveCursor, shouldMoveRefSelection, editor, onMoveInEditor); const handleSelectionChange = useEvent((refString: string, offset: number, isEnd: boolean) => { + if (!isFocusing) { + return; + } needEmit(); highligh(`=${refString}`); if (isEnd) { From 5256dab8427fd659425f19c78a9dd61e1beefeb9 Mon Sep 17 00:00:00 2001 From: zhangw Date: Thu, 12 Dec 2024 22:41:51 +0800 Subject: [PATCH 055/134] feat: update --- .../doc-drawing-transformer-update.controller.ts | 1 - .../src/views/formula-editor/index.tsx | 8 ++++++-- .../src/views/range-selector/index.tsx | 10 +++++++--- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/packages/docs-drawing-ui/src/controllers/doc-drawing-transformer-update.controller.ts b/packages/docs-drawing-ui/src/controllers/doc-drawing-transformer-update.controller.ts index 9d1809fd426..9605cf72ef4 100644 --- a/packages/docs-drawing-ui/src/controllers/doc-drawing-transformer-update.controller.ts +++ b/packages/docs-drawing-ui/src/controllers/doc-drawing-transformer-update.controller.ts @@ -80,7 +80,6 @@ export class DocDrawingTransformerController extends Disposable { private _listenDrawingFocus(): void { this.disposeWithMe( this._drawingManagerService.add$.subscribe((drawingParams) => { - // console.log('===add$', drawingParams); if (drawingParams.length === 0) { return; } diff --git a/packages/sheets-formula-ui/src/views/formula-editor/index.tsx b/packages/sheets-formula-ui/src/views/formula-editor/index.tsx index a061ac178e0..7faee7eea87 100644 --- a/packages/sheets-formula-ui/src/views/formula-editor/index.tsx +++ b/packages/sheets-formula-ui/src/views/formula-editor/index.tsx @@ -19,7 +19,7 @@ import type { Editor } from '@univerjs/docs-ui'; import type { KeyCode, MetaKeys } from '@univerjs/ui'; import type { ReactNode } from 'react'; import type { IKeyboardEventConfig } from '../range-selector/hooks/useKeyboardEvent'; -import { BuildTextUtils, createInternalEditorID, generateRandomId, IUniverInstanceService, useDependency, useObservable } from '@univerjs/core'; +import { BuildTextUtils, createInternalEditorID, generateRandomId, IUniverInstanceService, UniverInstanceType, useDependency, useObservable } from '@univerjs/core'; import { DocBackScrollRenderController, DocSelectionRenderService, IEditorService } from '@univerjs/docs-ui'; import { IRenderManagerService } from '@univerjs/engine-render'; import { EMBEDDING_FORMULA_EDITOR } from '@univerjs/sheets-ui'; @@ -127,6 +127,9 @@ export function FormulaEditor(props: IFormulaEditorProps) { const renderer = renderManagerService.getRenderById(editorId); const docSelectionRenderService = renderer?.with(DocSelectionRenderService); const isFocusing = docSelectionRenderService?.isFocusing; + const currentDoc$ = useMemo(() => univerInstanceService.getCurrentTypeOfUnit$(UniverInstanceType.UNIVER_DOC), [univerInstanceService]); + const currentDoc = useObservable(currentDoc$); + const docFocusing = currentDoc?.getUnitId() === editorId; useEffect(() => { if (isSelecting === FormulaSelectingType.NEED_ADD) { @@ -195,7 +198,7 @@ export function FormulaEditor(props: IFormulaEditorProps) { }, [_isFocus, focus, resetSelectionOnBlur]); const { checkScrollBar } = useResize(editor); - useRefactorEffect(isFocus, Boolean(isSelecting), unitId); + useRefactorEffect(isFocus, Boolean(isSelecting && docFocusing), unitId); useLeftAndRightArrow(isFocus && moveCursor, shouldMoveRefSelection, editor, onMoveInEditor); const handleSelectionChange = useEvent((refString: string, offset: number, isEnd: boolean) => { @@ -218,6 +221,7 @@ export function FormulaEditor(props: IFormulaEditorProps) { checkScrollBar(); } }); + useSheetSelectionChange(isFocus, unitId, subUnitId, sequenceNodes, isSupportAcrossSheet, shouldMoveRefSelection, editor, handleSelectionChange); useRefocus(); diff --git a/packages/sheets-formula-ui/src/views/range-selector/index.tsx b/packages/sheets-formula-ui/src/views/range-selector/index.tsx index b3db2116d8d..8ba64685017 100644 --- a/packages/sheets-formula-ui/src/views/range-selector/index.tsx +++ b/packages/sheets-formula-ui/src/views/range-selector/index.tsx @@ -17,7 +17,7 @@ import type { IDisposable, IUnitRangeName } from '@univerjs/core'; import type { Editor } from '@univerjs/docs-ui'; import type { ReactNode } from 'react'; -import { createInternalEditorID, DOCS_NORMAL_EDITOR_UNIT_ID_KEY, generateRandomId, ICommandService, LocaleService, useDependency } from '@univerjs/core'; +import { createInternalEditorID, DOCS_NORMAL_EDITOR_UNIT_ID_KEY, generateRandomId, ICommandService, IUniverInstanceService, LocaleService, UniverInstanceType, useDependency, useObservable } from '@univerjs/core'; import { Button, Dialog, Input, Tooltip } from '@univerjs/design'; import { DocBackScrollRenderController, IEditorService } from '@univerjs/docs-ui'; import { deserializeRangeWithSheet, LexerTreeBuilder, matchToken, sequenceNodeType } from '@univerjs/engine-formula'; @@ -109,6 +109,7 @@ export function RangeSelector(props: IRangeSelectorProps) { const editorId = useMemo(() => createInternalEditorID(`${RANGE_SELECTOR_SYMBOLS}-${generateRandomId(4)}`), []); const [editor, editorSet] = useState(); const containerRef = useRef(null); + const univerInstanceService = useDependency(IUniverInstanceService); const isNeed = useMemo(() => !rangeDialogVisible && isFocus, [rangeDialogVisible, isFocus]); const [rangeString, rangeStringSet] = useState(() => { if (typeof initValue === 'string') { @@ -117,6 +118,9 @@ export function RangeSelector(props: IRangeSelectorProps) { return unitRangesToText(initValue, isSupportAcrossSheet).join(matchToken.COMMA); } }); + const currentDoc$ = useMemo(() => univerInstanceService.getCurrentTypeOfUnit$(UniverInstanceType.UNIVER_DOC), [univerInstanceService]); + const currentDoc = useObservable(currentDoc$); + const docFocusing = currentDoc?.getUnitId() === editorId; // init actions if (actions) { @@ -229,7 +233,7 @@ export function RangeSelector(props: IRangeSelectorProps) { useSheetSelectionChange(isNeed, unitId, subUnitId, sequenceNodes, isSupportAcrossSheet, isOnlyOneRange, handleSheetSelectionChange); - useRefactorEffect(isNeed, isNeed, unitId); + useRefactorEffect(isNeed, isNeed && docFocusing, unitId); useOnlyOneRange(unitId, isOnlyOneRange); @@ -479,7 +483,7 @@ function RangeSelectorDialog(props: { const highlightSheet = useSheetHighlight(unitId); useSheetSelectionChange(focusIndex >= 0, unitId, subUnitId, sequenceNodes, isSupportAcrossSheet, isOnlyOneRange, handleSheetSelectionChange); - useRefactorEffect(focusIndex >= 0, focusIndex >= 0, unitId); + useRefactorEffect(focusIndex >= 0, focusIndex >= 0, unitId, editorId); useOnlyOneRange(unitId, isOnlyOneRange); useSwitchSheet(focusIndex >= 0, unitId, isSupportAcrossSheet, noop, noop, () => highlightSheet(refSelections)); From 0919cc8a8cfd2d5ed5b89c717fce3a9fa8e1c631 Mon Sep 17 00:00:00 2001 From: zhangw Date: Fri, 13 Dec 2024 21:11:42 +0800 Subject: [PATCH 056/134] feat: update --- .../src/views/formula-editor/index.tsx | 2 +- .../range-selector/hooks/useResetSelection.ts | 25 ++++++++----------- .../src/views/range-selector/index.tsx | 4 +-- 3 files changed, 13 insertions(+), 18 deletions(-) diff --git a/packages/sheets-formula-ui/src/views/formula-editor/index.tsx b/packages/sheets-formula-ui/src/views/formula-editor/index.tsx index 7faee7eea87..16780c53643 100644 --- a/packages/sheets-formula-ui/src/views/formula-editor/index.tsx +++ b/packages/sheets-formula-ui/src/views/formula-editor/index.tsx @@ -170,7 +170,7 @@ export function FormulaEditor(props: IFormulaEditorProps) { useVerify(isFocus, onVerify, formulaText); const focus = useFocus(editor); - const resetSelection = useResetSelection(isFocus); + const resetSelection = useResetSelection(isFocus, unitId, subUnitId); useEffect(() => { onFormulaSelectingChange(isSelecting); diff --git a/packages/sheets-formula-ui/src/views/range-selector/hooks/useResetSelection.ts b/packages/sheets-formula-ui/src/views/range-selector/hooks/useResetSelection.ts index fb06435fa6f..6450936db85 100644 --- a/packages/sheets-formula-ui/src/views/range-selector/hooks/useResetSelection.ts +++ b/packages/sheets-formula-ui/src/views/range-selector/hooks/useResetSelection.ts @@ -17,27 +17,22 @@ import type { Workbook } from '@univerjs/core'; import { IUniverInstanceService, UniverInstanceType, useDependency } from '@univerjs/core'; import { SheetsSelectionsService } from '@univerjs/sheets'; -import { useMemo } from 'react'; +import { useCallback } from 'react'; -export const useResetSelection = (isNeed: boolean) => { +export const useResetSelection = (isNeed: boolean, unitId: string, subUnitId: string) => { const univerInstanceService = useDependency(IUniverInstanceService); const sheetsSelectionsService = useDependency(SheetsSelectionsService); - const resetSelection = useMemo(() => { + const resetSelection = useCallback(() => { if (isNeed) { + const selections = [...sheetsSelectionsService.getWorkbookSelections(unitId).getSelectionsOfWorksheet(subUnitId)]; const workbook = univerInstanceService.getCurrentUnitForType(UniverInstanceType.UNIVER_SHEET); - const sheet = workbook?.getActiveSheet(); - const selections = [...sheetsSelectionsService.getCurrentSelections()]; - return () => { - const workbook = univerInstanceService.getCurrentUnitForType(UniverInstanceType.UNIVER_SHEET); - const currentSheet = workbook?.getActiveSheet(); - if (currentSheet && currentSheet === sheet) { - sheetsSelectionsService.setSelections(selections); - } - }; - } - return () => { }; - }, [isNeed]); + const currentSheet = workbook?.getActiveSheet(); + if (currentSheet && currentSheet.getSheetId() === subUnitId) { + sheetsSelectionsService.setSelections(selections); + } + }; + }, [isNeed, sheetsSelectionsService, subUnitId, unitId, univerInstanceService]); return resetSelection; }; diff --git a/packages/sheets-formula-ui/src/views/range-selector/index.tsx b/packages/sheets-formula-ui/src/views/range-selector/index.tsx index 8ba64685017..cf3b4f56486 100644 --- a/packages/sheets-formula-ui/src/views/range-selector/index.tsx +++ b/packages/sheets-formula-ui/src/views/range-selector/index.tsx @@ -138,7 +138,7 @@ export function RangeSelector(props: IRangeSelectorProps) { const isError = useMemo(() => errorText !== undefined, [errorText]); - const resetSelection = useResetSelection(!rangeDialogVisible && isFocus); + const resetSelection = useResetSelection(!rangeDialogVisible && isFocus, unitId, subUnitId); const handleInput = useMemo(() => (text: string) => { const nodes = lexerTreeBuilder.sequenceNodesBuilder(text); @@ -483,7 +483,7 @@ function RangeSelectorDialog(props: { const highlightSheet = useSheetHighlight(unitId); useSheetSelectionChange(focusIndex >= 0, unitId, subUnitId, sequenceNodes, isSupportAcrossSheet, isOnlyOneRange, handleSheetSelectionChange); - useRefactorEffect(focusIndex >= 0, focusIndex >= 0, unitId, editorId); + useRefactorEffect(focusIndex >= 0, focusIndex >= 0, unitId); useOnlyOneRange(unitId, isOnlyOneRange); useSwitchSheet(focusIndex >= 0, unitId, isSupportAcrossSheet, noop, noop, () => highlightSheet(refSelections)); From 7fc807385128c581b26c478b51cff2f588130dbb Mon Sep 17 00:00:00 2001 From: zhangw Date: Thu, 19 Dec 2024 11:18:19 -0500 Subject: [PATCH 057/134] refactor: editor-service --- .../commands/operations/sidebar.operation.ts | 10 +- .../src/controllers/debugger.controller.ts | 6 +- .../src/views/test-editor/TestTextEditor.tsx | 117 ----- .../src/views/components/DocFormulaPopup.tsx | 6 +- .../src/views/uni-toolbar/UniFormulaBar.tsx | 6 +- .../src/components/editor/TextEditor.tsx | 331 ------------- .../range-selector/RangeSelector.tsx | 379 --------------- .../range-selector/index.module.less | 158 ------- .../doc-editor-bridge.controller.ts | 74 +-- .../doc-selection-render.controller.ts | 13 +- packages/docs-ui/src/index.ts | 4 +- .../services/editor/editor-manager.service.ts | 434 +----------------- .../docs-ui/src/services/editor/editor.ts | 48 -- .../formula-input/custom-formula-input.tsx | 1 + .../src/views/formula-editor/index.tsx | 15 +- .../src/views/range-selector/index.tsx | 59 +-- .../sidebar-defined-name.operation.ts | 2 - .../editor/editing.render-controller.ts | 15 - .../src/services/editor-bridge.service.ts | 8 - .../slide-editing.render-controller.ts | 52 +-- .../editor-container/EditorContainer.tsx | 6 +- 21 files changed, 96 insertions(+), 1648 deletions(-) delete mode 100644 packages-experimental/debugger/src/views/test-editor/TestTextEditor.tsx delete mode 100644 packages/docs-ui/src/components/editor/TextEditor.tsx delete mode 100644 packages/docs-ui/src/components/range-selector/RangeSelector.tsx delete mode 100644 packages/docs-ui/src/components/range-selector/index.module.less diff --git a/packages-experimental/debugger/src/commands/operations/sidebar.operation.ts b/packages-experimental/debugger/src/commands/operations/sidebar.operation.ts index 57ce3d6e612..a64529ca454 100644 --- a/packages-experimental/debugger/src/commands/operations/sidebar.operation.ts +++ b/packages-experimental/debugger/src/commands/operations/sidebar.operation.ts @@ -14,10 +14,10 @@ * limitations under the License. */ +import type { IAccessor, ICommand, Workbook } from '@univerjs/core'; import { CommandType, IUniverInstanceService, UniverInstanceType } from '@univerjs/core'; import { IEditorService } from '@univerjs/docs-ui'; import { ISidebarService } from '@univerjs/ui'; -import type { IAccessor, ICommand, Workbook } from '@univerjs/core'; import { TEST_EDITOR_CONTAINER_COMPONENT } from '../../views/test-editor/component-name'; export interface IUIComponentCommandParams { @@ -34,25 +34,17 @@ export const SidebarOperation: ICommand = { const unit = univerInstanceService.getCurrentUnitForType(UniverInstanceType.UNIVER_SHEET)!; switch (params.value) { case 'open': - editorService.setOperationSheetUnitId(unit.getUnitId()); - editorService.setOperationSheetSubUnitId(unit.getActiveSheet()?.getSheetId()); sidebarService.open({ header: { title: 'Sidebar title' }, children: { label: TEST_EDITOR_CONTAINER_COMPONENT }, footer: { title: 'Sidebar Footer' }, onClose: () => { - editorService.setOperationSheetUnitId(null); - editorService.setOperationSheetSubUnitId(null); - editorService.closeRangePrompt(); }, }); break; case 'close': default: - editorService.setOperationSheetUnitId(null); - editorService.setOperationSheetSubUnitId(null); - editorService.closeRangePrompt(); sidebarService.close(); break; } diff --git a/packages-experimental/debugger/src/controllers/debugger.controller.ts b/packages-experimental/debugger/src/controllers/debugger.controller.ts index 646c6de3477..0e3ecd05040 100644 --- a/packages-experimental/debugger/src/controllers/debugger.controller.ts +++ b/packages-experimental/debugger/src/controllers/debugger.controller.ts @@ -35,8 +35,8 @@ import { ImageDemo } from '../components/Image'; // @ts-ignore import VueI18nIcon from '../components/VueI18nIcon.vue'; -import { TEST_EDITOR_CONTAINER_COMPONENT } from '../views/test-editor/component-name'; -import { TestEditorContainer } from '../views/test-editor/TestTextEditor'; +// import { TEST_EDITOR_CONTAINER_COMPONENT } from '../views/test-editor/component-name'; +// import { TestEditorContainer } from '../views/test-editor/TestTextEditor'; import { RecordController } from './local-save/record.controller'; import { menuSchema } from './menu.schema'; @@ -81,7 +81,7 @@ export class DebuggerController extends Disposable { private _initCustomComponents(): void { const componentManager = this._componentManager; - this.disposeWithMe(componentManager.register(TEST_EDITOR_CONTAINER_COMPONENT, TestEditorContainer)); + // this.disposeWithMe(componentManager.register(TEST_EDITOR_CONTAINER_COMPONENT, TestEditorContainer)); this.disposeWithMe(componentManager.register('VueI18nIcon', VueI18nIcon, { framework: 'vue3', })); diff --git a/packages-experimental/debugger/src/views/test-editor/TestTextEditor.tsx b/packages-experimental/debugger/src/views/test-editor/TestTextEditor.tsx deleted file mode 100644 index e37b2fd1cf3..00000000000 --- a/packages-experimental/debugger/src/views/test-editor/TestTextEditor.tsx +++ /dev/null @@ -1,117 +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 type { Workbook } from '@univerjs/core'; - -import { createInternalEditorID, IUniverInstanceService, UniverInstanceType, useDependency } from '@univerjs/core'; -import { Input } from '@univerjs/design'; -import { DocRangeSelector, TextEditor } from '@univerjs/docs-ui'; -import React, { useState } from 'react'; - -const editorStyle: React.CSSProperties = { - width: '100%', -}; - -/** - * Floating editor's container. - */ -export const TestEditorContainer = () => { - const univerInstanceService = useDependency(IUniverInstanceService); - const workbook = univerInstanceService.getCurrentUnitForType(UniverInstanceType.UNIVER_SHEET)!; - if (workbook == null) { - return; - } - - const unitId = workbook.getUnitId(); - - const sheetId = workbook.getActiveSheet()?.getSheetId(); - - const [readonly, setReadonly] = useState(false); - - return ( -
- -
- -
- -
- -
- -
- -
- -
- -
- ); -}; diff --git a/packages-experimental/uni-formula-ui/src/views/components/DocFormulaPopup.tsx b/packages-experimental/uni-formula-ui/src/views/components/DocFormulaPopup.tsx index 1b0b3b7e1b4..67e77dc14ac 100644 --- a/packages-experimental/uni-formula-ui/src/views/components/DocFormulaPopup.tsx +++ b/packages-experimental/uni-formula-ui/src/views/components/DocFormulaPopup.tsx @@ -17,7 +17,7 @@ import type { IDocumentData, Nullable } from '@univerjs/core'; import type { IUniFormulaPopupInfo } from '../../services/formula-popup.service'; import { BooleanNumber, createInternalEditorID, DEFAULT_EMPTY_DOCUMENT_VALUE, DocumentFlavor, HorizontalAlign, ICommandService, LocaleService, useDependency, VerticalAlign, WrapStrategy } from '@univerjs/core'; -import { TextEditor } from '@univerjs/docs-ui'; +// import { TextEditor } from '@univerjs/docs-ui'; import { CheckMarkSingle, CloseSingle } from '@univerjs/icons'; import { useObservable } from '@univerjs/ui'; import clsx from 'clsx'; @@ -123,7 +123,7 @@ function DocFormula(props: { popupInfo: IUniFormulaPopupInfo }) { return (
onHovered(true)} onMouseLeave={() => onHovered(false)}> - setFocused(false)} - /> + /> */}
- + /> */}
, HTMLDivElement>; - -const excludeProps = new Set([ - 'snapshot', - 'resizeCallBack', - 'cancelDefaultResizeListener', - 'isSheetEditor', - 'canvasStyle', - 'isFormulaEditor', - 'isSingle', - 'isReadonly', - 'onlyInputFormula', - 'onlyInputRange', - 'value', - 'onlyInputContent', - 'isSingleChoice', - 'openForSheetUnitId', - 'openForSheetSubUnitId', - 'onChange', - 'onActive', - 'onValid', - 'placeholder', -]); - -export interface ITextEditorProps { - id: string; // unitId - className?: string; // Parent class name. - - snapshot?: IDocumentData; // The default initialization snapshot for the editor can be simply replaced with the value attribute, for cellEditor and formulaBar - - value?: string; // default values. - - // WTF: snapshot and value both exists? And use have to set value and snapshot.textStream separately> - - resizeCallBack?: (editor: Nullable) => void; // Container scale callback. - - cancelDefaultResizeListener?: boolean; // Disable the default container scaling listener, for cellEditor and formulaBar - - canvasStyle?: IEditorCanvasStyle; // Setting the style of the editor is similar to setting the drawing style of a canvas, and therefore, it should be distinguished from the CSS 'style'. At present, it only supports the 'fontsize' attribute. - - isSheetEditor?: boolean; // Specify whether the editor is bound to a sheet. Currently, there are cellEditor and formulaBar. - isFormulaEditor?: boolean; - isSingle?: boolean; // Set whether the editor allows multiline input, default is true, equivalent to input; false is equivalent to textarea. - isReadonly?: boolean; // Set the editor to read-only state. - - onlyInputFormula?: boolean; // Only input formula string - onlyInputRange?: boolean; // Only input ref range - onlyInputContent?: boolean; // Only plain content can be entered, turning off formula and range input highlighting. - - isSingleChoice?: boolean; // Whether to restrict to only selecting a single region/area/district. - - openForSheetUnitId?: Nullable; // Configuring which workbook the selector defaults to opening in determines whether the ref includes a [unitId] prefix. - openForSheetSubUnitId?: Nullable; // Configuring the default worksheet where the selector opens determines whether the ref includes a [unitId]sheet1 prefix. - - onChange?: (value: Nullable) => void; // Callback for changes in the selector value. - onActive?: (state: boolean) => void; // Callback for editor active. - onValid?: (state: boolean) => void; // Editor input value validation, currently effective only under onlyRange and onlyFormula conditions. - - placeholder?: string; // Placeholder text. - isValueValid?: boolean; // Whether the value is valid. - disabled?: boolean; -} - -/** - * The component to render toolbar item label and menu item label. - * @param props - * @deprecated The business side encapsulates its own Editor component. - */ -export function TextEditor(props: ITextEditorProps & Omit): JSX.Element | null { - const { - id, - snapshot, - resizeCallBack, - cancelDefaultResizeListener, - isSheetEditor = false, - canvasStyle = {}, - value, - isSingle = true, - isReadonly = false, - isFormulaEditor = false, - onlyInputFormula = false, - onlyInputRange = false, - onlyInputContent = false, - isSingleChoice = false, - openForSheetUnitId, - openForSheetSubUnitId, - onChange, - onActive, - onValid, - isValueValid = true, - placeholder, - disabled, - } = props; - - const editorService = useDependency(IEditorService); - - const localeService = useDependency(LocaleService); - - const [placeholderValue, placeholderSet] = useState(''); - - const [validationContent, setValidationContent] = useState(''); - - const [validationVisible, setValidationVisible] = useState(isValueValid); - - const [validationOffset, setValidationOffset] = useState<[number, number]>([0, 0]); - - const editorRef = useRef(null); - - const [active, setActive] = useState(false); - - if (!isInternalEditorID(id)) { - throw new Error('Invalid editor ID'); - } - - useEffect(() => { - const editorDom = editorRef.current; - - if (!editorDom) { - return; - } - - const resizeObserver = new ResizeObserver(() => { - if (cancelDefaultResizeListener !== true) { - editorService.resize(id); - } - resizeCallBack && resizeCallBack(editorDom); - }); - - resizeObserver.observe(editorDom); - - const initialSnapshot = snapshot ?? genSnapShotByValue(id, value); - - if (initialSnapshot.id !== id) { - initialSnapshot.id = id; - } - - const registerSubscription = editorService.register({ - editorUnitId: id, - initialSnapshot, - cancelDefaultResizeListener, - isSheetEditor, - canvasStyle, - isSingle, - readonly: isReadonly, - isSingleChoice, - onlyInputFormula, - onlyInputRange, - onlyInputContent, - openForSheetUnitId, - openForSheetSubUnitId, - isFormulaEditor, - }, - editorDom); - - editorService.setValueNoRefresh(value || '', id); - placeholderSet(placeholder || ''); - - const activeChange = debounce((state: boolean) => { - setActive(state); - onActive && onActive(state); - }, 30); - - // !IMPORTANT: Set a delay of 160ms to ensure that the position is corrected after the sidebar animation ends @jikkai - const ANIMATION_DELAY = 160; - const valueChange = debounce((editor: Readonly) => { - const unitId = editor.getEditorId(); - const isLegality = editorService.checkValueLegality(unitId); - - setTimeout(() => { - const rect = editor.getBoundingClientRect(); - setValidationOffset([rect.left, rect.top - 16]); - if (rect.left + rect.top > 0) { - setValidationVisible(isLegality); - } - - if (editor.onlyInputFormula()) { - setValidationContent(localeService.t('textEditor.formulaError')); - } else { - setValidationContent(localeService.t('textEditor.rangeError')); - } - }, ANIMATION_DELAY); - - const currentValue = editorService.getValue(unitId); - - if (currentValue !== value) { - onValid && onValid(isLegality); - // WTF: why emit value on focus? - onChange && onChange(editorService.getValue(id)); - } - }, 30); - - const focusStyleSubscription = editorService.focusStyle$.subscribe((unitId: Nullable) => { - let state = false; - if (unitId === id) { - state = true; - } - activeChange(state); - - setTimeout(() => { - if (!isElementVisible(editorDom)) { - setValidationVisible(true); - } else { - const editor = editorService.getEditor(id); - editor && valueChange(editor); - } - }, ANIMATION_DELAY); - }); - - const valueChangeSubscription = editorService.valueChange$.subscribe((editor) => { - if (editor.isSheetEditor()) { - return; - } - - // WTF: should not use editorService to sync values. All editors instance would be notified! - if (editor.getEditorId() !== id) { - return; - } - - const focusEditor = editorService.getFocusEditor(); - - if (focusEditor && focusEditor.getEditorId() !== id) { - return; - } - - valueChange(editor); - }); - - return () => { - resizeObserver.unobserve(editorDom); - resizeObserver.disconnect(); - registerSubscription.dispose(); - focusStyleSubscription?.unsubscribe(); - valueChangeSubscription?.unsubscribe(); - }; - }, []); - - useEffect(() => { - const editor = editorService.getEditor(id); - if (editor == null) { - return; - } - - editor.update({ - readonly: isReadonly, isSingle, isSingleChoice, onlyInputContent, onlyInputFormula, onlyInputRange, openForSheetSubUnitId, openForSheetUnitId, - }); - }, [isReadonly, isSingle, isSingleChoice, onlyInputContent, onlyInputFormula, onlyInputRange, openForSheetSubUnitId, openForSheetUnitId]); - - useEffect(() => { - if (value == null) { - return; - } - - editorService.setValueNoRefresh(value, id); - }, [value]); - - useEffect(() => { - setValidationVisible(isValueValid); - }, [isValueValid]); - - function hasValue() { - const value = editorService.getValue(id); - if (value == null) { - return false; - } - - if (value === '') { - return false; - } - - return true; - } - - const propsNew = Object.fromEntries( - Object.entries(props).filter(([key]) => !excludeProps.has(key)) - ); - - let className = styles.textEditorContainer; - if (props.className != null) { - className = props.className; - } - - let borderStyle = ''; - - if (props.className == null) { - if (isReadonly) { - borderStyle = ` ${styles.textEditorContainerDisabled}`; - } else if (!validationVisible) { - borderStyle = ` ${styles.textEditorContainerError}`; - } else if (active) { - borderStyle = ` ${styles.textEditorContainerActive}`; - } - } - - return ( - <> -
-
{validationContent}
-
{placeholderValue}
-
- {/* Don't delete it yet, test the stability without popup */} - {/* -
{validationContent}
-
*/} - - ); -} diff --git a/packages/docs-ui/src/components/range-selector/RangeSelector.tsx b/packages/docs-ui/src/components/range-selector/RangeSelector.tsx deleted file mode 100644 index d0c6aac9696..00000000000 --- a/packages/docs-ui/src/components/range-selector/RangeSelector.tsx +++ /dev/null @@ -1,379 +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 type { IUnitRangeWithName, Nullable, Workbook } from '@univerjs/core'; -import { IUniverInstanceService, LocaleService, UniverInstanceType, useDependency } from '@univerjs/core'; -import { Button, Dialog, Input, Tooltip } from '@univerjs/design'; -import { getRangeWithRefsString, isReferenceStringWithEffectiveColumn, serializeRange, serializeRangeWithSheet, serializeRangeWithSpreadsheet } from '@univerjs/engine-formula'; -import { CloseSingle, DeleteSingle, IncreaseSingle, SelectRangeSingle } from '@univerjs/icons'; - -import { useEvent } from '@univerjs/ui'; -import clsx from 'clsx'; -import React, { useEffect, useRef, useState } from 'react'; -import { IEditorService } from '../../services/editor/editor-manager.service'; -import { IRangeSelectorService } from '../../services/range-selector/range-selector.service'; -import { TextEditor } from '../editor/TextEditor'; -import styles from './index.module.less'; - -export interface IRangeSelectorProps { - id: string; - value?: string; // default values. - onChange?: (ranges: IUnitRangeWithName[]) => void; // Callback for changes in the selector value. - onActive?: (state: boolean) => void; // Callback for editor active. - onValid?: (state: boolean) => void; // input value validation - isSingleChoice?: boolean; // Whether to restrict to only selecting a single region/area/district. - isReadonly?: boolean; // Set the selector to read-only state. - openForSheetUnitId?: Nullable; // Configuring which workbook the selector defaults to opening in determines whether the ref includes a [unitId] prefix. - openForSheetSubUnitId?: Nullable; // Configuring the default worksheet where the selector opens determines whether the ref includes a [unitId]sheet1 prefix. - width?: number | string; // The width of the selector. - size?: 'mini' | 'small' | 'middle' | 'large'; // The size of the selector. - placeholder?: string; // Placeholder text. - className?: string; - textEditorClassName?: string; - onSelectorVisibleChange?: (visible: boolean) => void; - dialogOnly?: boolean; -} - -const dialogOnlyInputStyle: React.CSSProperties = { - pointerEvents: 'none', -}; - -/** - * @deprecated - */ -export function RangeSelector(props: IRangeSelectorProps) { - const { dialogOnly, onChange, id, value = '', width = 220, placeholder = '', size = 'middle', onActive, onValid, isSingleChoice = false, openForSheetUnitId, openForSheetSubUnitId, isReadonly = false, className, textEditorClassName, onSelectorVisibleChange: _onSelectorVisibleChange } = props; - const onSelectorVisibleChange = useEvent(_onSelectorVisibleChange); - const [rangeDataList, setRangeDataList] = useState(['']); - - const addNewItem = (newValue: string) => { - setRangeDataList((prevRangeDataList) => [...prevRangeDataList, newValue]); - }; - - const removeItem = (indexToRemove: number) => { - setRangeDataList((prevRangeDataList) => - prevRangeDataList.filter((_, index) => index !== indexToRemove) - ); - }; - - const changeItem = (indexToChange: number, newValue: string) => { - setRangeDataList((prevRangeDataList) => - prevRangeDataList.map((item, index) => - index === indexToChange ? newValue : item - ) - ); - }; - - const changeLastItem = (newValue: string) => { - setRangeDataList((prevRangeDataList) => { - const newList = [...prevRangeDataList]; - if (newList.length > 0) { - newList[newList.length - 1] = newValue; - } - return newList; - }); - }; - - const editorService = useDependency(IEditorService); - - const rangeSelectorService = useDependency(IRangeSelectorService); - - const univerInstanceService = useDependency(IUniverInstanceService); - - const [selectorVisible, setSelectorVisible] = useState(false); - - const localeService = useDependency(LocaleService); - - const [active, setActive] = useState(false); - - const [valid, setValid] = useState(true); - - const [rangeValue, setRangeValue] = useState(value); - - const [currentInputIndex, setCurrentInputIndex] = useState(-1); - - const selectorRef = useRef(null); - - const currentInputIndexRef = useRef(-1); - - const openForSheetUnitIdRef = useRef>(openForSheetUnitId); - - const openForSheetSubUnitIdRef = useRef>(openForSheetSubUnitId); - - const isSingleChoiceRef = useRef>(isSingleChoice); - - const isReadonlyRef = useRef>(isReadonly); - - useEffect(() => { - const selector = selectorRef.current; - - if (!selector) { - return; - } - - const resizeObserver = new ResizeObserver(() => { - editorService.resize(id); - }); - resizeObserver.observe(selector); - - let prevRangesCount = 1; - const valueChangeSubscription = rangeSelectorService.selectionChange$.subscribe((ranges) => { - if (rangeSelectorService.getCurrentSelectorId() !== id) { - return; - } - - if (ranges.length === 0) { - prevRangesCount = 0; - return; - } - - const addItemCount = ranges.length - prevRangesCount; - - prevRangesCount = ranges.length; - - if (addItemCount < 0) { - return; - } - - const lastRange = ranges[ranges.length - 1]; - - let rangeRef: string = ''; - - if (lastRange.unitId === openForSheetUnitIdRef.current && lastRange.sheetId === openForSheetSubUnitIdRef.current) { - rangeRef = serializeRange(lastRange.range); - } else if (lastRange.unitId === openForSheetUnitIdRef.current) { - rangeRef = serializeRangeWithSheet(lastRange.sheetName, lastRange.range); - } else { - rangeRef = serializeRangeWithSpreadsheet(lastRange.unitId, lastRange.sheetName, lastRange.range); - } - - if (addItemCount >= 1 && !isSingleChoiceRef.current) { - addNewItem(rangeRef); - setCurrentInputIndex(-1); - } else { - if (currentInputIndexRef.current === -1) { - changeLastItem(rangeRef); - } else { - changeItem(currentInputIndexRef.current, rangeRef); - } - } - }); - - // Clean up on unmount - return () => { - valueChangeSubscription.unsubscribe(); - resizeObserver.unobserve(selector); - }; - }, []); - - useEffect(() => { - rangeSelectorService.triggerModalVisibleChange(selectorVisible); - }, [onSelectorVisibleChange, rangeSelectorService, selectorVisible]); - - useEffect(() => { - return () => { - rangeSelectorService.triggerModalVisibleChange(false); - }; - }, [rangeSelectorService]); - - useEffect(() => { - openForSheetUnitIdRef.current = openForSheetUnitId; - openForSheetSubUnitIdRef.current = openForSheetSubUnitId; - isSingleChoiceRef.current = isSingleChoice; - isReadonlyRef.current = isReadonly; - }, [openForSheetUnitId, openForSheetSubUnitId, isSingleChoice, isReadonly]); - - useEffect(() => { - currentInputIndexRef.current = currentInputIndex; - }, [currentInputIndex]); - - function handleCloseModal() { - setSelectorVisible(false); - onSelectorVisibleChange(false); - rangeSelectorService.setCurrentSelectorId(null); - } - - function handleOpenModal() { - if (isReadonlyRef.current === true) { - return; - } - - editorService.closeRangePrompt(); - - rangeSelectorService.setCurrentSelectorId(id); - - setSelectorVisible(true); - onSelectorVisibleChange(true); - - if (rangeValue.length > 0) { - if (valid) { - setRangeDataList(rangeValue.split(',')); - } else { - setRangeDataList(['']); - } - } else { - rangeSelectorService.openSelector(); - } - } - - function onEditorActive(state: boolean) { - setActive(state); - onActive && onActive(state); - } - - function onEditorValid(state: boolean) { - setValid(state); - onValid && onValid(state); - } - - function handleConform() { - if (isReadonlyRef.current === true) { - handleCloseModal(); - return; - } - - let result = ''; - const list = rangeDataList.filter((rangeRef) => { - return isReferenceStringWithEffectiveColumn(rangeRef.trim()); - }); - if (list.length === 1) { - const rangeRef = list[0]; - if (isReferenceStringWithEffectiveColumn(rangeRef.trim())) { - result = rangeRef.trim(); - } - } else { - result = list.join(','); - } - - editorService.setValue(result, id); - - handleTextValueChange(result); - - handleCloseModal(); - } - - function handleAddRange() { - addNewItem(''); - setCurrentInputIndex(-1); - } - - function getSheetIdByName(name: string) { - return univerInstanceService.getCurrentUnitForType(UniverInstanceType.UNIVER_SHEET)?.getSheetBySheetName(name)?.getSheetId() || ''; - } - - function handleTextValueChange(value: Nullable) { - setRangeValue(value || ''); - - if (value == null) { - onChange && onChange([]); - return; - } - - const ranges = getRangeWithRefsString(value, getSheetIdByName); - - onChange && onChange(ranges || []); - } - - let sClassName = styles.rangeSelector; - - if (isReadonly) { - sClassName = `${styles.rangeSelector} ${styles.rangeSelectorDisabled}`; - } else if (!valid) { - sClassName = `${styles.rangeSelector} ${styles.rangeSelectorError}`; - } else if (active) { - sClassName = `${styles.rangeSelector} ${styles.rangeSelectorActive}`; - } - - if (textEditorClassName) { - sClassName = `${sClassName} ${textEditorClassName}`; - } - - let height = 28; - if (size === 'mini') { - height = 20; - } else if (size === 'small') { - height = 24; - } else if (size === 'large') { - height = 32; - } - return ( - <> -
{ - if (dialogOnly) { - event.stopPropagation(); - event.preventDefault(); - handleOpenModal(); - } - }} - > - - - - -
- - } - footer={( -
- - -
- )} - onClose={handleCloseModal} - > -
- {rangeDataList.map((item, index) => ( -
-
- setCurrentInputIndex(index)} - value={item} - onChange={(value) => changeItem(index, value)} - /> -
-
- removeItem(index)} /> -
-
- ))} - -
- -
-
- -
- - ); -} diff --git a/packages/docs-ui/src/components/range-selector/index.module.less b/packages/docs-ui/src/components/range-selector/index.module.less deleted file mode 100644 index 0004b6aeec8..00000000000 --- a/packages/docs-ui/src/components/range-selector/index.module.less +++ /dev/null @@ -1,158 +0,0 @@ -@padding-top-bottom: 6px; -@height: 28px; -@width: 220px; -@icon-size: 24px; - -.range-selector { - overflow: hidden; - display: flex; - align-items: center; - justify-content: space-between; - - color: rgb(var(--grey-600)); - - // padding: 0 var(--padding-sm) 0 var(--padding-base); - - border: 1px solid rgb(var(--border-color)); - border-radius: var(--border-radius-base); - - width: @width; - height: @height; - - &-editor { - position: relative; - user-select: none; - width: 100%; - height: 100%; - border: 0; - outline: 0; - } - - &-icon { - cursor: pointer; - - display: flex; - align-items: center; - justify-content: center; - - width: @icon-size; - height: @icon-size; - padding: 0; - - margin-right: 4px; - - font-size: var(--font-size-lg); - color: rgb(var(--text-color)); - - background-color: transparent; - border: none; - border-radius: var(--border-radius-base); - outline: none; - - &:not([disabled]):hover { - background-color: rgb(var(--grey-100)); - } - - &[disabled] { - cursor: not-allowed; - color: rgb(var(--grey-200)); - } - } - - &:hover { - border-color: rgb(var(--hyacinth-500)); - } - - &-active { - border-color: rgb(var(--hyacinth-500)); - - .range-selector-icon { - color: rgb(var(--hyacinth-500)); - } - } - - &-error { - border-color: rgb(var(--red-400)); - - .range-selector-icon { - color: rgb(var(--red-400)); - } - - &:hover { - border-color: rgb(var(--red-400)); - } - } - - &-disabled { - border-color: rgb(var(--grey-100)); - - .range-selector-icon { - color: rgb(var(--grey-100)); - } - - &:hover { - border-color: rgb(var(--grey-100)); - } - } -} - -.range-selector-modal { - position: relative; - - max-height: 500px; - - overflow: hidden; - - overflow-y: auto; - - &-container { - display: flex; - flex-direction: row; - // justify-content: center; - align-items: center; - - margin-bottom: 10px; - - &-input { - display: inline-block; - width: 280px; - - &-active { - border-color: rgb(var(--hyacinth-500)); - } - } - - &-button { - display: inline-block; - text-align: center; - width: 28px; - - &:hover { - cursor: pointer; - color: rgb(var(--hyacinth-500)); - } - } - &-delete-button { - margin: auto; - } - } - - &-add { - position: relative; - width: 300px; - margin-top: 5px; - text-align: left; - color: rgb(var(--hyacinth-500)); - font-size: var(--font-size-xs); - & &-button { - display: flex; - align-items: center; - justify-content: center; - - &:hover { - cursor: pointer; - background-color: rgb(var(--hyacinth-500), 0.05); - } - } - } -} 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 f6aad176266..dd6a197a805 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 @@ -22,7 +22,6 @@ import { DocSkeletonManagerService, RichTextEditingMutation } from '@univerjs/do import { IRenderManagerService, ScrollBar } from '@univerjs/engine-render'; import { fromEvent } from 'rxjs'; import { VIEWPORT_KEY } from '../../basics/docs-view-key'; -import { CoverContentCommand } from '../../commands/commands/replace-content.command'; import { IEditorService } from '../../services/editor/editor-manager.service'; import { DocSelectionRenderService } from '../../services/selection/doc-selection-render.service'; @@ -43,16 +42,6 @@ export class DocEditorBridgeController extends Disposable implements IRenderModu } private _initialize() { - this.disposeWithMe( - this._editorService.resize$.subscribe((unitId: string) => { - if (unitId !== this._context.unitId) { - return; - } - - this._resize(unitId); - }) - ); - this._editorService.getAllEditor().forEach((editor) => { const unitId = editor.getEditorId(); @@ -74,8 +63,6 @@ export class DocEditorBridgeController extends Disposable implements IRenderModu this._initialBlur(); this._initialFocus(); - - this._initialValueChange(); } // eslint-disable-next-line complexity @@ -157,26 +144,24 @@ export class DocEditorBridgeController extends Disposable implements IRenderModu } private _initialSetValue() { - this.disposeWithMe( - this._editorService.setValue$.subscribe((param) => { - if (param.editorUnitId !== this._context.unitId) { - return; - } - - this._commandService.executeCommand(CoverContentCommand.id, { - unitId: param.editorUnitId, - body: param.body, - segmentId: null, - }); - }) - ); + // this.disposeWithMe( + // this._editorService.setValue$.subscribe((param) => { + // if (param.editorUnitId !== this._context.unitId) { + // return; + // } + + // this._commandService.executeCommand(CoverContentCommand.id, { + // unitId: param.editorUnitId, + // body: param.body, + // segmentId: null, + // }); + // }) + // ); } private _initialBlur() { this.disposeWithMe( this._editorService.blur$.subscribe(() => { - // this._docSelectionRenderService.removeAllRanges(); - this._docSelectionRenderService.blur(); }) ); @@ -228,11 +213,8 @@ export class DocEditorBridgeController extends Disposable implements IRenderModu const hasSearch = target.classList[0] || ''; if (checkForSubstrings(hasSearch, focusExcepts)) { - this._editorService.changeSpreadsheetFocusState(true); event.stopPropagation(); - return; } - this._editorService.changeSpreadsheetFocusState(false); }) ); @@ -251,39 +233,11 @@ export class DocEditorBridgeController extends Disposable implements IRenderModu return; } fromEvent(canvasEle, 'mousedown').subscribe((evt) => { - this._editorService.changeSpreadsheetFocusState(true); evt.stopPropagation(); }); }); } - private _initialValueChange() { - this.disposeWithMe( - this._docSelectionRenderService.onCompositionupdate$.subscribe(this._valueChange.bind(this)) - ); - this.disposeWithMe( - this._docSelectionRenderService.onInput$.subscribe(this._valueChange.bind(this)) - ); - this.disposeWithMe( - this._docSelectionRenderService.onKeydown$.subscribe(this._valueChange.bind(this)) - ); - this.disposeWithMe( - this._docSelectionRenderService.onPaste$.subscribe(this._valueChange.bind(this)) - ); - } - - private _valueChange() { - const { unitId } = this._context; - - const editor = this._editorService.getEditor(unitId); - - if (editor == null || editor.isSheetEditor()) { - return; - } - - this._editorService.refreshValueChange(unitId); - } - /** * Listen to document edits to refresh the size of the formula editor. */ @@ -305,8 +259,6 @@ export class DocEditorBridgeController extends Disposable implements IRenderModu // 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-selection-render.controller.ts b/packages/docs-ui/src/controllers/render-controllers/doc-selection-render.controller.ts index 4cbe5448033..7af3a5bcf49 100644 --- a/packages/docs-ui/src/controllers/render-controllers/doc-selection-render.controller.ts +++ b/packages/docs-ui/src/controllers/render-controllers/doc-selection-render.controller.ts @@ -221,18 +221,7 @@ export class DocSelectionRenderController extends Disposable implements IRenderM } private _setEditorFocus(unitId: string) { - // TODO@wzhudev: fix - /** - * The object for selecting data in the editor is set to the current sheet. - */ - // const sheetInstances = this._univerInstanceService.getAllUnitsForType(UniverInstanceType.UNIVER_SHEET); - // if (sheetInstances.length > 0) { - // const workbook = this._univerInstanceService.getCurrentUnitForType(UniverInstanceType.UNIVER_SHEET)!; - // this._editorService.setOperationSheetUnitId(workbook.getUnitId()); - // // this._editorService.setOperationSheetSubUnitId(workbook.getActiveSheet().getSheetId()); - // } - - this._editorService.focusStyle(unitId); + this._editorService.focus(unitId); } private _commandExecutedListener() { diff --git a/packages/docs-ui/src/index.ts b/packages/docs-ui/src/index.ts index 5c566d80c3b..6f572240a06 100644 --- a/packages/docs-ui/src/index.ts +++ b/packages/docs-ui/src/index.ts @@ -26,8 +26,8 @@ export { hasParagraphInTable } from './basics/paragraph'; export { docDrawingPositionToTransform, transformToDocDrawingPosition } from './basics/transform-position'; export { getCommandSkeleton, getRichTextEditPath } from './commands/util'; -export { TextEditor } from './components/editor/TextEditor'; -export { RangeSelector as DocRangeSelector } from './components/range-selector/RangeSelector'; +// export { TextEditor } from './components/editor/TextEditor'; +// export { RangeSelector as DocRangeSelector } from './components/range-selector/RangeSelector'; export { DocUIController } from './controllers/doc-ui.controller'; export { menuSchema as DocsUIMenuSchema } from './controllers/menu.schema'; export { DocBackScrollRenderController } from './controllers/render-controllers/back-scroll.render-controller'; 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 2540b94d748..95f587a3af1 100644 --- a/packages/docs-ui/src/services/editor/editor-manager.service.ts +++ b/packages/docs-ui/src/services/editor/editor-manager.service.ts @@ -14,13 +14,13 @@ * limitations under the License. */ -import type { DocumentDataModel, IDisposable, IDocumentBody, IDocumentData, Nullable, Workbook } from '@univerjs/core'; +import type { DocumentDataModel, IDisposable, IDocumentBody, IDocumentData, Nullable } from '@univerjs/core'; import type { ISuccinctDocRangeParam, Scene } from '@univerjs/engine-render'; import type { Observable } from 'rxjs'; import type { IEditorConfigParams, IEditorStateParams } from './editor'; -import { createIdentifier, DEFAULT_EMPTY_DOCUMENT_VALUE, Disposable, EDITOR_ACTIVATED, FOCUSING_EDITOR_INPUT_FORMULA, FOCUSING_EDITOR_STANDALONE, HorizontalAlign, ICommandService, IContextService, Inject, isInternalEditorID, IUndoRedoService, IUniverInstanceService, toDisposable, UniverInstanceType, VerticalAlign } from '@univerjs/core'; +import { createIdentifier, DEFAULT_EMPTY_DOCUMENT_VALUE, Disposable, EDITOR_ACTIVATED, FOCUSING_EDITOR_STANDALONE, HorizontalAlign, ICommandService, IContextService, Inject, isInternalEditorID, IUndoRedoService, IUniverInstanceService, toDisposable, UniverInstanceType, VerticalAlign } from '@univerjs/core'; import { DocSelectionManagerService } from '@univerjs/docs'; -import { isReferenceStrings, LexerTreeBuilder, operatorToken } from '@univerjs/engine-formula'; +import { LexerTreeBuilder } from '@univerjs/engine-formula'; import { IRenderManagerService } from '@univerjs/engine-render'; import { fromEvent, Subject } from 'rxjs'; import { Editor } from './editor'; @@ -46,145 +46,24 @@ export interface IEditorInputFormulaParam { formulaString: string; } -/** - * @deprecated - */ export interface IEditorService { getEditor(id?: string): Readonly>; register(config: IEditorConfigParams, container: HTMLDivElement): IDisposable; - /** - * @deprecated - */ - isVisible(id: string): Nullable; - - inputFormula$: Observable; - - /** - * @deprecated - */ - setFormula(formulaString: string): void; - - resize$: Observable; - /** - * @deprecated - */ - resize(id: string): void; - - /** - * @deprecated - */ getAllEditor(): Map; - /** - * The sheet currently being operated on will determine - * whether to include unitId information in the ref. - */ - setOperationSheetUnitId(unitId: Nullable): void; - getOperationSheetUnitId(): Nullable; - /** - * The sub-table within the sheet currently being operated on - * will determine whether to include subUnitId information in the ref. - */ - setOperationSheetSubUnitId(sheetId: Nullable): void; - getOperationSheetSubUnitId(): Nullable; - isEditor(editorUnitId: string): boolean; isSheetEditor(editorUnitId: string): boolean; - closeRangePrompt$: Observable; - /** - * @deprecated - */ - closeRangePrompt(): void; - blur$: Observable; - /** - * @deprecated - */ blur(): void; focus$: Observable; - /** - * @deprecated - */ - focus(editorUnitId?: string): void; - - setValue$: Observable; - valueChange$: Observable>; - - /** - * @deprecated - */ - setValue(val: string, editorUnitId?: string): void; - - /** - * @deprecated - */ - setValueNoRefresh(val: string, editorUnitId?: string): void; - - /** - * @deprecated - */ - setRichValue(body: IDocumentBody, editorUnitId?: string): void; - - /** - * @deprecated - */ - getFirstEditor(): Editor; - - focusStyle$: Observable>; - /** - * @deprecated - */ - focusStyle(editorUnitId: Nullable): void; - - /** - * @deprecated - */ - refreshValueChange(editorId: string): void; - - /** - * @deprecated - */ - checkValueLegality(editorId: string): boolean; - - /** - * @deprecated - */ - getValue(id: string): Nullable; - - /** - * @deprecated - */ - getRichValue(id: string): Nullable; - - /** - * @deprecated - */ - changeSpreadsheetFocusState(state: boolean): void; - - /** - * @deprecated - */ - getSpreadsheetFocusState(): boolean; - - /** - * @deprecated - */ - selectionChangingState(): boolean; - - singleSelection$: Observable; - /** - * @deprecated - */ - singleSelection(state: boolean): void; - - setFocusId(id: Nullable): void; - getFocusId(): Nullable; + focus(editorUnitId: string): void; + getFocusId(): Nullable; getFocusEditor(): Readonly>; } @@ -196,19 +75,6 @@ export class EditorService extends Disposable implements IEditorService, IDispos private readonly _state$ = new Subject>(); readonly state$ = this._state$.asObservable(); - private _currentSheetUnitId: Nullable; - - private _currentSheetSubUnitId: Nullable; - - private readonly _inputFormula$ = new Subject(); - readonly inputFormula$ = this._inputFormula$.asObservable(); - - private readonly _resize$ = new Subject(); - readonly resize$ = this._resize$.asObservable(); - - private readonly _closeRangePrompt$ = new Subject(); - readonly closeRangePrompt$ = this._closeRangePrompt$.asObservable(); - private readonly _blur$ = new Subject(); readonly blur$ = this._blur$.asObservable(); @@ -218,15 +84,9 @@ export class EditorService extends Disposable implements IEditorService, IDispos private readonly _setValue$ = new Subject(); readonly setValue$ = this._setValue$.asObservable(); - private readonly _valueChange$ = new Subject>(); - readonly valueChange$ = this._valueChange$.asObservable(); - private readonly _focusStyle$ = new Subject>(); readonly focusStyle$ = this._focusStyle$.asObservable(); - private readonly _singleSelection$ = new Subject(); - readonly singleSelection$ = this._singleSelection$.asObservable(); - private _spreadsheetFocusState: boolean = false; constructor( @@ -249,36 +109,30 @@ export class EditorService extends Disposable implements IEditorService, IDispos this.disposeWithMe( fromEvent(window, 'focusin').subscribe((event) => { const target = event.target as HTMLElement; - this._blurSheetEditor(target); }) ); } - /** @deprecated */ private _blurSheetEditor(target: HTMLElement) { if (editorFocusInElements.some((item) => target.classList.contains(item))) { return; } - // NOTE: Note that the focus editor will not be docs' editor but calling `this._editorService.blur()` will blur doc's editor. const focusEditor = this.getFocusEditor(); if (focusEditor && focusEditor.isSheetEditor() !== true) { this.blur(); } } - /** @deprecated */ - setFocusId(id: Nullable) { + private _setFocusId(id: Nullable) { this._focusEditorUnitId = id; } - /** @deprecated */ getFocusId() { return this._focusEditorUnitId; } - /** @deprecated */ getFocusEditor() { if (this._focusEditorUnitId) { return this.getEditor(this._focusEditorUnitId); @@ -295,113 +149,22 @@ export class EditorService extends Disposable implements IEditorService, IDispos return !!(editor && editor.isSheetEditor()); } - /** @deprecated */ - closeRangePrompt() { - const documentDataModel = this._univerInstanceService.getCurrentUniverDocInstance(); - if (!documentDataModel) { - return; - } - - const editorUnitId = documentDataModel.getUnitId(); - + blur() { + this._setFocusId(null); this._contextService.setContextValue(EDITOR_ACTIVATED, false); this._contextService.setContextValue(FOCUSING_EDITOR_STANDALONE, false); - if (!this.isEditor(editorUnitId) || this.isSheetEditor(editorUnitId)) { - return; - } - - this.changeSpreadsheetFocusState(false); - - this.blur(); - } - - /** @deprecated */ - changeSpreadsheetFocusState(state: boolean) { - this._spreadsheetFocusState = state; - } - - /** @deprecated */ - getSpreadsheetFocusState() { - return this._spreadsheetFocusState; - } - - /** @deprecated */ - focusStyle(editorUnitId: string) { - const editor = this.getEditor(editorUnitId); - if (!editor) { - return false; - } - - editor.setFocus(true); - - this._contextService.setContextValue(EDITOR_ACTIVATED, true); - - if (!isInternalEditorID(editorUnitId)) { - this._contextService.setContextValue(FOCUSING_EDITOR_STANDALONE, true); - // this._contextService.setContextValue(FOCUSING_UNIVER_EDITOR_STANDALONE_SINGLE_MODE, editor.isSingle()); - } - - if (!this._spreadsheetFocusState) { - this.singleSelection(!!editor.isSingleChoice()); - } - - this._focusStyle$.next(editorUnitId); - this.setFocusId(editorUnitId); - } - - /** @deprecated */ - singleSelection(state: boolean) { - this._singleSelection$.next(state); - } - - /** @deprecated */ - selectionChangingState() { - // const documentDataModel = this._univerInstanceService.getCurrentUniverDocInstance(); - const editorUnitId = this.getFocusId(); - if (editorUnitId == null) { - return true; - } - const editor = this.getEditor(editorUnitId); - - if (!editor || editor.isSheetEditor() || editor.isFormulaEditor()) { - return true; - } - - if (editor.onlyInputRange() !== true && editor.onlyInputFormula() !== true) { - this.blur(); - return true; - } - - if (editor.onlyInputFormula() === true && this._contextService.getContextValue(FOCUSING_EDITOR_INPUT_FORMULA) !== true) { - this.blur(); - return true; - } - - return !this.getSpreadsheetFocusState(); - } - - /** @deprecated */ - blur() { - if (!this._spreadsheetFocusState) { - this._closeRangePrompt$.next(null); - this.singleSelection(false); - this.setFocusId(null); - this._contextService.setContextValue(EDITOR_ACTIVATED, false); - this._contextService.setContextValue(FOCUSING_EDITOR_STANDALONE, false); - } - - this.getAllEditor().forEach((editor) => { - editor.setFocus(false); - }); - + const focusingEditor = this.getFocusEditor(); + focusingEditor?.blur(); this._focusStyle$.next(); this._blur$.next(null); } - /** @deprecated */ - focus(editorUnitId: string | undefined = this._univerInstanceService.getCurrentUniverDocInstance()?.getUnitId()) { + focus(editorUnitId: string) { + if (this._focusEditorUnitId) { + this.blur(); + } if (editorUnitId == null) { return; } @@ -412,10 +175,15 @@ export class EditorService extends Disposable implements IEditorService, IDispos } this._univerInstanceService.setCurrentUnitForType(editorUnitId); - const valueCount = editor.getValue().length; + this._contextService.setContextValue(EDITOR_ACTIVATED, true); - this.focusStyle(editorUnitId); + if (!isInternalEditorID(editorUnitId)) { + this._contextService.setContextValue(FOCUSING_EDITOR_STANDALONE, true); + } + + editor.focus(); + this._setFocusId(editorUnitId); this._focus$.next({ startOffset: valueCount, @@ -423,53 +191,6 @@ export class EditorService extends Disposable implements IEditorService, IDispos }); } - /** @deprecated */ - setFormula(formulaString: string, editorUnitId = this._getCurrentEditorUnitId()) { - this._inputFormula$.next({ formulaString, editorUnitId }); - } - - /** @deprecated */ - setValue(val: string, editorUnitId: string = this._getCurrentEditorUnitId()) { - this.setValueNoRefresh(val, editorUnitId); - this._refreshValueChange(editorUnitId); - } - - /** @deprecated */ - setValueNoRefresh(val: string, editorUnitId: string) { - this._setValue$.next({ - body: { - dataStream: val, - }, - editorUnitId, - }); - - this.resize(editorUnitId); - } - - /** @deprecated */ - getValue(id: string) { - const editor = this.getEditor(id); - if (editor == null) { - return; - } - return editor.getValue(); - } - - /** @deprecated */ - setRichValue(body: IDocumentBody, editorUnitId: string = this._getCurrentEditorUnitId()) { - this._setValue$.next({ body, editorUnitId }); - this._refreshValueChange(editorUnitId); - } - - /** @deprecated */ - getRichValue(id: string) { - const editor = this.getEditor(id); - if (editor == null) { - return; - } - return editor.getBody(); - } - override dispose(): void { this._state$.complete(); this._editors.clear(); @@ -480,53 +201,10 @@ export class EditorService extends Disposable implements IEditorService, IDispos return this._editors.get(id); } - /** @deprecated */ getAllEditor() { return this._editors; } - /** @deprecated */ - getFirstEditor() { - return [...this.getAllEditor().values()][0]; - } - - /** @deprecated */ - resize(unitId: string) { - const editor = this.getEditor(unitId); - if (editor == null) { - return; - } - - editor.verticalAlign(); - - this._resize$.next(unitId); - } - - /** @deprecated */ - isVisible(id: string) { - return this.getEditor(id)?.isVisible(); - } - - /** @deprecated */ - setOperationSheetUnitId(unitId: Nullable) { - this._currentSheetUnitId = unitId; - } - - /** @deprecated */ - getOperationSheetUnitId() { - return this._currentSheetUnitId; - } - - /** @deprecated */ - setOperationSheetSubUnitId(sheetId: Nullable) { - this._currentSheetSubUnitId = sheetId; - } - - /** @deprecated */ - getOperationSheetSubUnitId() { - return this._currentSheetSubUnitId; - } - register(config: IEditorConfigParams, container: HTMLDivElement): IDisposable { const { initialSnapshot, canvasStyle = {} } = config; const editorUnitId = initialSnapshot.id; @@ -586,76 +264,6 @@ export class EditorService extends Disposable implements IEditorService, IDispos editor.dispose(); this._editors.delete(editorUnitId); this._univerInstanceService.disposeUnit(editorUnitId); - // this._contextService.setContextValue(FOCUSING_UNIVER_EDITOR_STANDALONE_SINGLE_MODE, false); - - // DEBT: no necessary when we refactor editor module - if (!this.isSheetEditor(editorUnitId)) return; - - /** - * 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); - - if (current) { - this._univerInstanceService.focusUnit(current.getUnitId()); - } - } - } - - /** @deprecated */ - refreshValueChange(editorUnitId: string) { - this._refreshValueChange(editorUnitId); - } - - /** @deprecated */ - checkValueLegality(editorUnitId: string) { - const editor = this._editors.get(editorUnitId); - - if (editor == null) { - return true; - } - - let value = editor.getValue(); - - editor.setValueLegality(); - - value = value.replace(/\r\n/g, '').replace(/\n/g, '').replace(/\n/g, ''); - - if (value.length === 0) { - return true; - } - - if (editor.onlyInputFormula()) { - if (value.substring(0, 1) !== operatorToken.EQUALS) { - editor.setValueLegality(false); - return false; - } - const bracketCount = this._lexerTreeBuilder.checkIfAddBracket(value); - editor.setValueLegality(bracketCount === 0); - } else if (editor.onlyInputRange()) { - const valueArray = value.split(','); - if (editor.isSingleChoice() && valueArray.length > 1) { - editor.setValueLegality(false); - return false; - } - - editor.setValueLegality(isReferenceStrings(value)); - } - - return editor.isValueLegality(); - } - - private _refreshValueChange(editorId: string) { - const editor = this.getEditor(editorId); - if (editor == null) { - return; - } - - this._valueChange$.next(editor); } private _getCurrentEditorUnitId() { diff --git a/packages/docs-ui/src/services/editor/editor.ts b/packages/docs-ui/src/services/editor/editor.ts index e038aa75155..2fe5df992cd 100644 --- a/packages/docs-ui/src/services/editor/editor.ts +++ b/packages/docs-ui/src/services/editor/editor.ts @@ -173,12 +173,6 @@ export class Editor extends Disposable implements IEditor { private readonly _selectionChange$ = new Subject(); selectionChange$: Observable = this._selectionChange$.asObservable(); - private _valueLegality = true; - - private _openForSheetUnitId: Nullable; - - private _openForSheetSubUnitId: Nullable; - constructor( private _param: IEditorOptions, private _univerInstanceService: IUniverInstanceService, @@ -187,8 +181,6 @@ export class Editor extends Disposable implements IEditor { private _undoRedoService: IUndoRedoService ) { super(); - this._openForSheetUnitId = this._param.openForSheetUnitId; - this._openForSheetSubUnitId = this._param.openForSheetSubUnitId; this._listenSelection(); } @@ -394,40 +386,6 @@ export class Editor extends Disposable implements IEditor { return this._param.render; } - isSingleChoice() { - return this._param.isSingleChoice ?? false; - } - - /** @deprecated */ - setOpenForSheetUnitId(unitId: Nullable) { - this._openForSheetUnitId = unitId; - } - - /** @deprecated */ - getOpenForSheetUnitId() { - return this._openForSheetUnitId; - } - - /** @deprecated */ - setOpenForSheetSubUnitId(subUnitId: Nullable) { - this._openForSheetSubUnitId = subUnitId; - } - - /** @deprecated */ - getOpenForSheetSubUnitId() { - return this._openForSheetSubUnitId; - } - - /** @deprecated */ - isValueLegality() { - return this._valueLegality === true; - } - - /** @deprecated */ - setValueLegality(state = true) { - this._valueLegality = state; - } - isFocus() { return this._focus; } @@ -469,16 +427,10 @@ export class Editor extends Disposable implements IEditor { return this._param.visible; } - /** @deprecated */ isSheetEditor() { return isInternalEditorID(this._getEditorId()); } - /** @deprecated */ - isFormulaEditor() { - return this._param.isFormulaEditor === true; - } - /** * @deprecated use getDocumentData. */ diff --git a/packages/sheets-data-validation-ui/src/views/components/formula-input/custom-formula-input.tsx b/packages/sheets-data-validation-ui/src/views/components/formula-input/custom-formula-input.tsx index 3f50e0e7279..da5f0db20e9 100644 --- a/packages/sheets-data-validation-ui/src/views/components/formula-input/custom-formula-input.tsx +++ b/packages/sheets-data-validation-ui/src/views/components/formula-input/custom-formula-input.tsx @@ -29,6 +29,7 @@ export function CustomFormulaInput(props: IFormulaInputProps) { const handleOutClick = formulaEditorActionsRef.current?.handleOutClick; handleOutClick && handleOutClick(e, () => isFocusFormulaEditorSet(false)); }); + return ( { - // 在进行多个 input 切换的时候,失焦必须快于获得焦点. if (_isFocus) { - const time = setTimeout(() => { - isFocusSet(_isFocus); - if (_isFocus) { - focus(); - } - }, 30); - return () => { - clearTimeout(time); - }; + isFocusSet(_isFocus); + focus(); } else { if (resetSelectionOnBlur) { + editor?.blur(); resetSelection(); } isFocusSet(_isFocus); } - }, [_isFocus, focus, resetSelectionOnBlur]); + }, [_isFocus, editor, focus, resetSelection, resetSelectionOnBlur]); const { checkScrollBar } = useResize(editor); useRefactorEffect(isFocus, Boolean(isSelecting && docFocusing), unitId); diff --git a/packages/sheets-formula-ui/src/views/range-selector/index.tsx b/packages/sheets-formula-ui/src/views/range-selector/index.tsx index cf3b4f56486..f77a6b75820 100644 --- a/packages/sheets-formula-ui/src/views/range-selector/index.tsx +++ b/packages/sheets-formula-ui/src/views/range-selector/index.tsx @@ -17,7 +17,7 @@ import type { IDisposable, IUnitRangeName } from '@univerjs/core'; import type { Editor } from '@univerjs/docs-ui'; import type { ReactNode } from 'react'; -import { createInternalEditorID, DOCS_NORMAL_EDITOR_UNIT_ID_KEY, generateRandomId, ICommandService, IUniverInstanceService, LocaleService, UniverInstanceType, useDependency, useObservable } from '@univerjs/core'; +import { createInternalEditorID, generateRandomId, ICommandService, IUniverInstanceService, LocaleService, UniverInstanceType, useDependency, useObservable } from '@univerjs/core'; import { Button, Dialog, Input, Tooltip } from '@univerjs/design'; import { DocBackScrollRenderController, IEditorService } from '@univerjs/docs-ui'; import { deserializeRangeWithSheet, LexerTreeBuilder, matchToken, sequenceNodeType } from '@univerjs/engine-formula'; @@ -28,7 +28,7 @@ import { RANGE_SELECTOR_SYMBOLS, SetCellEditVisibleOperation } from '@univerjs/s import cl from 'clsx'; import React, { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react'; -import { filter, noop, throttleTime } from 'rxjs'; +import { noop, throttleTime } from 'rxjs'; import { RefSelectionsRenderService } from '../../services/render-services/ref-selections.render-service'; import { useEditorInput } from './hooks/useEditorInput'; @@ -174,22 +174,13 @@ export function RangeSelector(props: IRangeSelectorProps) { const focus = useFocus(editor); useLayoutEffect(() => { - // 如果是失去焦点的话,需要立刻执行 - // 在进行多个 input 切换的时候,失焦必须立刻执行. if (_isFocus) { - const time = setTimeout(() => { - isFocusSet(_isFocus); - if (_isFocus) { - focus(); - } - }, 30); - return () => { - clearTimeout(time); - }; + isFocusSet(_isFocus); + focus(); } else { + editor?.blur(); resetSelection(); isFocusSet(_isFocus); - editor?.blur(); } }, [_isFocus, focus]); @@ -303,15 +294,9 @@ export function RangeSelector(props: IRangeSelectorProps) { useFirstHighlightDoc(rangeString, '', isFocus, highlightDoc, highlightSheet, editor); const handleClick = () => { - // 在进行多个 input 切换的时候,失焦必须快于获得焦点. - // 即使失焦是 mousedown 事件, - // 聚焦是 mouseup 事件, - // 但是 react 的 useEffect 无法保证顺序,无法确保失焦在聚焦之前. - setTimeout(() => { - onFocus(); - focus(); - isFocusSet(true); - }, 30); + onFocus(); + focus(); + isFocusSet(true); }; const handleConfirm = (ranges: IUnitRangeName[]) => { @@ -498,20 +483,20 @@ function RangeSelectorDialog(props: { } }, [ranges]); - useEffect(() => { - const d = editorService.focusStyle$ - .pipe( - filter((e) => !!e && DOCS_NORMAL_EDITOR_UNIT_ID_KEY !== e) - ) - .subscribe((e) => { - if (e !== editorId) { - handleClose(); - } - }); - return () => { - d.unsubscribe(); - }; - }, [editorService, editorId]); + // useEffect(() => { + // const d = editorService.focusStyle$ + // .pipe( + // filter((e) => !!e && DOCS_NORMAL_EDITOR_UNIT_ID_KEY !== e) + // ) + // .subscribe((e) => { + // if (e !== editorId) { + // handleClose(); + // } + // }); + // return () => { + // d.unsubscribe(); + // }; + // }, [editorService, editorId]); return ( { - editorService.closeRangePrompt(); }, width: 333, }); 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 2a457ffa5d2..96c3d0971b5 100644 --- a/packages/sheets-ui/src/controllers/editor/editing.render-controller.ts +++ b/packages/sheets-ui/src/controllers/editor/editing.render-controller.ts @@ -398,8 +398,6 @@ export class EditingRenderController extends Disposable implements IRenderModule return; } - this._setOpenForCurrent(unitId, sheetId); - const { document, scene } = editorObject; this._contextService.setContextValue(EDITOR_ACTIVATED, true); @@ -458,7 +456,6 @@ export class EditingRenderController extends Disposable implements IRenderModule private async _handleEditorInvisible(param: IEditorBridgeServiceVisibleParam) { const editCellState = this._editorBridgeService.getEditCellState(); let { keycode } = param; - this._setOpenForCurrent(null, null); this._cursorChange = CursorChange.InitialState; this._exitInput(param); @@ -519,18 +516,6 @@ export class EditingRenderController extends Disposable implements IRenderModule this._moveCursor(keycode); } - private _setOpenForCurrent(unitId: Nullable, sheetId: Nullable) { - const sheetEditors = this._editorService.getAllEditor(); - for (const [_, sheetEditor] of sheetEditors) { - if (!sheetEditor.isSheetEditor()) { - continue; - } - - sheetEditor.setOpenForSheetUnitId(unitId); - sheetEditor.setOpenForSheetSubUnitId(sheetId); - } - } - private _getEditorObject() { return getEditorObject(this._editorBridgeService.getCurrentEditorId(), this._renderManagerService); } diff --git a/packages/sheets-ui/src/services/editor-bridge.service.ts b/packages/sheets-ui/src/services/editor-bridge.service.ts index 1b7e2af6004..93978ec6512 100644 --- a/packages/sheets-ui/src/services/editor-bridge.service.ts +++ b/packages/sheets-ui/src/services/editor-bridge.service.ts @@ -218,10 +218,6 @@ export class EditorBridgeService extends Disposable implements IEditorBridgeServ startY = this._currentEditCellLayout.position.startY; } - this._editorService.setOperationSheetUnitId(unitId); - - this._editorService.setOperationSheetSubUnitId(sheetId); - this._currentEditCellLayout = { position: { startX, @@ -387,10 +383,6 @@ export class EditorBridgeService extends Disposable implements IEditorBridgeServ } } - this._editorService.setOperationSheetUnitId(unitId); - - this._editorService.setOperationSheetSubUnitId(sheetId); - return { position: { startX, diff --git a/packages/slides-ui/src/controllers/slide-editing.render-controller.ts b/packages/slides-ui/src/controllers/slide-editing.render-controller.ts index c4034ba6e9d..a84570ec576 100644 --- a/packages/slides-ui/src/controllers/slide-editing.render-controller.ts +++ b/packages/slides-ui/src/controllers/slide-editing.render-controller.ts @@ -14,6 +14,25 @@ * limitations under the License. */ +import type { + ICommandInfo, + IDisposable, + IDocumentBody, + IPosition, + Nullable, + SlideDataModel, + UnitModel } from '@univerjs/core'; +import type { IDocObjectParam, IEditorInputConfig } from '@univerjs/docs-ui'; +import type { + DocBackground, + Documents, + DocumentSkeleton, + IDocumentLayoutObject, + IRenderContext, + IRenderModule, + Scene, +} from '@univerjs/engine-render'; +import type { IEditorBridgeServiceVisibleParam } from '../services/slide-editor-bridge.service'; import { DEFAULT_EMPTY_DOCUMENT_VALUE, Direction, @@ -52,30 +71,11 @@ import { } from '@univerjs/engine-render'; import { ILayoutService, KeyCode } from '@univerjs/ui'; import { filter } from 'rxjs'; -import type { - ICommandInfo, - IDisposable, - IDocumentBody, - IPosition, - Nullable, - SlideDataModel, - UnitModel } from '@univerjs/core'; -import type { IDocObjectParam, IEditorInputConfig } from '@univerjs/docs-ui'; -import type { - DocBackground, - Documents, - DocumentSkeleton, - IDocumentLayoutObject, - IRenderContext, - IRenderModule, - Scene, -} from '@univerjs/engine-render'; import { SetTextEditArrowOperation } from '../commands/operations/text-edit.operation'; import { SLIDE_EDITOR_ID } from '../const'; import { ISlideEditorBridgeService } from '../services/slide-editor-bridge.service'; import { ISlideEditorManagerService } from '../services/slide-editor-manager.service'; import { CursorChange } from '../type'; -import type { IEditorBridgeServiceVisibleParam } from '../services/slide-editor-bridge.service'; const HIDDEN_EDITOR_POSITION = -1000; @@ -745,18 +745,6 @@ export class SlideEditingRenderController extends Disposable implements IRenderM this._handleEditorVisible({ visible: true, eventType: 3, unitId }); } - private _setOpenForCurrent(unitId: Nullable, subUnitId: Nullable) { - const editors = this._editorService.getAllEditor(); - for (const [_, ed] of editors) { - // if (!ed.isSheetEditor()) { - // continue; - // } - - ed.setOpenForSheetUnitId(unitId); - ed.setOpenForSheetSubUnitId(subUnitId); - } - } - private _getEditorObject() { return getEditorObject(this._editorBridgeService.getCurrentEditorId(), this._renderManagerService); } @@ -764,8 +752,6 @@ export class SlideEditingRenderController extends Disposable implements IRenderM private async _handleEditorInvisible(param: IEditorBridgeServiceVisibleParam) { const { keycode } = param; - this._setOpenForCurrent(null, null); - this._cursorChange = CursorChange.InitialState; this._exitInput(param); diff --git a/packages/slides-ui/src/views/editor-container/EditorContainer.tsx b/packages/slides-ui/src/views/editor-container/EditorContainer.tsx index a04b58dcbdd..0bea2d643d6 100644 --- a/packages/slides-ui/src/views/editor-container/EditorContainer.tsx +++ b/packages/slides-ui/src/views/editor-container/EditorContainer.tsx @@ -16,7 +16,7 @@ import type { IDocumentData } from '@univerjs/core'; import { DEFAULT_EMPTY_DOCUMENT_VALUE, DocumentFlavor, IContextService, useDependency } from '@univerjs/core'; -import { IEditorService, TextEditor } from '@univerjs/docs-ui'; +import { IEditorService } from '@univerjs/docs-ui'; import { FIX_ONE_PIXEL_BLUR_OFFSET } from '@univerjs/engine-render'; import { DISABLE_AUTO_FOCUS_KEY, useObservable } from '@univerjs/ui'; @@ -131,14 +131,14 @@ export const SlideEditorContainer: React.FC = () => { height: state.height, }} > - + /> */}
); }; From 2ddedd3848270710902685c79d735b3d4df91507 Mon Sep 17 00:00:00 2001 From: zhangw Date: Thu, 19 Dec 2024 11:35:18 -0500 Subject: [PATCH 058/134] feat: update --- .../services/editor/editor-manager.service.ts | 25 +---- .../docs-ui/src/services/editor/editor.ts | 99 +------------------ 2 files changed, 2 insertions(+), 122 deletions(-) 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 95f587a3af1..113ece19307 100644 --- a/packages/docs-ui/src/services/editor/editor-manager.service.ts +++ b/packages/docs-ui/src/services/editor/editor-manager.service.ts @@ -17,10 +17,9 @@ import type { DocumentDataModel, IDisposable, IDocumentBody, IDocumentData, Nullable } from '@univerjs/core'; import type { ISuccinctDocRangeParam, Scene } from '@univerjs/engine-render'; import type { Observable } from 'rxjs'; -import type { IEditorConfigParams, IEditorStateParams } from './editor'; +import type { IEditorConfigParams } from './editor'; import { createIdentifier, DEFAULT_EMPTY_DOCUMENT_VALUE, Disposable, EDITOR_ACTIVATED, FOCUSING_EDITOR_STANDALONE, HorizontalAlign, ICommandService, IContextService, Inject, isInternalEditorID, IUndoRedoService, IUniverInstanceService, toDisposable, UniverInstanceType, VerticalAlign } from '@univerjs/core'; import { DocSelectionManagerService } from '@univerjs/docs'; -import { LexerTreeBuilder } from '@univerjs/engine-formula'; import { IRenderManagerService } from '@univerjs/engine-render'; import { fromEvent, Subject } from 'rxjs'; import { Editor } from './editor'; @@ -72,27 +71,15 @@ export class EditorService extends Disposable implements IEditorService, IDispos private _focusEditorUnitId: Nullable; - private readonly _state$ = new Subject>(); - readonly state$ = this._state$.asObservable(); - private readonly _blur$ = new Subject(); readonly blur$ = this._blur$.asObservable(); private readonly _focus$ = new Subject(); readonly focus$ = this._focus$.asObservable(); - private readonly _setValue$ = new Subject(); - readonly setValue$ = this._setValue$.asObservable(); - - private readonly _focusStyle$ = new Subject>(); - readonly focusStyle$ = this._focusStyle$.asObservable(); - - private _spreadsheetFocusState: boolean = false; - constructor( @IUniverInstanceService private readonly _univerInstanceService: IUniverInstanceService, @IRenderManagerService private readonly _renderManagerService: IRenderManagerService, - @Inject(LexerTreeBuilder) private readonly _lexerTreeBuilder: LexerTreeBuilder, @Inject(DocSelectionManagerService) private readonly _docSelectionManagerService: DocSelectionManagerService, @IContextService private readonly _contextService: IContextService, @ICommandService private readonly _commandService: ICommandService, @@ -143,7 +130,6 @@ 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()); @@ -156,8 +142,6 @@ export class EditorService extends Disposable implements IEditorService, IDispos const focusingEditor = this.getFocusEditor(); focusingEditor?.blur(); - this._focusStyle$.next(); - this._blur$.next(null); } @@ -192,7 +176,6 @@ export class EditorService extends Disposable implements IEditorService, IDispos } override dispose(): void { - this._state$.complete(); this._editors.clear(); super.dispose(); } @@ -242,12 +225,6 @@ export class EditorService extends Disposable implements IEditorService, IDispos if (!config.scrollBar) { (render.mainComponent?.getScene() as Scene)?.getViewports()?.[0].getScrollBar()?.dispose(); } - - // @ggg, Move this to Text Editor? - if (!editor.isSheetEditor() && !config.noNeedVerticalAlign) { - editor.verticalAlign(); - editor.updateCanvasStyle(); - } } return toDisposable(() => { this._unRegister(editorUnitId); diff --git a/packages/docs-ui/src/services/editor/editor.ts b/packages/docs-ui/src/services/editor/editor.ts index 2fe5df992cd..ded45747231 100644 --- a/packages/docs-ui/src/services/editor/editor.ts +++ b/packages/docs-ui/src/services/editor/editor.ts @@ -17,7 +17,7 @@ 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 { DEFAULT_STYLES, Disposable, isInternalEditorID, UniverInstanceType } from '@univerjs/core'; +import { Disposable, isInternalEditorID, UniverInstanceType } from '@univerjs/core'; import { KeyCode } from '@univerjs/ui'; import { merge, type Observable, Subject } from 'rxjs'; import { filter } from 'rxjs/operators'; @@ -99,47 +99,6 @@ export interface IEditorConfigParams { // 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; - /** - * If the editor is for formula editing. - * @deprecated this is a temp fix before refactoring editor. - */ - isFormulaEditor?: boolean; - /** - * @deprecated The implementer makes its own judgment. - */ - isSingle?: boolean; - /** - * @deprecated The implementer makes its own judgment. - */ - onlyInputFormula?: boolean; - /** - * @deprecated The implementer makes its own judgment. - */ - onlyInputRange?: boolean; - /** - * @deprecated The implementer makes its own judgment. - */ - onlyInputContent?: boolean; - /** - * @deprecated The implementer makes its own judgment. - */ - isSingleChoice?: boolean; - /** - * @deprecated The implementer makes its own judgment. - */ - openForSheetUnitId?: Nullable; - /** - * @deprecated The implementer makes its own judgment. - */ - openForSheetSubUnitId?: Nullable; } export interface IEditorOptions extends IEditorConfigParams, IEditorStateParams { @@ -395,30 +354,10 @@ export class Editor extends Disposable implements IEditor { this._focus = state; } - /** @deprecated */ - isSingle() { - return this._param.isSingle === true || this.onlyInputRange(); - } - isReadOnly() { return this._param.readonly === true; } - /** @deprecated */ - onlyInputContent() { - return this._param.onlyInputContent === true; - } - - /** @deprecated */ - onlyInputFormula() { - return this._param.onlyInputFormula === true; - } - - /** @deprecated */ - onlyInputRange() { - return this._param.onlyInputRange === true; - } - getBoundingClientRect() { return this._param.editorDom.getBoundingClientRect(); } @@ -458,42 +397,6 @@ export class Editor extends Disposable implements IEditor { }; } - /** - * @deprecated. - */ - verticalAlign() { - const docDataModel = this._getDocDataModel(); - - if (docDataModel == null) { - return; - } - - const { width, height } = this._param.editorDom.getBoundingClientRect(); - - if (height === 0 || width === 0) { - return; - } - - if (!this.isSingle()) { - docDataModel.updateDocumentDataPageSize(width, undefined); - return; - } - - let fontSize = DEFAULT_STYLES.fs; - - if (this._param.canvasStyle?.fontSize) { - fontSize = this._param.canvasStyle.fontSize; - } - - const top = (height - (fontSize * 4 / 3)) / 2 - 2; - - docDataModel.updateDocumentDataMargin({ - t: top < 0 ? 0 : top, - }); - - docDataModel.updateDocumentDataPageSize(undefined, undefined); - } - /** * @deprecated. */ From d7c1c70954935cc35d12612414025e0bee463d86 Mon Sep 17 00:00:00 2001 From: zhangw Date: Thu, 19 Dec 2024 11:40:39 -0500 Subject: [PATCH 059/134] feat: update --- packages/docs-ui/src/services/editor/editor.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/docs-ui/src/services/editor/editor.ts b/packages/docs-ui/src/services/editor/editor.ts index ded45747231..70e963ca3a0 100644 --- a/packages/docs-ui/src/services/editor/editor.ts +++ b/packages/docs-ui/src/services/editor/editor.ts @@ -99,6 +99,8 @@ export interface IEditorConfigParams { // show scrollBar scrollBar?: boolean; + + isSingle?: boolean; } export interface IEditorOptions extends IEditorConfigParams, IEditorStateParams { @@ -354,6 +356,10 @@ export class Editor extends Disposable implements IEditor { this._focus = state; } + isSingle() { + return this._param.isSingle === true; + } + isReadOnly() { return this._param.readonly === true; } From 8ed00860a3c68fc4431dfc09f3f2d66ecfb0ed39 Mon Sep 17 00:00:00 2001 From: zhangw Date: Thu, 19 Dec 2024 11:45:43 -0500 Subject: [PATCH 060/134] feat: update --- packages/docs-ui/src/services/editor/editor.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/docs-ui/src/services/editor/editor.ts b/packages/docs-ui/src/services/editor/editor.ts index 70e963ca3a0..ff894559f78 100644 --- a/packages/docs-ui/src/services/editor/editor.ts +++ b/packages/docs-ui/src/services/editor/editor.ts @@ -100,6 +100,9 @@ export interface IEditorConfigParams { // show scrollBar scrollBar?: boolean; + /** + * show text on single line + */ isSingle?: boolean; } From ac37858c3ea62fd8390a21ee5e03394dfd03b663 Mon Sep 17 00:00:00 2001 From: zhangw Date: Thu, 19 Dec 2024 12:01:41 -0500 Subject: [PATCH 061/134] feat: update --- .../docs-ui/src/services/editor/editor.ts | 18 +++++++++++++++++ .../operations/insert-function.operation.ts | 18 +++++++++++------ .../views/more-functions/MoreFunctions.tsx | 20 ++++++++++++++----- 3 files changed, 45 insertions(+), 11 deletions(-) diff --git a/packages/docs-ui/src/services/editor/editor.ts b/packages/docs-ui/src/services/editor/editor.ts index ff894559f78..aa0de3861ba 100644 --- a/packages/docs-ui/src/services/editor/editor.ts +++ b/packages/docs-ui/src/services/editor/editor.ts @@ -315,6 +315,24 @@ export class Editor extends Disposable implements IEditor { }); } + replaceText(text: string) { + const data = this.getDocumentData(); + + this.setDocumentData( + { + ...data, + body: { + dataStream: `${text}\r\n`, + }, + }, + [{ + startOffset: text.length, + endOffset: text.length, + collapsed: true, + }] + ); + } + // Clear the undo redo history of this editor. clearUndoRedoHistory(): void { const editorUnitId = this.getEditorId(); diff --git a/packages/sheets-formula-ui/src/commands/operations/insert-function.operation.ts b/packages/sheets-formula-ui/src/commands/operations/insert-function.operation.ts index 482727cfd6b..1034b3c4068 100644 --- a/packages/sheets-formula-ui/src/commands/operations/insert-function.operation.ts +++ b/packages/sheets-formula-ui/src/commands/operations/insert-function.operation.ts @@ -19,6 +19,7 @@ import { CellValueType, CommandType, DEFAULT_EMPTY_DOCUMENT_VALUE, + DOCS_NORMAL_EDITOR_UNIT_ID_KEY, getCellValueType, ICommandService, isRealNum, @@ -27,6 +28,7 @@ import { } from '@univerjs/core'; import { IEditorService } from '@univerjs/docs-ui'; import { serializeRange } from '@univerjs/engine-formula'; +import { DeviceInputEventType } from '@univerjs/engine-render'; import { getCellAtRowCol, @@ -35,6 +37,7 @@ import { SheetsSelectionsService, } from '@univerjs/sheets'; import { type IInsertFunction, InsertFunctionCommand } from '@univerjs/sheets-formula'; +import { IEditorBridgeService } from '@univerjs/sheets-ui'; export interface IInsertFunctionOperationParams { /** @@ -46,6 +49,7 @@ export interface IInsertFunctionOperationParams { export const InsertFunctionOperation: ICommand = { id: 'formula-ui.operation.insert-function', type: CommandType.OPERATION, + // eslint-disable-next-line max-lines-per-function handler: async (accessor: IAccessor, params: IInsertFunctionOperationParams) => { const selectionManagerService = accessor.get(SheetsSelectionsService); const editorService = accessor.get(IEditorService); @@ -62,6 +66,7 @@ export const InsertFunctionOperation: ICommand = { const { value } = params; const commandService = accessor.get(ICommandService); + const editorBridgeService = accessor.get(IEditorBridgeService); // No match refRange situation, enter edit mode // In each range, first take the judgment result of the primary position (if there is no primary, take the upper left corner), @@ -148,12 +153,13 @@ export const InsertFunctionOperation: ICommand = { selections: [resultRange], }; await commandService.executeCommand(SetSelectionsOperation.id, setSelectionParams); - - // TODO@DR-Univer: Maybe setTimeout can be removed - setTimeout(() => { - // edit cell - editorService.setFormula(`=${value}(${editFormulaRangeString}`); - }, 0); + const editor = editorService.getEditor(DOCS_NORMAL_EDITOR_UNIT_ID_KEY); + editorBridgeService.changeVisible({ + visible: true, + unitId, + eventType: DeviceInputEventType.Dblclick, + }); + editor?.replaceText(`=${value}(${editFormulaRangeString}`); } if (list.length === 0) return false; diff --git a/packages/sheets-formula-ui/src/views/more-functions/MoreFunctions.tsx b/packages/sheets-formula-ui/src/views/more-functions/MoreFunctions.tsx index 886c42df9af..e0d580f2176 100644 --- a/packages/sheets-formula-ui/src/views/more-functions/MoreFunctions.tsx +++ b/packages/sheets-formula-ui/src/views/more-functions/MoreFunctions.tsx @@ -15,10 +15,12 @@ */ import type { IFunctionInfo } from '@univerjs/engine-formula'; -import { LocaleService, useDependency } from '@univerjs/core'; +import { DOCS_NORMAL_EDITOR_UNIT_ID_KEY, IUniverInstanceService, LocaleService, useDependency } from '@univerjs/core'; import { Button } from '@univerjs/design'; import { IEditorService } from '@univerjs/docs-ui'; -import { useActiveWorkbook } from '@univerjs/sheets-ui'; +import { DeviceInputEventType } from '@univerjs/engine-render'; +import { getSheetCommandTarget } from '@univerjs/sheets'; +import { IEditorBridgeService, useActiveWorkbook } from '@univerjs/sheets-ui'; import React, { useState } from 'react'; import styles from './index.module.less'; import { InputParams } from './input-params/InputParams'; @@ -30,9 +32,10 @@ export function MoreFunctions() { const [inputParams, setInputParams] = useState(false); // const [params, setParams] = useState([]); // TODO@Dushusir: bind setParams to InputParams's onChange const [functionInfo, setFunctionInfo] = useState(null); - + const editorBridgeService = useDependency(IEditorBridgeService); const localeService = useDependency(LocaleService); const editorService = useDependency(IEditorService); + const univerInstanceService = useDependency(IUniverInstanceService); function handleClickNextPrev() { if (selectFunction) { @@ -44,8 +47,15 @@ export function MoreFunctions() { } function handleConfirm() { - // TODO@Dushusir: save function `=${functionInfo?.functionName}(${params.join(',')})` - editorService.setFormula(`=${functionInfo?.functionName}(`); + const sheetTarget = getSheetCommandTarget(univerInstanceService); + if (!sheetTarget) return; + editorBridgeService.changeVisible({ + visible: true, + unitId: sheetTarget.unitId, + eventType: DeviceInputEventType.Dblclick, + }); + const editor = editorService.getEditor(DOCS_NORMAL_EDITOR_UNIT_ID_KEY); + editor?.replaceText(`=${functionInfo?.functionName}(`); } return ( From cc7e6d4def51bd4a8c55ab21ba7d59bbb9599890 Mon Sep 17 00:00:00 2001 From: zhangw Date: Fri, 20 Dec 2024 01:13:34 -0500 Subject: [PATCH 062/134] feat: update --- .../editor/data-sync.controller.ts | 3 +- .../editor/editing.render-controller.ts | 31 +++++++++---------- .../editor-container/EditorContainer.tsx | 1 - .../src/views/formula-bar/FormulaBar.tsx | 1 - 4 files changed, 16 insertions(+), 20 deletions(-) diff --git a/packages/sheets-ui/src/controllers/editor/data-sync.controller.ts b/packages/sheets-ui/src/controllers/editor/data-sync.controller.ts index ab692e92a3a..a5e05bff6e8 100644 --- a/packages/sheets-ui/src/controllers/editor/data-sync.controller.ts +++ b/packages/sheets-ui/src/controllers/editor/data-sync.controller.ts @@ -214,7 +214,7 @@ export class EditorDataSyncController extends Disposable { const INCLUDE_LIST = [DOCS_NORMAL_EDITOR_UNIT_ID_KEY, DOCS_FORMULA_BAR_EDITOR_UNIT_ID_KEY]; const skeleton = this._renderManagerService.getRenderById(unitId)?.with(DocSkeletonManagerService).getSkeleton(); - const docDataModel = this._univerInstanceService.getUniverDocInstance(unitId); + const docDataModel = this._univerInstanceService.getUnit(unitId, UniverInstanceType.UNIVER_DOC); const docViewModel = this._getEditorViewModel(unitId); if (docDataModel == null || docViewModel == null || skeleton == null) { @@ -229,7 +229,6 @@ export class EditorDataSyncController extends Disposable { docViewModel.reset(docDataModel); const currentRender = this._renderManagerService.getRenderById(unitId); - if (currentRender == null) { return; } 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 96c3d0971b5..e896d001b65 100644 --- a/packages/sheets-ui/src/controllers/editor/editing.render-controller.ts +++ b/packages/sheets-ui/src/controllers/editor/editing.render-controller.ts @@ -16,7 +16,7 @@ /* eslint-disable max-lines-per-function */ -import type { DocumentDataModel, ICellData, ICommandInfo, IDisposable, IDocumentBody, IDocumentData, IStyleData, Nullable, Styles, UnitModel, Workbook } from '@univerjs/core'; +import type { DocumentDataModel, ICellData, ICommandInfo, IDisposable, IDocumentBody, IDocumentData, IDocumentStyle, IStyleData, Nullable, Styles, UnitModel, Workbook } from '@univerjs/core'; import type { IRichTextEditingMutationParams } from '@univerjs/docs'; import type { IRenderContext, IRenderModule } from '@univerjs/engine-render'; import type { WorkbookSelectionModel } from '@univerjs/sheets'; @@ -252,9 +252,7 @@ export class EditingRenderController extends Disposable implements IRenderModule const { position, documentLayoutObject, scaleX, editorUnitId } = state; - if ( - this._contextService.getContextValue(FOCUSING_EDITOR_STANDALONE) - ) { + if (this._contextService.getContextValue(FOCUSING_EDITOR_STANDALONE)) { return; } @@ -301,9 +299,9 @@ export class EditingRenderController extends Disposable implements IRenderModule })); } - /** - * Listen to document edits to refresh the size of the sheet editor, not for normal editor. - */ + /** + * Listen to document edits to refresh the size of the sheet editor, not for normal editor. + */ private _commandExecutedListener(d: DisposableCollection) { d.add(this._commandService.onCommandExecuted((command: ICommandInfo) => { if (command.id === RichTextEditingMutation.id) { @@ -311,10 +309,7 @@ export class EditingRenderController extends Disposable implements IRenderModule const { unitId: commandUnitId } = params; // Only when the sheet it attached to is focused. Maybe we should change it to the render unit sys. - if ( - !this._isCurrentSheetFocused() || - isRangeSelector(commandUnitId) - ) { + if (!this._isCurrentSheetFocused() || isRangeSelector(commandUnitId)) { return; } @@ -389,7 +384,6 @@ export class EditingRenderController extends Disposable implements IRenderModule documentLayoutObject, editorUnitId, unitId, - sheetId, isInArrayFormulaRange = false, } = editCellState; const editorObject = this._getEditorObject(); @@ -420,7 +414,7 @@ export class EditingRenderController extends Disposable implements IRenderModule eventType === DeviceInputEventType.Keyboard || (eventType === DeviceInputEventType.Dblclick && isInArrayFormulaRange) ) { - this._emptyDocumentDataModel(!!isInArrayFormulaRange); + this._emptyDocumentDataModel(documentDataModel.getSnapshot().documentStyle, !!isInArrayFormulaRange); document.makeDirty(); // @JOCS, Why calculate here? @@ -686,8 +680,8 @@ export class EditingRenderController extends Disposable implements IRenderModule return this._renderManagerService.getRenderById(editorId)?.with(DocSkeletonManagerService).getViewModel(); } - private _emptyDocumentDataModel(removeStyle: boolean) { - const empty = (documentDataModel: DocumentDataModel) => { + private _emptyDocumentDataModel(documentStyle: IDocumentStyle, removeStyle: boolean) { + const empty = (documentDataModel: DocumentDataModel, resetDocumentStyle?: boolean) => { const snapshot = Tools.deepClone(documentDataModel.getSnapshot()); const documentViewModel = this._getEditorViewModel(documentDataModel.getUnitId()); if (documentViewModel == null) { @@ -695,6 +689,9 @@ export class EditingRenderController extends Disposable implements IRenderModule } emptyBody(snapshot.body!, removeStyle); + if (resetDocumentStyle) { + snapshot.documentStyle = documentStyle; + } snapshot.drawings = {}; snapshot.drawingsOrder = []; documentDataModel.reset(snapshot); @@ -702,12 +699,14 @@ export class EditingRenderController extends Disposable implements IRenderModule }; const documentDataModel = this._univerInstanceService.getUnit(DOCS_NORMAL_EDITOR_UNIT_ID_KEY, UniverInstanceType.UNIVER_DOC); - documentDataModel && empty(documentDataModel); + documentDataModel && empty(documentDataModel, true); + const formulaDocument = this._univerInstanceService.getUnit(DOCS_FORMULA_BAR_EDITOR_UNIT_ID_KEY, UniverInstanceType.UNIVER_DOC); formulaDocument && empty(formulaDocument); } } +// eslint-disable-next-line complexity export function getCellDataByInput( cellData: ICellData, documentDataModel: Nullable, diff --git a/packages/sheets-ui/src/views/editor-container/EditorContainer.tsx b/packages/sheets-ui/src/views/editor-container/EditorContainer.tsx index 288dfa77c78..813f9270a5c 100644 --- a/packages/sheets-ui/src/views/editor-container/EditorContainer.tsx +++ b/packages/sheets-ui/src/views/editor-container/EditorContainer.tsx @@ -152,7 +152,6 @@ export const EditorContainer: React.FC = () => { {}} isFocus={visible?.visible} diff --git a/packages/sheets-ui/src/views/formula-bar/FormulaBar.tsx b/packages/sheets-ui/src/views/formula-bar/FormulaBar.tsx index b14939d28cb..260f1faffe0 100644 --- a/packages/sheets-ui/src/views/formula-bar/FormulaBar.tsx +++ b/packages/sheets-ui/src/views/formula-bar/FormulaBar.tsx @@ -221,7 +221,6 @@ export function FormulaBar() { {FormulaEditor && ( {}} isFocus={isFocusFxBar} From 7042677a00c8e695c15140464f42917a16f00933 Mon Sep 17 00:00:00 2001 From: zhangw Date: Fri, 20 Dec 2024 01:35:03 -0500 Subject: [PATCH 063/134] feat: update --- .../editor/data-sync.controller.ts | 29 ++++++++++++++++--- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/packages/sheets-ui/src/controllers/editor/data-sync.controller.ts b/packages/sheets-ui/src/controllers/editor/data-sync.controller.ts index a5e05bff6e8..6d56059ed70 100644 --- a/packages/sheets-ui/src/controllers/editor/data-sync.controller.ts +++ b/packages/sheets-ui/src/controllers/editor/data-sync.controller.ts @@ -14,12 +14,12 @@ * limitations under the License. */ -import type { DocumentDataModel, ICommandInfo, IDocumentBody, IDrawings, IParagraph, Nullable } from '@univerjs/core'; +import type { DocumentDataModel, ICommandInfo, IDocumentBody, IDocumentStyle, IDrawings, IParagraph, Nullable } from '@univerjs/core'; import type { IRichTextEditingMutationParams } from '@univerjs/docs'; import type { DocumentViewModel } from '@univerjs/engine-render'; import type { IMoveRangeMutationParams, ISetRangeValuesMutationParams } from '@univerjs/sheets'; import type { ICellEditorState } from '../../services/editor-bridge.service'; -import { BooleanNumber, Disposable, DOCS_FORMULA_BAR_EDITOR_UNIT_ID_KEY, DOCS_NORMAL_EDITOR_UNIT_ID_KEY, HorizontalAlign, ICommandService, Inject, IUniverInstanceService, Tools, UniverInstanceType } from '@univerjs/core'; +import { BooleanNumber, Disposable, DOCS_FORMULA_BAR_EDITOR_UNIT_ID_KEY, DOCS_NORMAL_EDITOR_UNIT_ID_KEY, DocumentFlavor, HorizontalAlign, ICommandService, Inject, IUniverInstanceService, Tools, UniverInstanceType, VerticalAlign, WrapStrategy } from '@univerjs/core'; import { DocSkeletonManagerService, RichTextEditingMutation } from '@univerjs/docs'; import { ReplaceSnapshotCommand } from '@univerjs/docs-ui'; import { DeviceInputEventType, IRenderManagerService } from '@univerjs/engine-render'; @@ -27,6 +27,27 @@ import { MoveRangeMutation, RangeProtectionRuleModel, SetRangeValuesMutation, Wo import { IEditorBridgeService } from '../../services/editor-bridge.service'; import { FormulaEditorController } from './formula-editor.controller'; +const formulaEditorStyle: IDocumentStyle = { + pageSize: { + width: Number.POSITIVE_INFINITY, + height: Number.POSITIVE_INFINITY, + }, + documentFlavor: DocumentFlavor.UNSPECIFIED, + marginTop: 5, + marginBottom: 5, + marginRight: 0, + marginLeft: 0, + paragraphLineGapDefault: 0, + renderConfig: { + horizontalAlign: HorizontalAlign.UNSPECIFIED, + verticalAlign: VerticalAlign.TOP, + centerAngle: 0, + vertexAngle: 0, + wrapStrategy: WrapStrategy.WRAP, + isRenderStyle: BooleanNumber.FALSE, + }, +}; + /** * sync data between cell editor and formula editor */ @@ -177,7 +198,7 @@ export class EditorDataSyncController extends Disposable { } const skeleton = currentRender.with(DocSkeletonManagerService).getSkeleton(); - const docDataModel = this._univerInstanceService.getUniverDocInstance(unitId); + const docDataModel = this._univerInstanceService.getUnit(unitId, UniverInstanceType.UNIVER_DOC); const docViewModel = this._getEditorViewModel(unitId); if (docDataModel == null || docViewModel == null) { @@ -226,7 +247,6 @@ export class EditorDataSyncController extends Disposable { docDataModel.getSnapshot().drawingsOrder = drawingsOrder ?? []; this._checkAndSetRenderStyleConfig(docDataModel); - docViewModel.reset(docDataModel); const currentRender = this._renderManagerService.getRenderById(unitId); if (currentRender == null) { @@ -252,6 +272,7 @@ export class EditorDataSyncController extends Disposable { return; } + snapshot.documentStyle = formulaEditorStyle; let renderConfig = snapshot.documentStyle.renderConfig; if (renderConfig == null) { From 6d959928740682687a18696a16ae77db2b0aaaad Mon Sep 17 00:00:00 2001 From: zhangw Date: Fri, 20 Dec 2024 01:43:19 -0500 Subject: [PATCH 064/134] feat: update --- .../sheets-zen-editor/src/views/zen-editor/ZenEditor.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) 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 7f7bada4fa5..12b856a844c 100644 --- a/packages/sheets-zen-editor/src/views/zen-editor/ZenEditor.tsx +++ b/packages/sheets-zen-editor/src/views/zen-editor/ZenEditor.tsx @@ -84,7 +84,6 @@ export function ZenEditor() { editorUnitId: DOCS_ZEN_EDITOR_UNIT_ID_KEY, initialSnapshot: INITIAL_SNAPSHOT, scrollBar: true, - noNeedVerticalAlign: true, backScrollOffset: 100, }, editorDom); @@ -104,10 +103,14 @@ export function ZenEditor() { }, []); // Empty dependency array means this effect runs once on mount and clean up on unmount function handleCloseBtnClick() { + const editor = editorService.getEditor(DOCS_ZEN_EDITOR_UNIT_ID_KEY); + editor?.blur(); commandService.executeCommand(CancelZenEditCommand.id); } function handleConfirmBtnClick() { + const editor = editorService.getEditor(DOCS_ZEN_EDITOR_UNIT_ID_KEY); + editor?.blur(); commandService.executeCommand(ConfirmZenEditCommand.id); } From d3ea7aa166b0f8f88f60239fe8e4b8c36bd3ef6a Mon Sep 17 00:00:00 2001 From: zhangw Date: Fri, 20 Dec 2024 02:17:26 -0500 Subject: [PATCH 065/134] feat: update --- .../hooks/useSheetSelectionChange.ts | 14 ++++++++++---- .../src/views/formula-editor/index.tsx | 8 +++++--- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/packages/sheets-formula-ui/src/views/formula-editor/hooks/useSheetSelectionChange.ts b/packages/sheets-formula-ui/src/views/formula-editor/hooks/useSheetSelectionChange.ts index 99737a44884..bf19774b93b 100644 --- a/packages/sheets-formula-ui/src/views/formula-editor/hooks/useSheetSelectionChange.ts +++ b/packages/sheets-formula-ui/src/views/formula-editor/hooks/useSheetSelectionChange.ts @@ -24,7 +24,7 @@ import type { INode } from '../../range-selector/utils/filterReferenceNode'; import { DisposableCollection, ICommandService, IUniverInstanceService, useDependency, useObservable } from '@univerjs/core'; import { deserializeRangeWithSheet, sequenceNodeType, serializeRange, serializeRangeWithSheet } from '@univerjs/engine-formula'; import { IRenderManagerService } from '@univerjs/engine-render'; -import { SetSelectionsOperation } from '@univerjs/sheets'; +import { IRefSelectionsService, SetSelectionsOperation } from '@univerjs/sheets'; import { useEffect, useMemo, useRef } from 'react'; import { merge } from 'rxjs'; import { debounceTime, distinctUntilChanged, map } from 'rxjs/operators'; @@ -45,7 +45,7 @@ export const useSheetSelectionChange = ( isSupportAcrossSheet: boolean, listenSelectionSet: boolean, editor?: Editor, - handleRangeChange: ((refString: string, offset: number, isEnd: boolean) => void) = noop + handleRangeChange: ((refString: string, offset: number, isEnd: boolean, isModify?: boolean) => void) = noop ) => { const renderManagerService = useDependency(IRenderManagerService); const univerInstanceService = useDependency(IUniverInstanceService); @@ -62,6 +62,7 @@ export const useSheetSelectionChange = ( const render = renderManagerService.getRenderById(unitId); const refSelectionsRenderService = render?.with(RefSelectionsRenderService); + const refSelectionsService = useDependency(IRefSelectionsService); const isScalingRef = useRef(false); @@ -259,14 +260,19 @@ export const useSheetSelectionChange = ( map((e) => { return serializeRange(e); }), - distinctUntilChanged() + distinctUntilChanged(), + debounceTime(100) ).subscribe((rangeText) => { isScalingRef.current = true; handleSequenceNodeReplace(rangeText, index); })); }); }; - const dispose = merge(editor.input$, refSelectionsRenderService.selectionMoveEnd$).pipe(debounceTime(50)).subscribe(() => { + const dispose = merge( + editor.input$, + refSelectionsService.selectionSet$, + refSelectionsRenderService.selectionMoveEnd$).pipe(debounceTime(50) + ).subscribe(() => { reListen(); }); diff --git a/packages/sheets-formula-ui/src/views/formula-editor/index.tsx b/packages/sheets-formula-ui/src/views/formula-editor/index.tsx index 4962759d8fd..651bae59d44 100644 --- a/packages/sheets-formula-ui/src/views/formula-editor/index.tsx +++ b/packages/sheets-formula-ui/src/views/formula-editor/index.tsx @@ -146,7 +146,7 @@ export function FormulaEditor(props: IFormulaEditorProps) { const highlightDoc = useDocHight('='); const highlightSheet = useSheetHighlight(unitId); - const highligh = useEvent((text: string, isNeedResetSelection: boolean = true) => { + const highligh = useEvent((text: string, isNeedResetSelection: boolean = true, isEnd?: boolean) => { if (!editor) { return; } @@ -160,7 +160,9 @@ export function FormulaEditor(props: IFormulaEditorProps) { // remove equals need to remove highlight style preText.slice(1) === text && preText[0] === '=' ); - highlightSheet(isFocus ? ranges : []); + if (isEnd) { + highlightSheet(isFocus ? ranges : []); + } }); useEffect(() => { @@ -199,7 +201,7 @@ export function FormulaEditor(props: IFormulaEditorProps) { return; } needEmit(); - highligh(`=${refString}`); + highligh(`=${refString}`, true, isEnd); if (isEnd) { focus(); if (offset !== -1) { From bed489726ae614e328cd0472f64fea8c0ac15fa0 Mon Sep 17 00:00:00 2001 From: zhangw Date: Fri, 20 Dec 2024 03:22:07 -0500 Subject: [PATCH 066/134] feat: update --- .../__tests__/end-edit.controller.spec.ts | 3 +- .../editor/editing.render-controller.ts | 56 ++++++++----------- 2 files changed, 23 insertions(+), 36 deletions(-) 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 1dd5e06d5ff..5640feff496 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 @@ -129,9 +129,8 @@ describe('Test EndEditController', () => { return getCellDataByInput( cell, - documentLayoutObject.documentModel, + documentLayoutObject.documentModel?.getSnapshot(), lexerTreeBuilder, - (model) => model.getSnapshot(), localeService, get(IMockFunctionService) as IFunctionService, workbook.getStyles() 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 e896d001b65..10275d43a0b 100644 --- a/packages/sheets-ui/src/controllers/editor/editing.render-controller.ts +++ b/packages/sheets-ui/src/controllers/editor/editing.render-controller.ts @@ -449,6 +449,8 @@ export class EditingRenderController extends Disposable implements IRenderModule private async _handleEditorInvisible(param: IEditorBridgeServiceVisibleParam) { const editCellState = this._editorBridgeService.getEditCellState(); + const documentDataModel = this._univerInstanceService.getUnit(DOCS_NORMAL_EDITOR_UNIT_ID_KEY); + const snapshot = Tools.deepClone(documentDataModel?.getSnapshot()); let { keycode } = param; this._cursorChange = CursorChange.InitialState; @@ -470,7 +472,19 @@ export class EditingRenderController extends Disposable implements IRenderModule const workbookId = this._context.unitId; const worksheetId = worksheet.getSheetId(); - // Reselect the current selections, when exist cell editor by press ESC.I + const { unitId, sheetId } = editCellState; + /** + * When closing the editor, switch to the current tab of the editor. + */ + if (workbookId === unitId && sheetId !== worksheetId) { + // SetWorksheetActivateCommand handler uses Promise + await this._commandService.executeCommand(SetWorksheetActivateCommand.id, { + subUnitId: sheetId, + unitId, + }); + } + + // Reselect the current selections, when exist cell editor by press ESC.I if (keycode === KeyCode.ESC) { if (this._editorBridgeService.isForceKeepVisible()) { this._editorBridgeService.disableForceKeepVisible(); @@ -487,23 +501,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. - */ - if (workbookId === unitId && sheetId !== worksheetId && this._editorBridgeService.isForceKeepVisible()) { - // SetWorksheetActivateCommand handler uses Promise - await this._commandService.executeCommand(SetWorksheetActivateCommand.id, { - subUnitId: sheetId, - unitId, - }); - } - - const documentDataModel = this._univerInstanceService.getUnit(DOCS_NORMAL_EDITOR_UNIT_ID_KEY); - - if (documentDataModel) { - await this._submitCellData(documentDataModel); + if (snapshot) { + await this._submitCellData(snapshot); } // moveCursor need to put behind of SetRangeValuesCommand, fix https://github.com/dream-num/univer/issues/1155 @@ -515,10 +514,10 @@ export class EditingRenderController extends Disposable implements IRenderModule } submitCellData(documentDataModel: DocumentDataModel) { - return this._submitCellData(documentDataModel); + return this._submitCellData(documentDataModel.getSnapshot()); } - private async _submitCellData(documentDataModel: DocumentDataModel) { + private async _submitCellData(snapshot: IDocumentData) { const editCellState = this._editorBridgeService.getEditCellState(); if (editCellState == null) { return; @@ -541,9 +540,8 @@ export class EditingRenderController extends Disposable implements IRenderModule // This should moved to after cell editor const cellData: Nullable = getCellDataByInput( worksheet.getCellRaw(row, column) || {}, - documentDataModel, + snapshot, this._lexerTreeBuilder, - (model) => model.getSnapshot(), this._localService, this._functionService, workbook.getStyles() @@ -709,26 +707,16 @@ export class EditingRenderController extends Disposable implements IRenderModule // eslint-disable-next-line complexity export function getCellDataByInput( cellData: ICellData, - documentDataModel: Nullable, + snapshot: Nullable, lexerTreeBuilder: LexerTreeBuilder, - getSnapshot: (data: DocumentDataModel) => IDocumentData, localeService: LocaleService, functionService: IFunctionService, styles: Styles ) { - cellData = Tools.deepClone(cellData); - - if (documentDataModel == null) { + if (snapshot?.body == null) { return null; } - - const snapshot = getSnapshot(documentDataModel); - const { body } = snapshot; - if (body == null) { - return null; - } - cellData.t = undefined; const data = body.dataStream; From 74938922759c125d210665f893c47ec50aff30b5 Mon Sep 17 00:00:00 2001 From: zhangw Date: Fri, 20 Dec 2024 05:48:41 -0500 Subject: [PATCH 067/134] feat: update --- .../doc-editor-bridge.controller.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) 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 dd6a197a805..8c6d45a9526 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 @@ -189,16 +189,16 @@ export class DocEditorBridgeController extends Disposable implements IRenderModu } private _initialFocus() { - this.disposeWithMe( - this._editorService.focus$.subscribe((textRange) => { - if (this._editorService.getFocusEditor()?.getEditorId() !== this._context.unitId) { - return; - } + // this.disposeWithMe( + // this._editorService.focus$.subscribe((textRange) => { + // if (this._editorService.getFocusEditor()?.getEditorId() !== this._context.unitId) { + // return; + // } - this._docSelectionRenderService.removeAllRanges(); - this._docSelectionRenderService.addDocRanges([textRange]); - }) - ); + // this._docSelectionRenderService.removeAllRanges(); + // this._docSelectionRenderService.addDocRanges([textRange]); + // }) + // ); const focusExcepts = [ 'univer-formula-search', From f5e4a5e128e2265bcae9251604293b7cd784d78d Mon Sep 17 00:00:00 2001 From: zhangw Date: Fri, 20 Dec 2024 06:31:52 -0500 Subject: [PATCH 068/134] feat: cursor --- .../doc-editor-bridge.controller.ts | 16 +++++++------- .../docs-ui/src/services/editor/editor.ts | 22 +++++++++---------- 2 files changed, 19 insertions(+), 19 deletions(-) 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 8c6d45a9526..08a485006ea 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 @@ -190,14 +190,14 @@ export class DocEditorBridgeController extends Disposable implements IRenderModu private _initialFocus() { // this.disposeWithMe( - // this._editorService.focus$.subscribe((textRange) => { - // if (this._editorService.getFocusEditor()?.getEditorId() !== this._context.unitId) { - // return; - // } - - // this._docSelectionRenderService.removeAllRanges(); - // this._docSelectionRenderService.addDocRanges([textRange]); - // }) + // this._editorService.focus$.subscribe((textRange) => { + // if (this._editorService.getFocusEditor()?.getEditorId() !== this._context.unitId) { + // return; + // } + + // this._docSelectionRenderService.removeAllRanges(); + // this._docSelectionRenderService.addDocRanges([textRange]); + // }) // ); const focusExcepts = [ diff --git a/packages/docs-ui/src/services/editor/editor.ts b/packages/docs-ui/src/services/editor/editor.ts index aa0de3861ba..f9ca26c02fc 100644 --- a/packages/docs-ui/src/services/editor/editor.ts +++ b/packages/docs-ui/src/services/editor/editor.ts @@ -237,17 +237,17 @@ export class Editor extends Disposable implements IEditor { docSelectionRenderService.focus(); // Step 3: Sets the selection of the last selection, and if not, to the beginning of the document. - const lastSelectionInfo = this._docSelectionManagerService.getDocRanges({ - unitId: editorUnitId, - subUnitId: editorUnitId, - }); - - if (lastSelectionInfo) { - this._docSelectionManagerService.replaceDocRanges(lastSelectionInfo, { - unitId: editorUnitId, - subUnitId: editorUnitId, - }, false); - } + // const lastSelectionInfo = this._docSelectionManagerService.getDocRanges({ + // unitId: editorUnitId, + // subUnitId: editorUnitId, + // }); + + // if (lastSelectionInfo) { + // this._docSelectionManagerService.replaceDocRanges(lastSelectionInfo, { + // unitId: editorUnitId, + // subUnitId: editorUnitId, + // }, false); + // } this._focus = true; } From 3e154a053f7b150e3a52cb78e6d682bac2036c47 Mon Sep 17 00:00:00 2001 From: zhangw Date: Fri, 20 Dec 2024 07:07:10 -0500 Subject: [PATCH 069/134] feat: update --- .../src/controllers/editor/editing.render-controller.ts | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) 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 10275d43a0b..b81599bccf4 100644 --- a/packages/sheets-ui/src/controllers/editor/editing.render-controller.ts +++ b/packages/sheets-ui/src/controllers/editor/editing.render-controller.ts @@ -256,17 +256,14 @@ export class EditingRenderController extends Disposable implements IRenderModule return; } - if (this._instanceSrv.getUnit(DOCS_NORMAL_EDITOR_UNIT_ID_KEY) === documentLayoutObject.documentModel) { - return; - } - + const cellDocument = this._instanceSrv.getUnit(DOCS_NORMAL_EDITOR_UNIT_ID_KEY, UniverInstanceType.UNIVER_DOC); + if (cellDocument == null) return; const { startX, endX } = position; const { textRotation, wrapStrategy, documentModel } = documentLayoutObject; const { vertexAngle: angle } = convertTextRotation(textRotation); - documentModel!.updateDocumentId(editorUnitId); if (wrapStrategy === WrapStrategy.WRAP && angle === 0) { - documentModel!.updateDocumentDataPageSize((endX - startX) / scaleX); + cellDocument.updateDocumentDataPageSize((endX - startX) / scaleX); } this._commandService.syncExecuteCommand(ReplaceSnapshotCommand.id, { From cfc9c82ae480281a0a52912d8cdd8b72c65e8aed Mon Sep 17 00:00:00 2001 From: zhangw Date: Mon, 23 Dec 2024 01:25:55 -0500 Subject: [PATCH 070/134] feat: update --- .../src/commands/commands/replace-content.command.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) 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 ce3dce51510..97318b5a5dc 100644 --- a/packages/docs-ui/src/commands/commands/replace-content.command.ts +++ b/packages/docs-ui/src/commands/commands/replace-content.command.ts @@ -46,7 +46,7 @@ export const ReplaceSnapshotCommand: ICommand = { return false; } - const { body, tableSource, footers, headers, lists, drawings, drawingsOrder } = snapshot; + const { body, tableSource, footers, headers, lists, drawings, drawingsOrder, documentStyle } = snapshot; const { body: prevBody, tableSource: prevTableSource, @@ -55,6 +55,7 @@ export const ReplaceSnapshotCommand: ICommand = { lists: prevLists, drawings: prevDrawings, drawingsOrder: prevDrawingsOrder, + documentStyle: prevDocumentStyle, } = prevSnapshot; if (body == null || prevBody == null) { @@ -88,6 +89,13 @@ export const ReplaceSnapshotCommand: ICommand = { const jsonX = JSONX.getInstance(); + if (!Tools.diffValue(prevDocumentStyle, documentStyle)) { + const actions = jsonX.replaceOp(['documentStyle'], prevDocumentStyle, documentStyle); + if (actions != null) { + rawActions.push(actions); + } + } + if (!Tools.diffValue(body, prevBody)) { const actions = jsonX.replaceOp(['body'], prevBody, body); if (actions != null) { From 47aba48a31f487f2ae51384262196e71b853b0f8 Mon Sep 17 00:00:00 2001 From: zhangw Date: Mon, 23 Dec 2024 01:59:58 -0500 Subject: [PATCH 071/134] feat: update --- .../src/controllers/editor/editing.render-controller.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 b81599bccf4..b6b8c134908 100644 --- a/packages/sheets-ui/src/controllers/editor/editing.render-controller.ts +++ b/packages/sheets-ui/src/controllers/editor/editing.render-controller.ts @@ -536,7 +536,7 @@ export class EditingRenderController extends Disposable implements IRenderModule // If cross-sheet operation, switch current sheet first, then const cellData // This should moved to after cell editor const cellData: Nullable = getCellDataByInput( - worksheet.getCellRaw(row, column) || {}, + Tools.deepClone(worksheet.getCellRaw(row, column) || {}), snapshot, this._lexerTreeBuilder, this._localService, From 7c6cfba8cc038514e46f6402dbf58ae3aee601e8 Mon Sep 17 00:00:00 2001 From: zhangw Date: Mon, 23 Dec 2024 02:00:33 -0500 Subject: [PATCH 072/134] feat: update --- .../src/controllers/editor/editing.render-controller.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 b6b8c134908..afd38196a7d 100644 --- a/packages/sheets-ui/src/controllers/editor/editing.render-controller.ts +++ b/packages/sheets-ui/src/controllers/editor/editing.render-controller.ts @@ -536,7 +536,7 @@ export class EditingRenderController extends Disposable implements IRenderModule // If cross-sheet operation, switch current sheet first, then const cellData // This should moved to after cell editor const cellData: Nullable = getCellDataByInput( - Tools.deepClone(worksheet.getCellRaw(row, column) || {}), + { ...(worksheet.getCellRaw(row, column) || {}) }, snapshot, this._lexerTreeBuilder, this._localService, From dcaa1228f88d8ece21a80a5db68b5c43fb6b5294 Mon Sep 17 00:00:00 2001 From: zhangw Date: Mon, 23 Dec 2024 03:47:35 -0500 Subject: [PATCH 073/134] feat: update --- .../editor/cell-editor-resize.service.ts | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/packages/sheets-ui/src/services/editor/cell-editor-resize.service.ts b/packages/sheets-ui/src/services/editor/cell-editor-resize.service.ts index 0b4e5f34054..a409e94f359 100644 --- a/packages/sheets-ui/src/services/editor/cell-editor-resize.service.ts +++ b/packages/sheets-ui/src/services/editor/cell-editor-resize.service.ts @@ -72,8 +72,8 @@ export class SheetCellEditorResizeService extends Disposable implements IRenderM scaleX, scaleY ); - const { verticalAlign, paddingData, fill } = documentLayoutObject; + const { verticalAlign, horizontalAlign, paddingData, fill } = documentLayoutObject; let editorWidth = endX - startX; let editorHeight = endY - startY; @@ -97,17 +97,25 @@ export class SheetCellEditorResizeService extends Disposable implements IRenderM offsetTop = (editorHeight - actualHeight) / scaleY - (paddingData.b || 0); } + let offsetLeft = 0; + if (horizontalAlign === HorizontalAlign.CENTER) { + offsetLeft = (editorWidth - actualWidth) / 2 / scaleX; + } else if (horizontalAlign === HorizontalAlign.RIGHT) { + offsetLeft = (editorWidth - actualWidth) / scaleX - (paddingData.r || 0); + } else { + offsetLeft = paddingData.l || 0; + } // offsetTop /= scaleY; offsetTop = offsetTop < (paddingData.t || 0) ? paddingData.t || 0 : offsetTop; - + offsetLeft = offsetLeft < (paddingData.l || 0) ? paddingData.l || 0 : offsetLeft; documentDataModel.updateDocumentDataMargin({ t: offsetTop, + l: offsetLeft, }); } // re-calculate skeleton(viewModel for component) documentSkeleton.calculate(); - editorWidth -= 1; editorHeight -= 1; this._editAreaProcessing(editorWidth, editorHeight, position, canvasOffset, fill, scaleX, scaleY, callback); @@ -168,7 +176,7 @@ export class SheetCellEditorResizeService extends Disposable implements IRenderM }); return { - actualWidth: editorWidth, + actualWidth: size.actualWidth * scaleX, actualHeight: size.actualHeight * scaleY, }; } From 0debcbaa959f7bf2b78f7b1c3b331236e86bae61 Mon Sep 17 00:00:00 2001 From: zhangw Date: Mon, 23 Dec 2024 05:25:19 -0500 Subject: [PATCH 074/134] feat: update --- .../editor/cell-editor-resize.service.ts | 69 ++++++++++--------- 1 file changed, 35 insertions(+), 34 deletions(-) diff --git a/packages/sheets-ui/src/services/editor/cell-editor-resize.service.ts b/packages/sheets-ui/src/services/editor/cell-editor-resize.service.ts index a409e94f359..f7ba2a3dbe1 100644 --- a/packages/sheets-ui/src/services/editor/cell-editor-resize.service.ts +++ b/packages/sheets-ui/src/services/editor/cell-editor-resize.service.ts @@ -49,6 +49,7 @@ export class SheetCellEditorResizeService extends Disposable implements IRenderM super(); } + // eslint-disable-next-line complexity fitTextSize(callback?: () => void) { const param = this._editorBridgeService.getEditCellState(); if (!param) return; @@ -64,7 +65,7 @@ export class SheetCellEditorResizeService extends Disposable implements IRenderM const documentSkeleton = this._getEditorSkeleton(); if (!documentSkeleton) return; - const { actualWidth, actualHeight } = this._predictingSize( + let { actualWidth, actualHeight } = this._predictingSize( position, canvasOffset, documentSkeleton, @@ -74,50 +75,50 @@ export class SheetCellEditorResizeService extends Disposable implements IRenderM ); const { verticalAlign, horizontalAlign, paddingData, fill } = documentLayoutObject; + actualWidth = actualWidth + (paddingData.l ?? 0) + (paddingData.r ?? 0); + actualHeight = actualHeight + (paddingData.t ?? 0) + (paddingData.b ?? 0); + let editorWidth = endX - startX; let editorHeight = endY - startY; - if (editorWidth < actualWidth) { - editorWidth = actualWidth; + editorWidth = Math.ceil(actualWidth); } if (editorHeight < actualHeight) { - editorHeight = actualHeight; - // To restore the page margins for the skeleton. - documentDataModel.updateDocumentDataMargin(paddingData); - } else { - // Set the top margin under vertical alignment. - let offsetTop = 0; - - if (verticalAlign === VerticalAlign.MIDDLE) { - offsetTop = (editorHeight - actualHeight) / 2 / scaleY; - } else if (verticalAlign === VerticalAlign.TOP) { - offsetTop = paddingData.t || 0; - } else { // VerticalAlign.UNSPECIFIED follow the same rule as HorizontalAlign.BOTTOM. - offsetTop = (editorHeight - actualHeight) / scaleY - (paddingData.b || 0); - } + editorHeight = Math.ceil(actualHeight); + } - let offsetLeft = 0; - if (horizontalAlign === HorizontalAlign.CENTER) { - offsetLeft = (editorWidth - actualWidth) / 2 / scaleX; - } else if (horizontalAlign === HorizontalAlign.RIGHT) { - offsetLeft = (editorWidth - actualWidth) / scaleX - (paddingData.r || 0); - } else { - offsetLeft = paddingData.l || 0; - } - // offsetTop /= scaleY; - offsetTop = offsetTop < (paddingData.t || 0) ? paddingData.t || 0 : offsetTop; - offsetLeft = offsetLeft < (paddingData.l || 0) ? paddingData.l || 0 : offsetLeft; - documentDataModel.updateDocumentDataMargin({ - t: offsetTop, - l: offsetLeft, - }); + // Set the top margin under vertical alignment. + let offsetTop = 0; + + if (verticalAlign === VerticalAlign.MIDDLE) { + offsetTop = (editorHeight - actualHeight) / 2 / scaleY; + } else if (verticalAlign === VerticalAlign.TOP) { + offsetTop = paddingData.t || 0; + } else { // VerticalAlign.UNSPECIFIED follow the same rule as HorizontalAlign.BOTTOM. + offsetTop = (editorHeight - actualHeight) / scaleY - (paddingData.b || 0); + } + + let offsetLeft = 0; + if (horizontalAlign === HorizontalAlign.CENTER) { + offsetLeft = (editorWidth - actualWidth) / 2 / scaleX; + } else if (horizontalAlign === HorizontalAlign.RIGHT) { + offsetLeft = (editorWidth - actualWidth) / scaleX - (paddingData.r || 0); + } else { + offsetLeft = paddingData.l || 0; } + // offsetTop /= scaleY; + offsetTop = offsetTop < (paddingData.t || 0) ? paddingData.t || 0 : offsetTop; + offsetLeft = offsetLeft < (paddingData.l || 0) ? paddingData.l || 0 : offsetLeft; + documentDataModel.updateDocumentDataMargin({ + t: offsetTop, + l: offsetLeft, + }); // re-calculate skeleton(viewModel for component) documentSkeleton.calculate(); - editorWidth -= 1; - editorHeight -= 1; + // editorWidth -= 1; + // editorHeight -= 1; this._editAreaProcessing(editorWidth, editorHeight, position, canvasOffset, fill, scaleX, scaleY, callback); } From 7ad2f0c4264ef511c120e09055e94607b5aeaf5a Mon Sep 17 00:00:00 2001 From: zhangw Date: Mon, 23 Dec 2024 07:37:27 -0500 Subject: [PATCH 075/134] feat: update --- .../src/views/formula-editor/index.tsx | 4 +- .../views/range-selector/hooks/useResize.ts | 13 +++-- .../editor/cell-editor-resize.service.ts | 51 ++++++++++++++----- .../editor-container/EditorContainer.tsx | 1 + .../src/views/formula-bar/FormulaBar.tsx | 1 + 5 files changed, 52 insertions(+), 18 deletions(-) diff --git a/packages/sheets-formula-ui/src/views/formula-editor/index.tsx b/packages/sheets-formula-ui/src/views/formula-editor/index.tsx index 651bae59d44..6bd09ca619e 100644 --- a/packages/sheets-formula-ui/src/views/formula-editor/index.tsx +++ b/packages/sheets-formula-ui/src/views/formula-editor/index.tsx @@ -68,6 +68,7 @@ export interface IFormulaEditorProps { keyboradEventConfig?: IKeyboardEventConfig; onMoveInEditor?: (keyCode: KeyCode, metaKey?: MetaKeys) => void; resetSelectionOnBlur?: boolean; + autoScrollbar?: boolean; } const noop = () => { }; @@ -91,6 +92,7 @@ export function FormulaEditor(props: IFormulaEditorProps) { keyboradEventConfig, onMoveInEditor, resetSelectionOnBlur = true, + autoScrollbar = true, } = props; const editorService = useDependency(IEditorService); @@ -192,7 +194,7 @@ export function FormulaEditor(props: IFormulaEditorProps) { } }, [_isFocus, editor, focus, resetSelection, resetSelectionOnBlur]); - const { checkScrollBar } = useResize(editor); + const { checkScrollBar } = useResize(editor, autoScrollbar); useRefactorEffect(isFocus, Boolean(isSelecting && docFocusing), unitId); useLeftAndRightArrow(isFocus && moveCursor, shouldMoveRefSelection, editor, onMoveInEditor); diff --git a/packages/sheets-formula-ui/src/views/range-selector/hooks/useResize.ts b/packages/sheets-formula-ui/src/views/range-selector/hooks/useResize.ts index 25afa03b274..c542676eafb 100644 --- a/packages/sheets-formula-ui/src/views/range-selector/hooks/useResize.ts +++ b/packages/sheets-formula-ui/src/views/range-selector/hooks/useResize.ts @@ -21,7 +21,8 @@ import { type Editor, VIEWPORT_KEY } from '@univerjs/docs-ui'; import { ScrollBar } from '@univerjs/engine-render'; import { useEffect, useMemo } from 'react'; -export const useResize = (editor?: Editor) => { +// eslint-disable-next-line max-lines-per-function +export const useResize = (editor?: Editor, autoScrollbar?: boolean) => { const resize = () => { if (editor) { const { scene, mainComponent } = editor.render; @@ -40,7 +41,7 @@ export const useResize = (editor?: Editor) => { const checkScrollBar = useMemo(() => { return debounce(() => { - if (!editor) { + if (!editor || !autoScrollbar) { return; } const docSkeletonManagerService = editor.render.with(DocSkeletonManagerService); @@ -73,9 +74,10 @@ export const useResize = (editor?: Editor) => { viewportMain?.getScrollBar()?.dispose(); } }, 30); - }, [editor]); + }, [editor, autoScrollbar]); useEffect(() => { + if (!autoScrollbar) return; if (editor) { const time = setTimeout(() => { resize(); @@ -85,9 +87,10 @@ export const useResize = (editor?: Editor) => { clearTimeout(time); }; } - }, [editor]); + }, [editor, autoScrollbar]); useEffect(() => { + if (!autoScrollbar) return; if (editor) { const d = editor.input$.subscribe(() => { checkScrollBar(); @@ -96,7 +99,7 @@ export const useResize = (editor?: Editor) => { d.unsubscribe(); }; } - }, [editor]); + }, [editor, autoScrollbar]); return { resize, checkScrollBar }; }; diff --git a/packages/sheets-ui/src/services/editor/cell-editor-resize.service.ts b/packages/sheets-ui/src/services/editor/cell-editor-resize.service.ts index f7ba2a3dbe1..255268f62fe 100644 --- a/packages/sheets-ui/src/services/editor/cell-editor-resize.service.ts +++ b/packages/sheets-ui/src/services/editor/cell-editor-resize.service.ts @@ -49,7 +49,7 @@ export class SheetCellEditorResizeService extends Disposable implements IRenderM super(); } - // eslint-disable-next-line complexity + // eslint-disable-next-line complexity, max-lines-per-function fitTextSize(callback?: () => void) { const param = this._editorBridgeService.getEditCellState(); if (!param) return; @@ -96,18 +96,18 @@ export class SheetCellEditorResizeService extends Disposable implements IRenderM } else if (verticalAlign === VerticalAlign.TOP) { offsetTop = paddingData.t || 0; } else { // VerticalAlign.UNSPECIFIED follow the same rule as HorizontalAlign.BOTTOM. - offsetTop = (editorHeight - actualHeight) / scaleY - (paddingData.b || 0); + offsetTop = (editorHeight - actualHeight) / scaleY; } let offsetLeft = 0; if (horizontalAlign === HorizontalAlign.CENTER) { offsetLeft = (editorWidth - actualWidth) / 2 / scaleX; } else if (horizontalAlign === HorizontalAlign.RIGHT) { - offsetLeft = (editorWidth - actualWidth) / scaleX - (paddingData.r || 0); + offsetLeft = (editorWidth - actualWidth) / scaleX; } else { offsetLeft = paddingData.l || 0; } - // offsetTop /= scaleY; + offsetTop = offsetTop < (paddingData.t || 0) ? paddingData.t || 0 : offsetTop; offsetLeft = offsetLeft < (paddingData.l || 0) ? paddingData.l || 0 : offsetLeft; documentDataModel.updateDocumentDataMargin({ @@ -119,7 +119,17 @@ export class SheetCellEditorResizeService extends Disposable implements IRenderM documentSkeleton.calculate(); // editorWidth -= 1; // editorHeight -= 1; - this._editAreaProcessing(editorWidth, editorHeight, position, canvasOffset, fill, scaleX, scaleY, callback); + this._editAreaProcessing( + editorWidth, + editorHeight, + position, + canvasOffset, + fill, + scaleX, + scaleY, + horizontalAlign, + callback + ); } /** @@ -182,7 +192,7 @@ export class SheetCellEditorResizeService extends Disposable implements IRenderM }; } - private _getEditorMaxSize(position: IPosition, canvasOffset: ICanvasOffset) { + private _getEditorMaxSize(position: IPosition, canvasOffset: ICanvasOffset, horizontalAlign: HorizontalAlign) { const editorObject = this._getEditorObject(); if (editorObject == null) { return; @@ -199,8 +209,8 @@ export class SheetCellEditorResizeService extends Disposable implements IRenderM const widthOfCanvas = pxToNum(canvasElement.style.width); // declared width const { width } = canvasClientRect; // real width affected by scale const scaleAdjust = width / widthOfCanvas; - - const { startX, startY } = position; + const { startX, startY, endX } = position; + const enginWidth = this._context.engine.width; const clientHeight = document.body.clientHeight - @@ -209,7 +219,15 @@ export class SheetCellEditorResizeService extends Disposable implements IRenderM canvasOffset.top - EDITOR_BORDER_SIZE * 2; - const clientWidth = document.body.clientWidth - startX - canvasOffset.left; + let clientWidth = width - startX; + + if (horizontalAlign === HorizontalAlign.CENTER) { + const rightGap = enginWidth - endX; + const leftGap = startX; + clientWidth = (endX - startX) + Math.min(leftGap, rightGap) * 2; + } else if (horizontalAlign === HorizontalAlign.RIGHT) { + clientWidth = endX; + } return { height: clientHeight, @@ -232,6 +250,7 @@ export class SheetCellEditorResizeService extends Disposable implements IRenderM fill: Nullable, scaleX: number = 1, scaleY: number = 1, + horizontalAlign: HorizontalAlign, callback?: () => void ) { const editorObject = this._getEditorObject(); @@ -249,7 +268,7 @@ export class SheetCellEditorResizeService extends Disposable implements IRenderM const { document: documentComponent, scene: editorScene, engine: docEngine } = editorObject; const viewportMain = editorScene.getViewport(DOC_VIEWPORT_KEY.VIEW_MAIN); - const info = this._getEditorMaxSize(actualRangeWithCoord, canvasOffset); + const info = this._getEditorMaxSize(actualRangeWithCoord, canvasOffset, horizontalAlign); if (!info) return; const { height: clientHeight, width: clientWidth, scaleAdjust } = info; @@ -312,6 +331,13 @@ export class SheetCellEditorResizeService extends Disposable implements IRenderM startX = startX * scaleAdjust + (canvasBoundingRect.left - contentBoundingRect.left); startY = startY * scaleAdjust + (canvasBoundingRect.top - contentBoundingRect.top); + const cellWidth = actualRangeWithCoord.endX - actualRangeWithCoord.startX; + if (horizontalAlign === HorizontalAlign.RIGHT) { + startX += (cellWidth - editorWidth) * scaleAdjust; + } else if (horizontalAlign === HorizontalAlign.CENTER) { + startX += (cellWidth - editorWidth * scaleAdjust) / 2; + } + // Update cell editor container position and size. this._cellEditorManagerService.setState({ startX, @@ -370,8 +396,9 @@ export class SheetCellEditorResizeService extends Disposable implements IRenderM const skeleton = this._sheetSkeletonManagerService.getWorksheetSkeleton(editCellState.sheetId)?.skeleton; if (!skeleton) return; - const { row, column, scaleX, scaleY, position, canvasOffset } = editCellState; - const maxSize = this._getEditorMaxSize(position, canvasOffset); + const { row, column, scaleX, scaleY, position, canvasOffset, documentLayoutObject } = editCellState; + const { horizontalAlign } = documentLayoutObject; + const maxSize = this._getEditorMaxSize(position, canvasOffset, horizontalAlign); if (!maxSize) return; const { height: clientHeight, width: clientWidth, scaleAdjust } = maxSize; diff --git a/packages/sheets-ui/src/views/editor-container/EditorContainer.tsx b/packages/sheets-ui/src/views/editor-container/EditorContainer.tsx index 813f9270a5c..e9f63ec32d6 100644 --- a/packages/sheets-ui/src/views/editor-container/EditorContainer.tsx +++ b/packages/sheets-ui/src/views/editor-container/EditorContainer.tsx @@ -169,6 +169,7 @@ export const EditorContainer: React.FC = () => { editorBridgeService.disableForceKeepVisible(); } }} + autoScrollbar={false} /> )}
diff --git a/packages/sheets-ui/src/views/formula-bar/FormulaBar.tsx b/packages/sheets-ui/src/views/formula-bar/FormulaBar.tsx index 260f1faffe0..231c915a118 100644 --- a/packages/sheets-ui/src/views/formula-bar/FormulaBar.tsx +++ b/packages/sheets-ui/src/views/formula-bar/FormulaBar.tsx @@ -229,6 +229,7 @@ export function FormulaBar() { subUnitId={editState?.sheetId} isSupportAcrossSheet resetSelectionOnBlur={false} + autoScrollbar={false} keyboradEventConfig={keyCodeConfig} onFormulaSelectingChange={(isSelecting: 0 | 1 | 2) => { isRefSelecting.current = isSelecting; From 62f1295cd412618e29c46ba4570d8cf1280f532e Mon Sep 17 00:00:00 2001 From: zhangw Date: Mon, 23 Dec 2024 08:36:25 -0500 Subject: [PATCH 076/134] feat: update --- .../src/views/formula-editor/index.tsx | 60 +++++++++---------- .../views/range-selector/hooks/useFocus.ts | 11 ++-- .../src/views/range-selector/index.tsx | 51 +++++++++------- 3 files changed, 63 insertions(+), 59 deletions(-) diff --git a/packages/sheets-formula-ui/src/views/formula-editor/index.tsx b/packages/sheets-formula-ui/src/views/formula-editor/index.tsx index 6bd09ca619e..c6298472584 100644 --- a/packages/sheets-formula-ui/src/views/formula-editor/index.tsx +++ b/packages/sheets-formula-ui/src/views/formula-editor/index.tsx @@ -33,7 +33,6 @@ import { useDocHight, useSheetHighlight } from '../range-selector/hooks/useHighl import { useKeyboardEvent } from '../range-selector/hooks/useKeyboardEvent'; import { useLeftAndRightArrow } from '../range-selector/hooks/useLeftAndRightArrow'; import { useRefactorEffect } from '../range-selector/hooks/useRefactorEffect'; -import { useRefocus } from '../range-selector/hooks/useRefocus'; import { useResetSelection } from '../range-selector/hooks/useResetSelection'; import { useResize } from '../range-selector/hooks/useResize'; import { useSwitchSheet } from '../range-selector/hooks/useSwitchSheet'; @@ -181,6 +180,35 @@ export function FormulaEditor(props: IFormulaEditorProps) { }, [onFormulaSelectingChange, isSelecting]); useKeyboardEvent(isFocus, keyboradEventConfig, editor); + + useLayoutEffect(() => { + let dispose: IDisposable; + if (formulaEditorContainerRef.current) { + dispose = editorService.register({ + autofocus: true, + editorUnitId: editorId, + isSingle: true, + initialSnapshot: { + id: editorId, + body: { + dataStream: `${initValue}\r\n`, + textRuns: [], + customBlocks: [], + customDecorations: [], + customRanges: [], + }, + documentStyle: {}, + }, + }, formulaEditorContainerRef.current); + const editor = editorService.getEditor(editorId)! as Editor; + editorSet(editor); + } + + return () => { + dispose?.dispose(); + }; + }, []); + useLayoutEffect(() => { if (_isFocus) { isFocusSet(_isFocus); @@ -220,41 +248,11 @@ export function FormulaEditor(props: IFormulaEditorProps) { }); useSheetSelectionChange(isFocus, unitId, subUnitId, sequenceNodes, isSupportAcrossSheet, shouldMoveRefSelection, editor, handleSelectionChange); - - useRefocus(); useSwitchSheet(isFocus, unitId, isSupportAcrossSheet, isFocusSet, onBlur, noop); const { searchList, searchText, handlerFormulaReplace, reset: resetFormulaSearch } = useFormulaSearch(isFocus, sequenceNodes, editor); const { functionInfo, paramIndex, reset } = useFormulaDescribe(isFocus, formulaText, editor); - useLayoutEffect(() => { - let dispose: IDisposable; - if (formulaEditorContainerRef.current) { - dispose = editorService.register({ - autofocus: true, - editorUnitId: editorId, - isSingle: true, - initialSnapshot: { - id: editorId, - body: { - dataStream: `${initValue}\r\n`, - textRuns: [], - customBlocks: [], - customDecorations: [], - customRanges: [], - }, - documentStyle: {}, - }, - }, formulaEditorContainerRef.current); - const editor = editorService.getEditor(editorId)! as Editor; - editorSet(editor); - } - - return () => { - dispose?.dispose(); - }; - }, []); - const handleFunctionSelect = (v: string) => { const res = handlerFormulaReplace(v); if (res) { diff --git a/packages/sheets-formula-ui/src/views/range-selector/hooks/useFocus.ts b/packages/sheets-formula-ui/src/views/range-selector/hooks/useFocus.ts index 2cf60c7b1e1..c9e582e404a 100644 --- a/packages/sheets-formula-ui/src/views/range-selector/hooks/useFocus.ts +++ b/packages/sheets-formula-ui/src/views/range-selector/hooks/useFocus.ts @@ -15,18 +15,19 @@ */ import type { Editor } from '@univerjs/docs-ui'; +import { Tools } from '@univerjs/core'; import { useCallback } from 'react'; export const useFocus = (editor?: Editor) => { - const focus = useCallback(() => { + const focus = useCallback((offset?: number) => { if (editor) { editor.focus(); const selections = [...editor.getSelectionRanges()]; - if (selections.length) { + if (Tools.isDefine(offset)) { + editor.setSelectionRanges([{ startOffset: offset, endOffset: offset }]); + } else if (selections.length) { editor.setSelectionRanges(selections); - } - // end - if (!selections.length) { + } else { const body = editor.getDocumentData().body?.dataStream ?? '\r\n'; const offset = Math.max(body.length - 2, 0); editor.setSelectionRanges([{ startOffset: offset, endOffset: offset }]); diff --git a/packages/sheets-formula-ui/src/views/range-selector/index.tsx b/packages/sheets-formula-ui/src/views/range-selector/index.tsx index f77a6b75820..8851b47effa 100644 --- a/packages/sheets-formula-ui/src/views/range-selector/index.tsx +++ b/packages/sheets-formula-ui/src/views/range-selector/index.tsx @@ -88,21 +88,26 @@ export interface IRangeSelectorProps { const noopFunction = () => { }; export function RangeSelector(props: IRangeSelectorProps) { - const { initValue, unitId, subUnitId, errorText, placeholder, actions, - onChange = noopFunction, - onVerify = noopFunction, - onRangeSelectorDialogVisibleChange = noopFunction, - onBlur = noopFunction, - onFocus = noopFunction, - isFocus: _isFocus = true, - isOnlyOneRange = false, - isSupportAcrossSheet = false } = props; - + const { + initValue, + unitId, + subUnitId, + errorText, + placeholder, + actions, + onChange = noopFunction, + onVerify = noopFunction, + onRangeSelectorDialogVisibleChange = noopFunction, + onBlur = noopFunction, + onFocus = noopFunction, + isFocus: _isFocus = true, + isOnlyOneRange = false, + isSupportAcrossSheet = false, + } = props; const editorService = useDependency(IEditorService); const localeService = useDependency(LocaleService); const commandService = useDependency(ICommandService); const lexerTreeBuilder = useDependency(LexerTreeBuilder); - const rangeSelectorWrapRef = useRef(null); const [rangeDialogVisible, rangeDialogVisibleSet] = useState(false); const [isFocus, isFocusSet] = useState(_isFocus); @@ -173,17 +178,6 @@ export function RangeSelector(props: IRangeSelectorProps) { const focus = useFocus(editor); - useLayoutEffect(() => { - if (_isFocus) { - isFocusSet(_isFocus); - focus(); - } else { - editor?.blur(); - resetSelection(); - isFocusSet(_isFocus); - } - }, [_isFocus, focus]); - const { checkScrollBar } = useResize(editor); const getFormulaToken = useFormulaToken(); const sequenceNodes = useMemo(() => getFormulaToken(rangeString), [rangeString]); @@ -279,7 +273,7 @@ export function RangeSelector(props: IRangeSelectorProps) { isSingle: true, initialSnapshot: { id: editorId, - body: { dataStream: '\r\n' }, + body: { dataStream: `${initValue}\r\n` }, documentStyle: {}, }, }, containerRef.current); @@ -291,6 +285,17 @@ export function RangeSelector(props: IRangeSelectorProps) { }; }, []); + useLayoutEffect(() => { + if (_isFocus) { + isFocusSet(_isFocus); + focus(); + } else { + editor?.blur(); + resetSelection(); + isFocusSet(_isFocus); + } + }, [_isFocus, focus]); + useFirstHighlightDoc(rangeString, '', isFocus, highlightDoc, highlightSheet, editor); const handleClick = () => { From c419a3d3f7a519f34869df43e2c780dbc8ad9c67 Mon Sep 17 00:00:00 2001 From: zhangw Date: Tue, 24 Dec 2024 15:07:06 +0800 Subject: [PATCH 077/134] feat: update --- .../editor/editing.render-controller.ts | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) 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 afd38196a7d..11c9712df37 100644 --- a/packages/sheets-ui/src/controllers/editor/editing.render-controller.ts +++ b/packages/sheets-ui/src/controllers/editor/editing.render-controller.ts @@ -844,15 +844,11 @@ function emptyBody(body: IDocumentBody, removeStyle = false) { } if (body.paragraphs != null) { - if (body.paragraphs.length === 1) { - body.paragraphs[0].startIndex = 0; - } else { - body.paragraphs = [ - { - startIndex: 0, - }, - ]; - } + body.paragraphs = [ + { + startIndex: 0, + }, + ]; } if (body.sectionBreaks != null) { From 295497ce3e88a08268162c8e4a61451317880513 Mon Sep 17 00:00:00 2001 From: zhangw Date: Wed, 25 Dec 2024 02:24:13 +0800 Subject: [PATCH 078/134] feat: disable undo redo shortcut on cell editor --- .../src/controllers/shared-shortcut.controller.ts | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/packages/ui/src/controllers/shared-shortcut.controller.ts b/packages/ui/src/controllers/shared-shortcut.controller.ts index fe4b737794b..7c43de28758 100644 --- a/packages/ui/src/controllers/shared-shortcut.controller.ts +++ b/packages/ui/src/controllers/shared-shortcut.controller.ts @@ -17,7 +17,7 @@ import type { IContextService } from '@univerjs/core'; import type { IShortcutItem } from '../services/shortcut/shortcut.service'; -import { Disposable, FOCUSING_UNIVER_EDITOR, ICommandService, RedoCommand, UndoCommand } from '@univerjs/core'; +import { Disposable, EDITOR_ACTIVATED, FOCUSING_FX_BAR_EDITOR, FOCUSING_UNIVER_EDITOR, ICommandService, RedoCommand, UndoCommand } from '@univerjs/core'; import { CopyCommand, CutCommand, PasteCommand } from '../services/clipboard/clipboard.command'; import { KeyCode, MetaKeys } from '../services/shortcut/keycode'; import { IShortcutService } from '../services/shortcut/shortcut.service'; @@ -30,6 +30,13 @@ function whenEditorFocused(contextService: IContextService): boolean { return contextService.getContextValue(FOCUSING_UNIVER_EDITOR); } +function whenEditorFocusedButNotCellEditor(contextService: IContextService): boolean { + return ( + contextService.getContextValue(FOCUSING_UNIVER_EDITOR) && + !(contextService.getContextValue(EDITOR_ACTIVATED) || contextService.getContextValue(FOCUSING_FX_BAR_EDITOR)) + ); +} + export const CopyShortcutItem: IShortcutItem = { id: CopyCommand.id, description: 'shortcut.copy', @@ -72,7 +79,7 @@ export const UndoShortcutItem: IShortcutItem = { description: 'shortcut.undo', group: '1_common-edit', binding: KeyCode.Z | MetaKeys.CTRL_COMMAND, - preconditions: whenEditorFocused, + preconditions: whenEditorFocusedButNotCellEditor, }; export const RedoShortcutItem: IShortcutItem = { @@ -80,7 +87,7 @@ export const RedoShortcutItem: IShortcutItem = { description: 'shortcut.redo', group: '1_common-edit', binding: KeyCode.Y | MetaKeys.CTRL_COMMAND, - preconditions: whenEditorFocused, + preconditions: whenEditorFocusedButNotCellEditor, }; /** From a000ea09f34b16c38becea0a2c7bcdc37fbe6854 Mon Sep 17 00:00:00 2001 From: zhangw Date: Wed, 25 Dec 2024 13:04:40 +0800 Subject: [PATCH 079/134] feat: update --- packages/docs-ui/src/index.ts | 3 +- .../docs-ui/src/services/editor/editor.ts | 14 +- .../selection/doc-selection-render.service.ts | 8 +- .../src/views/rich-text-editor/hooks/index.ts | 17 +++ .../hooks/useKeyboardEvent.ts | 70 +++++++++ .../hooks/useLeftAndRightArrow.ts | 113 +++++++++++++++ .../views/rich-text-editor/index.module.less | 37 +++++ .../src/views/rich-text-editor/index.tsx | 134 ++++++++++++++++++ .../operations/insert-function.operation.ts | 6 +- .../views/more-functions/MoreFunctions.tsx | 7 +- .../range-selector/hooks/useKeyboardEvent.ts | 55 +------ 11 files changed, 398 insertions(+), 66 deletions(-) create mode 100644 packages/docs-ui/src/views/rich-text-editor/hooks/index.ts create mode 100644 packages/docs-ui/src/views/rich-text-editor/hooks/useKeyboardEvent.ts create mode 100644 packages/docs-ui/src/views/rich-text-editor/hooks/useLeftAndRightArrow.ts create mode 100644 packages/docs-ui/src/views/rich-text-editor/index.module.less create mode 100644 packages/docs-ui/src/views/rich-text-editor/index.tsx diff --git a/packages/docs-ui/src/index.ts b/packages/docs-ui/src/index.ts index 6f572240a06..6226fd91c7f 100644 --- a/packages/docs-ui/src/index.ts +++ b/packages/docs-ui/src/index.ts @@ -24,7 +24,8 @@ export { addCustomDecorationBySelectionFactory, addCustomDecorationFactory, dele export * from './basics/docs-view-key'; export { hasParagraphInTable } from './basics/paragraph'; export { docDrawingPositionToTransform, transformToDocDrawingPosition } from './basics/transform-position'; - +export { type IKeyboardEventConfig, useKeyboardEvent } from './views/rich-text-editor/hooks'; +export { RichTextEditor } from './views/rich-text-editor'; export { getCommandSkeleton, getRichTextEditPath } from './commands/util'; // export { TextEditor } from './components/editor/TextEditor'; // export { RangeSelector as DocRangeSelector } from './components/range-selector/RangeSelector'; diff --git a/packages/docs-ui/src/services/editor/editor.ts b/packages/docs-ui/src/services/editor/editor.ts index f9ca26c02fc..5558c53d30f 100644 --- a/packages/docs-ui/src/services/editor/editor.ts +++ b/packages/docs-ui/src/services/editor/editor.ts @@ -315,7 +315,7 @@ export class Editor extends Disposable implements IEditor { }); } - replaceText(text: string) { + replaceText(text: string, resetCursor = true) { const data = this.getDocumentData(); this.setDocumentData( @@ -325,11 +325,13 @@ export class Editor extends Disposable implements IEditor { dataStream: `${text}\r\n`, }, }, - [{ - startOffset: text.length, - endOffset: text.length, - collapsed: true, - }] + resetCursor + ? [{ + startOffset: text.length, + endOffset: text.length, + collapsed: true, + }] + : null ); } diff --git a/packages/docs-ui/src/services/selection/doc-selection-render.service.ts b/packages/docs-ui/src/services/selection/doc-selection-render.service.ts index 504373ca134..5d05ad52072 100644 --- a/packages/docs-ui/src/services/selection/doc-selection-render.service.ts +++ b/packages/docs-ui/src/services/selection/doc-selection-render.service.ts @@ -103,7 +103,11 @@ export class DocSelectionRenderService extends RxDisposable implements IRenderMo private _reserveRanges = false; get isFocusing() { - return this._input === document.activeElement || document.activeElement === document.body; + return this._input === document.activeElement; + } + + get canFocusing() { + return this.isFocusing || document.activeElement === document.body || document.activeElement === null; } constructor( @@ -312,7 +316,7 @@ export class DocSelectionRenderService extends RxDisposable implements IRenderMo this._container.style.top = `${y}px`; this._container.style.zIndex = '1000'; - if (this.isFocusing || document.activeElement === null || force) { + if (this.canFocusing || force) { this.focus(); } } diff --git a/packages/docs-ui/src/views/rich-text-editor/hooks/index.ts b/packages/docs-ui/src/views/rich-text-editor/hooks/index.ts new file mode 100644 index 00000000000..f31ab518f60 --- /dev/null +++ b/packages/docs-ui/src/views/rich-text-editor/hooks/index.ts @@ -0,0 +1,17 @@ +/** + * 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. + */ + +export { type IKeyboardEventConfig, useKeyboardEvent } from './useKeyboardEvent'; diff --git a/packages/docs-ui/src/views/rich-text-editor/hooks/useKeyboardEvent.ts b/packages/docs-ui/src/views/rich-text-editor/hooks/useKeyboardEvent.ts new file mode 100644 index 00000000000..e501e226266 --- /dev/null +++ b/packages/docs-ui/src/views/rich-text-editor/hooks/useKeyboardEvent.ts @@ -0,0 +1,70 @@ +/** + * 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 type { KeyCode, MetaKeys } from '@univerjs/ui'; +import type { Editor } from '../../../services/editor/editor'; +import { CommandType, DisposableCollection, ICommandService, useDependency } from '@univerjs/core'; +import { DeviceInputEventType } from '@univerjs/engine-render'; +import { IShortcutService } from '@univerjs/ui'; +import { useEffect } from 'react'; + +export interface IKeyboardEventConfig { + keyCodes: { keyCode: KeyCode; metaKey?: MetaKeys }[]; + handler: (keyCode: KeyCode, metaKey?: MetaKeys) => void; +} + +export function useKeyboardEvent(isNeed: boolean, config?: IKeyboardEventConfig, editor?: Editor) { + const commandService = useDependency(ICommandService); + const shortcutService = useDependency(IShortcutService); + + useEffect(() => { + if (!editor || !isNeed || !config) { + return; + } + const editorId = editor.getEditorId(); + const operationId = `sheet.operation.formula-embedding-editor-${editorId}-keyboard-event`; + const d = new DisposableCollection(); + + d.add(commandService.registerCommand({ + id: operationId, + type: CommandType.OPERATION, + handler(_event, params) { + const { keyCode, metaKey } = params as { eventType: DeviceInputEventType; keyCode: KeyCode; metaKey?: MetaKeys }; + config.handler(keyCode, metaKey); + }, + })); + + config.keyCodes.map((keyCode) => { + return { + id: operationId, + binding: keyCode.metaKey ? keyCode.keyCode | keyCode.metaKey : keyCode.keyCode, + preconditions: () => true, + priority: 901, + staticParameters: { + eventType: DeviceInputEventType.Keyboard, + keyCode: keyCode.keyCode, + metaKey: keyCode.metaKey, + }, + }; + }).forEach((item) => { + d.add(shortcutService.registerShortcut(item)); + }); + + return () => { + d.dispose(); + }; + }, [commandService, config, editor, isNeed, shortcutService]); +} diff --git a/packages/docs-ui/src/views/rich-text-editor/hooks/useLeftAndRightArrow.ts b/packages/docs-ui/src/views/rich-text-editor/hooks/useLeftAndRightArrow.ts new file mode 100644 index 00000000000..98a16162a59 --- /dev/null +++ b/packages/docs-ui/src/views/rich-text-editor/hooks/useLeftAndRightArrow.ts @@ -0,0 +1,113 @@ +/** + * 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 type { Editor } from '../../../services/editor/editor'; +import { CommandType, Direction, DisposableCollection, ICommandService, useDependency } from '@univerjs/core'; +import { DeviceInputEventType } from '@univerjs/engine-render'; +import { IShortcutService, KeyCode, MetaKeys } from '@univerjs/ui'; +import { useEffect, useRef } from 'react'; +import { MoveCursorOperation, MoveSelectionOperation } from '../../../commands/operations/doc-cursor.operation'; + +// eslint-disable-next-line max-lines-per-function +export const useLeftAndRightArrow = (isNeed: boolean, shouldMoveSelection: boolean, editor?: Editor, onMoveInEditor?: (keyCode: KeyCode, metaKey?: MetaKeys) => void) => { + const commandService = useDependency(ICommandService); + const shortcutService = useDependency(IShortcutService); + const shouldMoveSelectionRef = useRef(shouldMoveSelection); + shouldMoveSelectionRef.current = shouldMoveSelection; + const onMoveInEditorRef = useRef(onMoveInEditor); + onMoveInEditorRef.current = onMoveInEditor; + + useEffect(() => { + if (!editor || !isNeed) { + return; + } + const editorId = editor.getEditorId(); + const operationId = `sheet.formula-embedding-editor.${editorId}`; + const d = new DisposableCollection(); + const handleMoveInEditor = (keycode: KeyCode, metaKey?: MetaKeys) => { + if (onMoveInEditorRef.current) { + onMoveInEditorRef.current(keycode, metaKey); + return; + } + + let direction = Direction.LEFT; + if (keycode === KeyCode.ARROW_DOWN) { + direction = Direction.DOWN; + } else if (keycode === KeyCode.ARROW_UP) { + direction = Direction.UP; + } else if (keycode === KeyCode.ARROW_RIGHT) { + direction = Direction.RIGHT; + } + + if (metaKey === MetaKeys.SHIFT) { + commandService.executeCommand(MoveSelectionOperation.id, { + direction, + }); + } else { + commandService.executeCommand(MoveCursorOperation.id, { + direction, + }); + } + }; + + d.add(commandService.registerCommand({ + id: operationId, + type: CommandType.OPERATION, + handler(_event, params) { + const { keyCode } = params as { eventType: DeviceInputEventType; keyCode: KeyCode }; + handleMoveInEditor(keyCode); + }, + })); + + const keyCodes = [ + { keyCode: KeyCode.ARROW_DOWN }, + { keyCode: KeyCode.ARROW_LEFT }, + { keyCode: KeyCode.ARROW_RIGHT }, + { keyCode: KeyCode.ARROW_UP }, + { keyCode: KeyCode.ARROW_DOWN, metaKey: MetaKeys.SHIFT }, + { keyCode: KeyCode.ARROW_LEFT, metaKey: MetaKeys.SHIFT }, + { keyCode: KeyCode.ARROW_RIGHT, metaKey: MetaKeys.SHIFT }, + { keyCode: KeyCode.ARROW_UP, metaKey: MetaKeys.SHIFT }, + { keyCode: KeyCode.ARROW_DOWN, metaKey: MetaKeys.CTRL_COMMAND }, + { keyCode: KeyCode.ARROW_LEFT, metaKey: MetaKeys.CTRL_COMMAND }, + { keyCode: KeyCode.ARROW_RIGHT, metaKey: MetaKeys.CTRL_COMMAND }, + { keyCode: KeyCode.ARROW_UP, metaKey: MetaKeys.CTRL_COMMAND }, + { keyCode: KeyCode.ARROW_DOWN, metaKey: MetaKeys.CTRL_COMMAND | MetaKeys.SHIFT }, + { keyCode: KeyCode.ARROW_LEFT, metaKey: MetaKeys.CTRL_COMMAND | MetaKeys.SHIFT }, + { keyCode: KeyCode.ARROW_RIGHT, metaKey: MetaKeys.CTRL_COMMAND | MetaKeys.SHIFT }, + { keyCode: KeyCode.ARROW_UP, metaKey: MetaKeys.CTRL_COMMAND | MetaKeys.SHIFT }, + ]; + + keyCodes.map(({ keyCode, metaKey }) => { + return { + id: operationId, + binding: metaKey ? keyCode | metaKey : keyCode, + preconditions: () => true, + priority: 900, + staticParameters: { + eventType: DeviceInputEventType.Keyboard, + keyCode, + }, + }; + }).forEach((item) => { + d.add(shortcutService.registerShortcut(item)); + }); + + return () => { + d.dispose(); + }; + }, [commandService, editor, isNeed, shortcutService]); +}; diff --git a/packages/docs-ui/src/views/rich-text-editor/index.module.less b/packages/docs-ui/src/views/rich-text-editor/index.module.less new file mode 100644 index 00000000000..73c41e878ff --- /dev/null +++ b/packages/docs-ui/src/views/rich-text-editor/index.module.less @@ -0,0 +1,37 @@ +.rich-text-editor { + &-active { + border-color: rgb(var(--hyacinth-500)) !important; + } + + &-wrap { + height: 32px; + padding: 6px 8px 2px 6px; + width: 100%; + display: flex; + justify-content: space-around; + align-items: center; + gap: 8px; + border: 1px solid rgb(var(--border-color)); + border-radius: var(--border-radius-base); + box-sizing: border-box; + position: relative; + + .rich-text-editor-text { + width: 100%; + height: 100%; + position: relative; + } + + .rich-text-editor-error-wrap { + font-size: 12px; + color: rgb(var(--red-500)); + position: absolute; + bottom: -18px; + left: 0px; + } + } + + &-error { + border: 1px solid rgb(var(--red-500)) !important; + } +} diff --git a/packages/docs-ui/src/views/rich-text-editor/index.tsx b/packages/docs-ui/src/views/rich-text-editor/index.tsx new file mode 100644 index 00000000000..2c95f6ccf67 --- /dev/null +++ b/packages/docs-ui/src/views/rich-text-editor/index.tsx @@ -0,0 +1,134 @@ +/** + * 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 type { Editor } from '../../services/editor/editor'; +import type { IKeyboardEventConfig } from './hooks'; +import { createInternalEditorID, generateRandomId, type IDisposable, type IDocumentData, useDependency, useObservable } from '@univerjs/core'; +import { IRenderManagerService } from '@univerjs/engine-render'; +import { useEvent } from '@univerjs/ui'; +import clsx from 'clsx'; +import React, { useEffect, useLayoutEffect, useMemo, useState } from 'react'; +import { IEditorService } from '../../services/editor/editor-manager.service'; +import { DocSelectionRenderService } from '../../services/selection/doc-selection-render.service'; +import { useKeyboardEvent } from './hooks'; +import { useLeftAndRightArrow } from './hooks/useLeftAndRightArrow'; +import styles from './index.module.less'; + +export interface IRichTextEditorProps { + className?: string; + autoFocus?: boolean; + onFocusChange?: (isFocus: boolean) => void; + initialValue?: IDocumentData; + onClickOutside?: () => void; + keyboradEventConfig?: IKeyboardEventConfig; + moveCursor?: boolean; +} + +export const RichTextEditor = (props: IRichTextEditorProps) => { + const { + className, + autoFocus: _autoFocus, + onFocusChange: _onFocusChange, + initialValue, + onClickOutside: _onClickOutside, + keyboradEventConfig, + moveCursor = true, + } = props; + const autoFocus = useMemo(() => _autoFocus ?? false, []); + const onFocusChange = useEvent(_onFocusChange); + const onClickOutside = useEvent(_onClickOutside); + const editorService = useDependency(IEditorService); + const formulaEditorContainerRef = React.useRef(null); + const editorId = useMemo(() => createInternalEditorID(`RICH_TEXT_EDITOR-${generateRandomId(4)}`), []); + const [editor, setEditor] = useState(); + const renderManagerService = useDependency(IRenderManagerService); + const renderer = renderManagerService.getRenderById(editorId); + const docSelectionRenderService = renderer?.with(DocSelectionRenderService); + const isFocusing = docSelectionRenderService?.isFocusing ?? false; + const sheetEmbeddingRef = React.useRef(null); + useObservable(editor?.blur$); + useObservable(editor?.focus$); + + useEffect(() => { + onFocusChange?.(isFocusing); + }, [isFocusing, onFocusChange]); + + useEffect(() => { + const handleClickOutside = (event: MouseEvent) => { + if (sheetEmbeddingRef.current && !sheetEmbeddingRef.current.contains(event.target as any)) { + onClickOutside?.(); + } + }; + document.addEventListener('click', handleClickOutside); + return () => { + document.removeEventListener('click', handleClickOutside); + }; + }, [onClickOutside]); + + useLayoutEffect(() => { + let dispose: IDisposable; + if (formulaEditorContainerRef.current) { + dispose = editorService.register({ + autofocus: true, + editorUnitId: editorId, + isSingle: true, + initialSnapshot: { + body: { + dataStream: '\r\n', + textRuns: [], + customBlocks: [], + customDecorations: [], + customRanges: [], + }, + documentStyle: {}, + ...initialValue, + id: editorId, + }, + }, formulaEditorContainerRef.current); + const editor = editorService.getEditor(editorId)! as Editor; + setEditor(editor); + + if (autoFocus) { + editor.focus(); + } + } + + return () => { + dispose?.dispose(); + }; + }, []); + + useLeftAndRightArrow(isFocusing && moveCursor, false, editor); + useKeyboardEvent(isFocusing, keyboradEventConfig, editor); + +
+
+
{ + editor?.focus(); + }} + > +
+
+
; +}; diff --git a/packages/sheets-formula-ui/src/commands/operations/insert-function.operation.ts b/packages/sheets-formula-ui/src/commands/operations/insert-function.operation.ts index 1034b3c4068..7c0b9a5347c 100644 --- a/packages/sheets-formula-ui/src/commands/operations/insert-function.operation.ts +++ b/packages/sheets-formula-ui/src/commands/operations/insert-function.operation.ts @@ -19,6 +19,7 @@ import { CellValueType, CommandType, DEFAULT_EMPTY_DOCUMENT_VALUE, + DOCS_FORMULA_BAR_EDITOR_UNIT_ID_KEY, DOCS_NORMAL_EDITOR_UNIT_ID_KEY, getCellValueType, ICommandService, @@ -154,12 +155,15 @@ export const InsertFunctionOperation: ICommand = { }; await commandService.executeCommand(SetSelectionsOperation.id, setSelectionParams); const editor = editorService.getEditor(DOCS_NORMAL_EDITOR_UNIT_ID_KEY); + const formulaEditor = editorService.getEditor(DOCS_FORMULA_BAR_EDITOR_UNIT_ID_KEY); editorBridgeService.changeVisible({ visible: true, unitId, eventType: DeviceInputEventType.Dblclick, }); - editor?.replaceText(`=${value}(${editFormulaRangeString}`); + const formulaText = `=${value}(${editFormulaRangeString}`; + editor?.replaceText(formulaText); + formulaEditor?.replaceText(formulaText, false); } if (list.length === 0) return false; diff --git a/packages/sheets-formula-ui/src/views/more-functions/MoreFunctions.tsx b/packages/sheets-formula-ui/src/views/more-functions/MoreFunctions.tsx index e0d580f2176..f34c5bba854 100644 --- a/packages/sheets-formula-ui/src/views/more-functions/MoreFunctions.tsx +++ b/packages/sheets-formula-ui/src/views/more-functions/MoreFunctions.tsx @@ -15,7 +15,7 @@ */ import type { IFunctionInfo } from '@univerjs/engine-formula'; -import { DOCS_NORMAL_EDITOR_UNIT_ID_KEY, IUniverInstanceService, LocaleService, useDependency } from '@univerjs/core'; +import { DOCS_FORMULA_BAR_EDITOR_UNIT_ID_KEY, DOCS_NORMAL_EDITOR_UNIT_ID_KEY, IUniverInstanceService, LocaleService, useDependency } from '@univerjs/core'; import { Button } from '@univerjs/design'; import { IEditorService } from '@univerjs/docs-ui'; import { DeviceInputEventType } from '@univerjs/engine-render'; @@ -55,7 +55,10 @@ export function MoreFunctions() { eventType: DeviceInputEventType.Dblclick, }); const editor = editorService.getEditor(DOCS_NORMAL_EDITOR_UNIT_ID_KEY); - editor?.replaceText(`=${functionInfo?.functionName}(`); + const formulaEditor = editorService.getEditor(DOCS_FORMULA_BAR_EDITOR_UNIT_ID_KEY); + const formulaText = `=${functionInfo?.functionName}(`; + editor?.replaceText(formulaText); + formulaEditor?.replaceText(formulaText, false); } return ( diff --git a/packages/sheets-formula-ui/src/views/range-selector/hooks/useKeyboardEvent.ts b/packages/sheets-formula-ui/src/views/range-selector/hooks/useKeyboardEvent.ts index 0e0d3f5318a..5a21a0682c7 100644 --- a/packages/sheets-formula-ui/src/views/range-selector/hooks/useKeyboardEvent.ts +++ b/packages/sheets-formula-ui/src/views/range-selector/hooks/useKeyboardEvent.ts @@ -14,57 +14,4 @@ * limitations under the License. */ -import type { Editor } from '@univerjs/docs-ui'; -import type { KeyCode, MetaKeys } from '@univerjs/ui'; -import { CommandType, DisposableCollection, ICommandService, useDependency } from '@univerjs/core'; -import { DeviceInputEventType } from '@univerjs/engine-render'; -import { IShortcutService } from '@univerjs/ui'; -import { useEffect } from 'react'; - -export interface IKeyboardEventConfig { - keyCodes: { keyCode: KeyCode; metaKey?: MetaKeys }[]; - handler: (keyCode: KeyCode, metaKey?: MetaKeys) => void; -} - -export function useKeyboardEvent(isNeed: boolean, config?: IKeyboardEventConfig, editor?: Editor) { - const commandService = useDependency(ICommandService); - const shortcutService = useDependency(IShortcutService); - - useEffect(() => { - if (!editor || !isNeed || !config) { - return; - } - const editorId = editor.getEditorId(); - const operationId = `sheet.operation.formula-embedding-editor-${editorId}-keyboard-event`; - const d = new DisposableCollection(); - - d.add(commandService.registerCommand({ - id: operationId, - type: CommandType.OPERATION, - handler(_event, params) { - const { keyCode, metaKey } = params as { eventType: DeviceInputEventType; keyCode: KeyCode; metaKey?: MetaKeys }; - config.handler(keyCode, metaKey); - }, - })); - - config.keyCodes.map((keyCode) => { - return { - id: operationId, - binding: keyCode.metaKey ? keyCode.keyCode | keyCode.metaKey : keyCode.keyCode, - preconditions: () => true, - priority: 901, - staticParameters: { - eventType: DeviceInputEventType.Keyboard, - keyCode: keyCode.keyCode, - metaKey: keyCode.metaKey, - }, - }; - }).forEach((item) => { - d.add(shortcutService.registerShortcut(item)); - }); - - return () => { - d.dispose(); - }; - }, [commandService, config, editor, isNeed, shortcutService]); -} +export { type IKeyboardEventConfig, useKeyboardEvent } from '@univerjs/docs-ui'; From 11579723b1d3ae50f1575f0afae2e462ed825bca Mon Sep 17 00:00:00 2001 From: zhangw Date: Wed, 25 Dec 2024 13:24:03 +0800 Subject: [PATCH 080/134] feat: update --- .../src/views/range-selector/index.tsx | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/packages/sheets-formula-ui/src/views/range-selector/index.tsx b/packages/sheets-formula-ui/src/views/range-selector/index.tsx index 8851b47effa..70c2bfdf8c1 100644 --- a/packages/sheets-formula-ui/src/views/range-selector/index.tsx +++ b/packages/sheets-formula-ui/src/views/range-selector/index.tsx @@ -26,11 +26,12 @@ import { CloseSingle, DeleteSingle, IncreaseSingle, SelectRangeSingle } from '@u import { IDescriptionService } from '@univerjs/sheets-formula'; import { RANGE_SELECTOR_SYMBOLS, SetCellEditVisibleOperation } from '@univerjs/sheets-ui'; +import { useEvent } from '@univerjs/ui'; import cl from 'clsx'; import React, { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react'; import { noop, throttleTime } from 'rxjs'; -import { RefSelectionsRenderService } from '../../services/render-services/ref-selections.render-service'; +import { RefSelectionsRenderService } from '../../services/render-services/ref-selections.render-service'; import { useEditorInput } from './hooks/useEditorInput'; import { useEmitChange } from './hooks/useEmitChange'; import { useFirstHighlightDoc } from './hooks/useFirstHighlightDoc'; @@ -112,7 +113,8 @@ export function RangeSelector(props: IRangeSelectorProps) { const [rangeDialogVisible, rangeDialogVisibleSet] = useState(false); const [isFocus, isFocusSet] = useState(_isFocus); const editorId = useMemo(() => createInternalEditorID(`${RANGE_SELECTOR_SYMBOLS}-${generateRandomId(4)}`), []); - const [editor, editorSet] = useState(); + const editorRef = useRef(); + const editor = editorRef.current; const containerRef = useRef(null); const univerInstanceService = useDependency(IUniverInstanceService); const isNeed = useMemo(() => !rangeDialogVisible && isFocus, [rangeDialogVisible, isFocus]); @@ -184,14 +186,16 @@ export function RangeSelector(props: IRangeSelectorProps) { const highlightDoc = useDocHight(); const highlightSheet = useSheetHighlight(unitId); - const highligh = (text: string, isNeedResetSelection: boolean = true) => { - if (!editor) { + const highligh = useEvent((text: string, isNeedResetSelection: boolean = true, showSelection = true) => { + if (!editorRef.current) { return; } const sequenceNodes = getFormulaToken(text); - const ranges = highlightDoc(editor, sequenceNodes, isNeedResetSelection); - highlightSheet(ranges); - }; + const ranges = highlightDoc(editorRef.current, sequenceNodes, isNeedResetSelection); + if (showSelection) { + highlightSheet(ranges); + } + }); const needEmit = useEmitChange(sequenceNodes, handleInput, editor); @@ -273,12 +277,13 @@ export function RangeSelector(props: IRangeSelectorProps) { isSingle: true, initialSnapshot: { id: editorId, - body: { dataStream: `${initValue}\r\n` }, + body: { dataStream: `${rangeString}\r\n`, textRuns: [] }, documentStyle: {}, }, }, containerRef.current); const editor = editorService.getEditor(editorId)! as Editor; - editorSet(editor); + editorRef.current = editor; + highligh(rangeString, false, false); } return () => { dispose?.dispose(); From 45f897b79b2492b5e66026aef6b252da64818d2a Mon Sep 17 00:00:00 2001 From: zhangw Date: Wed, 25 Dec 2024 13:54:18 +0800 Subject: [PATCH 081/134] feat: update --- .../src/views/formula-editor/index.tsx | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/packages/sheets-formula-ui/src/views/formula-editor/index.tsx b/packages/sheets-formula-ui/src/views/formula-editor/index.tsx index c6298472584..a4e4ad149b5 100644 --- a/packages/sheets-formula-ui/src/views/formula-editor/index.tsx +++ b/packages/sheets-formula-ui/src/views/formula-editor/index.tsx @@ -109,7 +109,8 @@ export function FormulaEditor(props: IFormulaEditorProps) { const onFormulaSelectingChange = useEvent(propOnFormulaSelectingChange); const searchFunctionRef = useRef(null); - const [editor, editorSet] = useState(); + const editorRef = useRef(); + const editor = editorRef.current; const [isFocus, isFocusSet] = useState(_isFocus); const formulaEditorContainerRef = useRef(null); const editorId = useMemo(() => propEditorId ?? createInternalEditorID(`${EMBEDDING_FORMULA_EDITOR}-${generateRandomId(4)}`), []); @@ -148,19 +149,20 @@ export function FormulaEditor(props: IFormulaEditorProps) { const highlightDoc = useDocHight('='); const highlightSheet = useSheetHighlight(unitId); const highligh = useEvent((text: string, isNeedResetSelection: boolean = true, isEnd?: boolean) => { - if (!editor) { + if (!editorRef.current) { return; } const preText = highTextRef.current; highTextRef.current = text; const sequenceNodes = getFormulaToken(text[0] === '=' ? text.slice(1) : ''); const ranges = highlightDoc( - editor, + editorRef.current, sequenceNodes, isNeedResetSelection, // remove equals need to remove highlight style preText.slice(1) === text && preText[0] === '=' ); + if (isEnd) { highlightSheet(isFocus ? ranges : []); } @@ -170,6 +172,12 @@ export function FormulaEditor(props: IFormulaEditorProps) { highligh(formulaText, false); }, [highligh, formulaText, isFocus]); + useEffect(() => { + if (isFocus) { + highligh(formulaText, false, true); + } + }, [isFocus]); + useVerify(isFocus, onVerify, formulaText); const focus = useFocus(editor); @@ -201,7 +209,8 @@ export function FormulaEditor(props: IFormulaEditorProps) { }, }, formulaEditorContainerRef.current); const editor = editorService.getEditor(editorId)! as Editor; - editorSet(editor); + editorRef.current = editor; + highligh(initValue, false, true); } return () => { From cca97df7a7e1e51435d635f313125a9dca818b8a Mon Sep 17 00:00:00 2001 From: zhangw Date: Wed, 25 Dec 2024 14:13:16 +0800 Subject: [PATCH 082/134] feat: update --- .../src/controllers/editor/editing.render-controller.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 b48409d8356..984015b50fd 100644 --- a/packages/sheets-ui/src/controllers/editor/editing.render-controller.ts +++ b/packages/sheets-ui/src/controllers/editor/editing.render-controller.ts @@ -481,7 +481,7 @@ export class EditingRenderController extends Disposable implements IRenderModule }); } - // Reselect the current selections, when exist cell editor by press ESC.I + // Reselect the current selections, when exist cell editor by press ESC.I if (keycode === KeyCode.ESC) { if (this._editorBridgeService.isForceKeepVisible()) { this._editorBridgeService.disableForceKeepVisible(); @@ -490,7 +490,7 @@ export class EditingRenderController extends Disposable implements IRenderModule if (selections) { this._commandService.syncExecuteCommand(SetSelectionsOperation.id, { unitId: this._context.unit.getUnitId(), - subUnitId: worksheetId, + subUnitId: sheetId, selections, }); } From 70ffcf8512b977b7c00234db34b7420055837d0f Mon Sep 17 00:00:00 2001 From: zhangw Date: Wed, 25 Dec 2024 14:31:15 +0800 Subject: [PATCH 083/134] feat: update --- .../src/views/range-selector/index.tsx | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/packages/sheets-formula-ui/src/views/range-selector/index.tsx b/packages/sheets-formula-ui/src/views/range-selector/index.tsx index 70c2bfdf8c1..b24f2ee819c 100644 --- a/packages/sheets-formula-ui/src/views/range-selector/index.tsx +++ b/packages/sheets-formula-ui/src/views/range-selector/index.tsx @@ -129,14 +129,16 @@ export function RangeSelector(props: IRangeSelectorProps) { const currentDoc = useObservable(currentDoc$); const docFocusing = currentDoc?.getUnitId() === editorId; + const clickOutside = useEvent((e: MouseEvent, cb: () => void) => { + if (rangeSelectorWrapRef.current && !rangeDialogVisible) { + const isContain = rangeSelectorWrapRef.current.contains(e.target as Node); + !isContain && cb(); + } + }); + // init actions if (actions) { - actions.handleOutClick = (e: MouseEvent, cb: () => void) => { - if (rangeSelectorWrapRef.current && !rangeDialogVisible) { - const isContain = rangeSelectorWrapRef.current.contains(e.target as Node); - !isContain && cb(); - } - }; + actions.handleOutClick = clickOutside; } const ranges = useMemo(() => { From c57b00a764368e3a2ed63867e6e1fc39b24f3174 Mon Sep 17 00:00:00 2001 From: zhangw Date: Wed, 25 Dec 2024 14:48:44 +0800 Subject: [PATCH 084/134] feat: update --- .../range-selector/hooks/useHighlight.ts | 47 +++++++++++++++---- 1 file changed, 37 insertions(+), 10 deletions(-) diff --git a/packages/sheets-formula-ui/src/views/range-selector/hooks/useHighlight.ts b/packages/sheets-formula-ui/src/views/range-selector/hooks/useHighlight.ts index 84757a58c46..a45f443f7e2 100644 --- a/packages/sheets-formula-ui/src/views/range-selector/hooks/useHighlight.ts +++ b/packages/sheets-formula-ui/src/views/range-selector/hooks/useHighlight.ts @@ -155,8 +155,6 @@ export function useDocHight(_leadingCharacter: string = '') { }); } - // const cloneData = { ...data, body: cloneBody }; - // editor.setDocumentData(cloneData, selections); commandService.syncExecuteCommand(ReplaceTextRunsCommand.id, { unitId: editorId, body: getBodySlice(cloneBody, 0, cloneBody.dataStream.length - 2), @@ -168,7 +166,14 @@ export function useDocHight(_leadingCharacter: string = '') { return highlightDoc; } -export function useColor() { +interface IColorMap { + formulaRefColors: string[]; + numberColor: string; + stringColor: string; + plainTextColor: string; +} + +export function useColor(): IColorMap { const themeService = useDependency(ThemeService); const style = themeService.getCurrentTheme(); const result = useMemo(() => { @@ -188,17 +193,15 @@ export function useColor() { ]; const numberColor = style.hyacinth700; const stringColor = style.verdancy800; - return { formulaRefColors, numberColor, stringColor }; + const plainTextColor = style.colorBlack; + return { formulaRefColors, numberColor, stringColor, plainTextColor }; }, [style]); return result; } -export function buildTextRuns(descriptionService: IDescriptionService, colorMap: { - formulaRefColors: string[]; - numberColor: string; - stringColor: string; -}, sequenceNodes: Array) { - const { formulaRefColors, numberColor, stringColor } = colorMap; +// eslint-disable-next-line max-lines-per-function +export function buildTextRuns(descriptionService: IDescriptionService, colorMap: IColorMap, sequenceNodes: Array) { + const { formulaRefColors, numberColor, stringColor, plainTextColor } = colorMap; const textRuns: ITextRun[] = []; const refSelections: IRefSelection[] = []; const themeColorMap = new Map(); @@ -213,10 +216,24 @@ export function buildTextRuns(descriptionService: IDescriptionService, colorMap: textRuns.push({ st: start, ed: end, + ts: { + cl: { + rgb: plainTextColor, + }, + }, }); continue; } if (descriptionService.hasDefinedNameDescription(node.token.trim())) { + textRuns.push({ + st: node.startIndex, + ed: node.endIndex + 1, + ts: { + cl: { + rgb: plainTextColor, + }, + }, + }); continue; } const { startIndex, endIndex, nodeType, token } = node; @@ -254,6 +271,16 @@ export function buildTextRuns(descriptionService: IDescriptionService, colorMap: }, }, }); + } else { + textRuns.push({ + st: startIndex, + ed: endIndex + 1, + ts: { + cl: { + rgb: plainTextColor, + }, + }, + }); } } From 8a9dbdc0b846fc10f389d225bcb67fa7802c2e37 Mon Sep 17 00:00:00 2001 From: zhangw Date: Wed, 25 Dec 2024 15:37:33 +0800 Subject: [PATCH 085/134] feat: update --- .../src/views/formula-editor/index.tsx | 8 +++----- .../editor/editing.render-controller.ts | 14 +++----------- .../services/editor/cell-editor-resize.service.ts | 13 +++---------- 3 files changed, 9 insertions(+), 26 deletions(-) diff --git a/packages/sheets-formula-ui/src/views/formula-editor/index.tsx b/packages/sheets-formula-ui/src/views/formula-editor/index.tsx index a4e4ad149b5..ba12ed8480c 100644 --- a/packages/sheets-formula-ui/src/views/formula-editor/index.tsx +++ b/packages/sheets-formula-ui/src/views/formula-editor/index.tsx @@ -290,11 +290,9 @@ export function FormulaEditor(props: IFormulaEditorProps) { setShouldMoveRefSelection(false); } - setTimeout(() => { - isFocusSet(true); - onFocus(); - focus(); - }, 30); + isFocusSet(true); + onFocus(); + focus(); }; return (
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 984015b50fd..ec1856daad3 100644 --- a/packages/sheets-ui/src/controllers/editor/editing.render-controller.ts +++ b/packages/sheets-ui/src/controllers/editor/editing.render-controller.ts @@ -354,7 +354,6 @@ export class EditingRenderController extends Disposable implements IRenderModule // You can double-click on the cell or input content by keyboard to put the cell into the edit state. private _handleEditorVisible(param: IEditorBridgeServiceVisibleParam) { const { eventType, keycode } = param; - // Change `CursorChange` to changed status, when formula bar clicked. this._cursorChange = (eventType === DeviceInputEventType.PointerDown || eventType === DeviceInputEventType.Dblclick) @@ -376,13 +375,7 @@ export class EditingRenderController extends Disposable implements IRenderModule }); this._editorBridgeService.refreshEditCellPosition(false); - - const { - documentLayoutObject, - editorUnitId, - unitId, - isInArrayFormulaRange = false, - } = editCellState; + const { unitId, isInArrayFormulaRange = false } = editCellState; const editorObject = this._getEditorObject(); if (editorObject == null) { @@ -392,9 +385,8 @@ export class EditingRenderController extends Disposable implements IRenderModule const { document, scene } = editorObject; this._contextService.setContextValue(EDITOR_ACTIVATED, true); - - const { documentModel: documentDataModel } = documentLayoutObject; - const skeleton = this._getEditorSkeleton(editorUnitId); + const documentDataModel = this._univerInstanceService.getUnit(DOCS_NORMAL_EDITOR_UNIT_ID_KEY, UniverInstanceType.UNIVER_DOC); + const skeleton = this._getEditorSkeleton(DOCS_NORMAL_EDITOR_UNIT_ID_KEY); if (!skeleton || !documentDataModel) { return; } diff --git a/packages/sheets-ui/src/services/editor/cell-editor-resize.service.ts b/packages/sheets-ui/src/services/editor/cell-editor-resize.service.ts index 255268f62fe..8058838f297 100644 --- a/packages/sheets-ui/src/services/editor/cell-editor-resize.service.ts +++ b/packages/sheets-ui/src/services/editor/cell-editor-resize.service.ts @@ -49,7 +49,7 @@ export class SheetCellEditorResizeService extends Disposable implements IRenderM super(); } - // eslint-disable-next-line complexity, max-lines-per-function + // eslint-disable-next-line complexity fitTextSize(callback?: () => void) { const param = this._editorBridgeService.getEditCellState(); if (!param) return; @@ -95,7 +95,8 @@ export class SheetCellEditorResizeService extends Disposable implements IRenderM offsetTop = (editorHeight - actualHeight) / 2 / scaleY; } else if (verticalAlign === VerticalAlign.TOP) { offsetTop = paddingData.t || 0; - } else { // VerticalAlign.UNSPECIFIED follow the same rule as HorizontalAlign.BOTTOM. + } else { + // VerticalAlign.UNSPECIFIED follow the same rule as HorizontalAlign.BOTTOM. offsetTop = (editorHeight - actualHeight) / scaleY; } @@ -115,10 +116,7 @@ export class SheetCellEditorResizeService extends Disposable implements IRenderM l: offsetLeft, }); - // re-calculate skeleton(viewModel for component) documentSkeleton.calculate(); - // editorWidth -= 1; - // editorHeight -= 1; this._editAreaProcessing( editorWidth, editorHeight, @@ -262,7 +260,6 @@ export class SheetCellEditorResizeService extends Disposable implements IRenderM const canvasElement = engine.getCanvasElement(); // We should take the scale into account when canvas is scaled by CSS. - let { startX, startY } = actualRangeWithCoord; const { document: documentComponent, scene: editorScene, engine: docEngine } = editorObject; @@ -295,10 +292,6 @@ export class SheetCellEditorResizeService extends Disposable implements IRenderM editorWidth = clientWidth; } - // move to fitTextSize - // startX -= FIX_ONE_PIXEL_BLUR_OFFSET; - // startY -= FIX_ONE_PIXEL_BLUR_OFFSET; - this._addBackground(editorScene, editorWidth / scaleX, editorHeight / scaleY, fill); const { scaleX: precisionScaleX, scaleY: precisionScaleY } = editorScene.getPrecisionScale(); From 38b1d7839c895c441b26f07c7a5c67767ee4b1da Mon Sep 17 00:00:00 2001 From: jocs Date: Wed, 25 Dec 2024 17:03:10 +0800 Subject: [PATCH 086/134] fix: no need to resize --- .../doc.render-controller.ts | 10 + .../src/components/docs/document.ts | 1 + packages/sheets/api-extractor.json | 454 ++++++++++++++++++ packages/sheets/build.js | 20 + packages/sheets/src/tsdoc-metadata.json | 11 + 5 files changed, 496 insertions(+) create mode 100644 packages/sheets/api-extractor.json create mode 100644 packages/sheets/build.js create mode 100644 packages/sheets/src/tsdoc-metadata.json 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 d5a51dee644..62026d5b8b2 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 @@ -178,6 +178,16 @@ export class DocRenderController extends RxDisposable implements IRenderModule { docsComponent.changeSkeleton(skeleton); docBackground.changeSkeleton(skeleton); + const { unitId } = this._context; + + // 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; + } + this._recalculateSizeBySkeleton(skeleton); } diff --git a/packages/engine-render/src/components/docs/document.ts b/packages/engine-render/src/components/docs/document.ts index 0d0484bda80..6f4e756956e 100644 --- a/packages/engine-render/src/components/docs/document.ts +++ b/packages/engine-render/src/components/docs/document.ts @@ -177,6 +177,7 @@ export class Documents extends DocComponent { pagePaddingBottom, verticalAlign ); + const alignOffsetNoAngle = Vector2.create(horizontalOffsetNoAngle, verticalOffsetNoAngle); const centerAngle = degToRad(centerAngleDeg); const vertexAngle = degToRad(vertexAngleDeg); diff --git a/packages/sheets/api-extractor.json b/packages/sheets/api-extractor.json new file mode 100644 index 00000000000..1f20c772b0c --- /dev/null +++ b/packages/sheets/api-extractor.json @@ -0,0 +1,454 @@ +/** + * Config file for API Extractor. For more info, please visit: https://api-extractor.com + */ +{ + "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", + + /** + * Optionally specifies another JSON config file that this file extends from. This provides a way for + * standard settings to be shared across multiple projects. + * + * If the path starts with "./" or "../", the path is resolved relative to the folder of the file that contains + * the "extends" field. Otherwise, the first path segment is interpreted as an NPM package name, and will be + * resolved using NodeJS require(). + * + * SUPPORTED TOKENS: none + * DEFAULT VALUE: "" + */ + // "extends": "./shared/api-extractor-base.json" + // "extends": "my-package/include/api-extractor-base.json" + + /** + * Determines the "" token that can be used with other config file settings. The project folder + * typically contains the tsconfig.json and package.json config files, but the path is user-defined. + * + * The path is resolved relative to the folder of the config file that contains the setting. + * + * The default value for "projectFolder" is the token "", which means the folder is determined by traversing + * parent folders, starting from the folder containing api-extractor.json, and stopping at the first folder + * that contains a tsconfig.json file. If a tsconfig.json file cannot be found in this way, then an error + * will be reported. + * + * SUPPORTED TOKENS: + * DEFAULT VALUE: "" + */ + // "projectFolder": "..", + + /** + * (REQUIRED) Specifies the .d.ts file to be used as the starting point for analysis. API Extractor + * analyzes the symbols exported by this module. + * + * The file extension must be ".d.ts" and not ".ts". + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * SUPPORTED TOKENS: , , + */ + "mainEntryPointFilePath": "./lib/types/facade/f-selection.d.ts", + + /** + * A list of NPM package names whose exports should be treated as part of this package. + * + * For example, suppose that Webpack is used to generate a distributed bundle for the project "library1", + * and another NPM package "library2" is embedded in this bundle. Some types from library2 may become part + * of the exported API for library1, but by default API Extractor would generate a .d.ts rollup that explicitly + * imports library2. To avoid this, we might specify: + * + * "bundledPackages": [ "library2" ], + * + * This would direct API Extractor to embed those types directly in the .d.ts rollup, as if they had been + * local files for library1. + * + * The "bundledPackages" elements may specify glob patterns using minimatch syntax. To ensure deterministic + * output, globs are expanded by matching explicitly declared top-level dependencies only. For example, + * the pattern below will NOT match "@my-company/example" unless it appears in a field such as "dependencies" + * or "devDependencies" of the project's package.json file: + * + * "bundledPackages": [ "@my-company/*" ], + */ + "bundledPackages": [], + + /** + * Specifies what type of newlines API Extractor should use when writing output files. By default, the output files + * will be written with Windows-style newlines. To use POSIX-style newlines, specify "lf" instead. + * To use the OS's default newline kind, specify "os". + * + * DEFAULT VALUE: "crlf" + */ + // "newlineKind": "crlf", + + /** + * Specifies how API Extractor sorts members of an enum when generating the .api.json file. By default, the output + * files will be sorted alphabetically, which is "by-name". To keep the ordering in the source code, specify + * "preserve". + * + * DEFAULT VALUE: "by-name" + */ + // "enumMemberOrder": "by-name", + + /** + * Set to true when invoking API Extractor's test harness. When `testMode` is true, the `toolVersion` field in the + * .api.json file is assigned an empty string to prevent spurious diffs in output files tracked for tests. + * + * DEFAULT VALUE: "false" + */ + // "testMode": false, + + /** + * Determines how the TypeScript compiler engine will be invoked by API Extractor. + */ + "compiler": { + /** + * Specifies the path to the tsconfig.json file to be used by API Extractor when analyzing the project. + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * Note: This setting will be ignored if "overrideTsconfig" is used. + * + * SUPPORTED TOKENS: , , + * DEFAULT VALUE: "/tsconfig.json" + */ + // "tsconfigFilePath": "/tsconfig.json", + /** + * Provides a compiler configuration that will be used instead of reading the tsconfig.json file from disk. + * The object must conform to the TypeScript tsconfig schema: + * + * http://json.schemastore.org/tsconfig + * + * If omitted, then the tsconfig.json file will be read from the "projectFolder". + * + * DEFAULT VALUE: no overrideTsconfig section + */ + // "overrideTsconfig": { + // . . . + // } + /** + * This option causes the compiler to be invoked with the --skipLibCheck option. This option is not recommended + * and may cause API Extractor to produce incomplete or incorrect declarations, but it may be required when + * dependencies contain declarations that are incompatible with the TypeScript engine that API Extractor uses + * for its analysis. Where possible, the underlying issue should be fixed rather than relying on skipLibCheck. + * + * DEFAULT VALUE: false + */ + // "skipLibCheck": true, + }, + + /** + * Configures how the API report file (*.api.md) will be generated. + */ + "apiReport": { + /** + * (REQUIRED) Whether to generate an API report. + */ + "enabled": false + + /** + * The base filename for the API report files, to be combined with "reportFolder" or "reportTempFolder" + * to produce the full file path. The "reportFileName" should not include any path separators such as + * "\" or "/". The "reportFileName" should not include a file extension, since API Extractor will automatically + * append an appropriate file extension such as ".api.md". If the "reportVariants" setting is used, then the + * file extension includes the variant name, for example "my-report.public.api.md" or "my-report.beta.api.md". + * The "complete" variant always uses the simple extension "my-report.api.md". + * + * Previous versions of API Extractor required "reportFileName" to include the ".api.md" extension explicitly; + * for backwards compatibility, that is still accepted but will be discarded before applying the above rules. + * + * SUPPORTED TOKENS: , + * DEFAULT VALUE: "" + */ + // "reportFileName": "", + + /** + * To support different approval requirements for different API levels, multiple "variants" of the API report can + * be generated. The "reportVariants" setting specifies a list of variants to be generated. If omitted, + * by default only the "complete" variant will be generated, which includes all @internal, @alpha, @beta, + * and @public items. Other possible variants are "alpha" (@alpha + @beta + @public), "beta" (@beta + @public), + * and "public" (@public only). + * + * DEFAULT VALUE: [ "complete" ] + */ + // "reportVariants": ["public", "beta"], + + /** + * Specifies the folder where the API report file is written. The file name portion is determined by + * the "reportFileName" setting. + * + * The API report file is normally tracked by Git. Changes to it can be used to trigger a branch policy, + * e.g. for an API review. + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * SUPPORTED TOKENS: , , + * DEFAULT VALUE: "/etc/" + */ + // "reportFolder": "/etc/", + + /** + * Specifies the folder where the temporary report file is written. The file name portion is determined by + * the "reportFileName" setting. + * + * After the temporary file is written to disk, it is compared with the file in the "reportFolder". + * If they are different, a production build will fail. + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * SUPPORTED TOKENS: , , + * DEFAULT VALUE: "/temp/" + */ + // "reportTempFolder": "/temp/", + + /** + * Whether "forgotten exports" should be included in the API report file. Forgotten exports are declarations + * flagged with `ae-forgotten-export` warnings. See https://api-extractor.com/pages/messages/ae-forgotten-export/ to + * learn more. + * + * DEFAULT VALUE: "false" + */ + // "includeForgottenExports": false + }, + + /** + * Configures how the doc model file (*.api.json) will be generated. + */ + "docModel": { + /** + * (REQUIRED) Whether to generate a doc model file. + */ + "enabled": true + + /** + * The output path for the doc model file. The file extension should be ".api.json". + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * SUPPORTED TOKENS: , , + * DEFAULT VALUE: "/temp/.api.json" + */ + // "apiJsonFilePath": "/temp/.api.json", + + /** + * Whether "forgotten exports" should be included in the doc model file. Forgotten exports are declarations + * flagged with `ae-forgotten-export` warnings. See https://api-extractor.com/pages/messages/ae-forgotten-export/ to + * learn more. + * + * DEFAULT VALUE: "false" + */ + // "includeForgottenExports": false, + + /** + * The base URL where the project's source code can be viewed on a website such as GitHub or + * Azure DevOps. This URL path corresponds to the `` path on disk. + * + * This URL is concatenated with the file paths serialized to the doc model to produce URL file paths to individual API items. + * For example, if the `projectFolderUrl` is "https://github.com/microsoft/rushstack/tree/main/apps/api-extractor" and an API + * item's file path is "api/ExtractorConfig.ts", the full URL file path would be + * "https://github.com/microsoft/rushstack/tree/main/apps/api-extractor/api/ExtractorConfig.js". + * + * This setting can be omitted if you don't need source code links in your API documentation reference. + * + * SUPPORTED TOKENS: none + * DEFAULT VALUE: "" + */ + // "projectFolderUrl": "http://github.com/path/to/your/projectFolder" + }, + + /** + * Configures how the .d.ts rollup file will be generated. + */ + "dtsRollup": { + /** + * (REQUIRED) Whether to generate the .d.ts rollup file. + */ + "enabled": true + + /** + * Specifies the output path for a .d.ts rollup file to be generated without any trimming. + * This file will include all declarations that are exported by the main entry point. + * + * If the path is an empty string, then this file will not be written. + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * SUPPORTED TOKENS: , , + * DEFAULT VALUE: "/dist/.d.ts" + */ + // "untrimmedFilePath": "/dist/.d.ts", + + /** + * Specifies the output path for a .d.ts rollup file to be generated with trimming for an "alpha" release. + * This file will include only declarations that are marked as "@public", "@beta", or "@alpha". + * + * If the path is an empty string, then this file will not be written. + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * SUPPORTED TOKENS: , , + * DEFAULT VALUE: "" + */ + // "alphaTrimmedFilePath": "/dist/-alpha.d.ts", + + /** + * Specifies the output path for a .d.ts rollup file to be generated with trimming for a "beta" release. + * This file will include only declarations that are marked as "@public" or "@beta". + * + * If the path is an empty string, then this file will not be written. + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * SUPPORTED TOKENS: , , + * DEFAULT VALUE: "" + */ + // "betaTrimmedFilePath": "/dist/-beta.d.ts", + + /** + * Specifies the output path for a .d.ts rollup file to be generated with trimming for a "public" release. + * This file will include only declarations that are marked as "@public". + * + * If the path is an empty string, then this file will not be written. + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * SUPPORTED TOKENS: , , + * DEFAULT VALUE: "" + */ + // "publicTrimmedFilePath": "/dist/-public.d.ts", + + /** + * When a declaration is trimmed, by default it will be replaced by a code comment such as + * "Excluded from this release type: exampleMember". Set "omitTrimmingComments" to true to remove the + * declaration completely. + * + * DEFAULT VALUE: false + */ + // "omitTrimmingComments": true + }, + + /** + * Configures how the tsdoc-metadata.json file will be generated. + */ + "tsdocMetadata": { + /** + * Whether to generate the tsdoc-metadata.json file. + * + * DEFAULT VALUE: true + */ + // "enabled": true, + /** + * Specifies where the TSDoc metadata file should be written. + * + * The path is resolved relative to the folder of the config file that contains the setting; to change this, + * prepend a folder token such as "". + * + * The default value is "", which causes the path to be automatically inferred from the "tsdocMetadata", + * "typings" or "main" fields of the project's package.json. If none of these fields are set, the lookup + * falls back to "tsdoc-metadata.json" in the package folder. + * + * SUPPORTED TOKENS: , , + * DEFAULT VALUE: "" + */ + // "tsdocMetadataFilePath": "/dist/tsdoc-metadata.json" + }, + + /** + * Configures how API Extractor reports error and warning messages produced during analysis. + * + * There are three sources of messages: compiler messages, API Extractor messages, and TSDoc messages. + */ + "messages": { + /** + * Configures handling of diagnostic messages reported by the TypeScript compiler engine while analyzing + * the input .d.ts files. + * + * TypeScript message identifiers start with "TS" followed by an integer. For example: "TS2551" + * + * DEFAULT VALUE: A single "default" entry with logLevel=warning. + */ + "compilerMessageReporting": { + /** + * Configures the default routing for messages that don't match an explicit rule in this table. + */ + "default": { + /** + * Specifies whether the message should be written to the the tool's output log. Note that + * the "addToApiReportFile" property may supersede this option. + * + * Possible values: "error", "warning", "none" + * + * Errors cause the build to fail and return a nonzero exit code. Warnings cause a production build fail + * and return a nonzero exit code. For a non-production build (e.g. when "api-extractor run" includes + * the "--local" option), the warning is displayed but the build will not fail. + * + * DEFAULT VALUE: "warning" + */ + "logLevel": "warning" + + /** + * When addToApiReportFile is true: If API Extractor is configured to write an API report file (.api.md), + * then the message will be written inside that file; otherwise, the message is instead logged according to + * the "logLevel" option. + * + * DEFAULT VALUE: false + */ + // "addToApiReportFile": false + } + + // "TS2551": { + // "logLevel": "warning", + // "addToApiReportFile": true + // }, + // + // . . . + }, + + /** + * Configures handling of messages reported by API Extractor during its analysis. + * + * API Extractor message identifiers start with "ae-". For example: "ae-extra-release-tag" + * + * DEFAULT VALUE: See api-extractor-defaults.json for the complete table of extractorMessageReporting mappings + */ + "extractorMessageReporting": { + "default": { + "logLevel": "warning" + // "addToApiReportFile": false + } + + // "ae-extra-release-tag": { + // "logLevel": "warning", + // "addToApiReportFile": true + // }, + // + // . . . + }, + + /** + * Configures handling of messages reported by the TSDoc parser when analyzing code comments. + * + * TSDoc message identifiers start with "tsdoc-". For example: "tsdoc-link-tag-unescaped-text" + * + * DEFAULT VALUE: A single "default" entry with logLevel=warning. + */ + "tsdocMessageReporting": { + "default": { + "logLevel": "warning" + // "addToApiReportFile": false + } + + // "tsdoc-link-tag-unescaped-text": { + // "logLevel": "warning", + // "addToApiReportFile": true + // }, + // + // . . . + } + } +} diff --git a/packages/sheets/build.js b/packages/sheets/build.js new file mode 100644 index 00000000000..97500f1edf7 --- /dev/null +++ b/packages/sheets/build.js @@ -0,0 +1,20 @@ +const tsc = require('tsc-prog'); + +tsc.build({ + basePath: __dirname, // always required, used for relative paths + configFilePath: 'tsconfig.json', // config to inherit from (optional) + compilerOptions: { + rootDir: 'src', + outDir: 'dist', + declaration: true, + skipLibCheck: true, + }, + bundleDeclaration: { + entryPoint: './facade/index.d.ts', // relative to the OUTPUT directory ('dist' here) + fallbackOnError: false, // default: true + globals: false, // default: true + augmentations: false, // default: true + }, + include: ['src/**/*'], + exclude: ['**/*.test.ts', '**/*.spec.ts'], +}); diff --git a/packages/sheets/src/tsdoc-metadata.json b/packages/sheets/src/tsdoc-metadata.json new file mode 100644 index 00000000000..4c59070de4d --- /dev/null +++ b/packages/sheets/src/tsdoc-metadata.json @@ -0,0 +1,11 @@ +// This file is read by tools that parse documentation comments conforming to the TSDoc standard. +// It should be published with your NPM package. It should not be tracked by Git. +{ + "tsdocVersion": "0.12", + "toolPackages": [ + { + "packageName": "@microsoft/api-extractor", + "packageVersion": "7.48.0" + } + ] +} From d0ce17c872f2e2dd65945734cce0d14da0e7808a Mon Sep 17 00:00:00 2001 From: zhangw Date: Wed, 25 Dec 2024 17:44:20 +0800 Subject: [PATCH 087/134] feat: update --- .../doc-editor-bridge.controller.ts | 33 ++--------------- .../docs-ui/src/services/editor/editor.ts | 9 ----- .../src/views/rich-text-editor/index.tsx | 1 - .../src/views/formula-editor/index.tsx | 6 ++-- .../views/range-selector/hooks/useResize.ts | 34 ++++++++++++------ .../src/views/range-selector/index.tsx | 1 - .../editor/cell-editor-resize.service.ts | 36 +++++++++---------- .../editor-container/EditorContainer.tsx | 2 +- .../src/views/formula-bar/FormulaBar.tsx | 2 +- 9 files changed, 51 insertions(+), 73 deletions(-) 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 08a485006ea..796fa68f996 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 @@ -16,10 +16,10 @@ import type { DocumentDataModel, ICommandInfo, Nullable, Workbook } from '@univerjs/core'; import type { IRichTextEditingMutationParams } from '@univerjs/docs'; -import type { IRenderContext, IRenderModule } from '@univerjs/engine-render'; +import type { IRenderContext, IRenderModule, ScrollBar } from '@univerjs/engine-render'; import { checkForSubstrings, Disposable, ICommandService, Inject, IUniverInstanceService, UniverInstanceType } from '@univerjs/core'; import { DocSkeletonManagerService, RichTextEditingMutation } from '@univerjs/docs'; -import { IRenderManagerService, ScrollBar } from '@univerjs/engine-render'; +import { IRenderManagerService } from '@univerjs/engine-render'; import { fromEvent } from 'rxjs'; import { VIEWPORT_KEY } from '../../basics/docs-view-key'; import { IEditorService } from '../../services/editor/editor-manager.service'; @@ -65,7 +65,6 @@ export class DocEditorBridgeController extends Disposable implements IRenderModu this._initialFocus(); } - // eslint-disable-next-line complexity private _resize(unitId: Nullable) { if (unitId == null) { return; @@ -103,7 +102,7 @@ export class DocEditorBridgeController extends Disposable implements IRenderModu const viewportMain = scene.getViewport(VIEWPORT_KEY.VIEW_MAIN); - let scrollBar = viewportMain?.getScrollBar() as Nullable; + const scrollBar = viewportMain?.getScrollBar() as Nullable; const contentWidth = Math.max(actualWidth, width); @@ -115,32 +114,6 @@ export class DocEditorBridgeController extends Disposable implements IRenderModu }); mainComponent?.resize(contentWidth, contentHeight); - - if (!editor.isSingle()) { - if (actualHeight > height) { - if (scrollBar == null) { - viewportMain && new ScrollBar(viewportMain, { enableHorizontal: false, barSize: 8 }); - } else { - viewportMain?.resetCanvasSizeAndUpdateScroll(); - } - } else { - scrollBar = null; - viewportMain?.scrollToBarPos({ x: 0, y: 0 }); - viewportMain?.getScrollBar()?.dispose(); - } - } else { - if (actualWidth > width) { - if (scrollBar == null) { - viewportMain && new ScrollBar(viewportMain, { barSize: 8, enableVertical: false }); - } else { - viewportMain?.resetCanvasSizeAndUpdateScroll(); - } - } else { - scrollBar = null; - viewportMain?.scrollToBarPos({ x: 0, y: 0 }); - viewportMain?.getScrollBar()?.dispose(); - } - } } private _initialSetValue() { diff --git a/packages/docs-ui/src/services/editor/editor.ts b/packages/docs-ui/src/services/editor/editor.ts index 5558c53d30f..709880b86d6 100644 --- a/packages/docs-ui/src/services/editor/editor.ts +++ b/packages/docs-ui/src/services/editor/editor.ts @@ -99,11 +99,6 @@ export interface IEditorConfigParams { // show scrollBar scrollBar?: boolean; - - /** - * show text on single line - */ - isSingle?: boolean; } export interface IEditorOptions extends IEditorConfigParams, IEditorStateParams { @@ -379,10 +374,6 @@ export class Editor extends Disposable implements IEditor { this._focus = state; } - isSingle() { - return this._param.isSingle === true; - } - isReadOnly() { return this._param.readonly === true; } diff --git a/packages/docs-ui/src/views/rich-text-editor/index.tsx b/packages/docs-ui/src/views/rich-text-editor/index.tsx index 2c95f6ccf67..46cb664181e 100644 --- a/packages/docs-ui/src/views/rich-text-editor/index.tsx +++ b/packages/docs-ui/src/views/rich-text-editor/index.tsx @@ -84,7 +84,6 @@ export const RichTextEditor = (props: IRichTextEditorProps) => { dispose = editorService.register({ autofocus: true, editorUnitId: editorId, - isSingle: true, initialSnapshot: { body: { dataStream: '\r\n', diff --git a/packages/sheets-formula-ui/src/views/formula-editor/index.tsx b/packages/sheets-formula-ui/src/views/formula-editor/index.tsx index ba12ed8480c..f7f6cf3832a 100644 --- a/packages/sheets-formula-ui/src/views/formula-editor/index.tsx +++ b/packages/sheets-formula-ui/src/views/formula-editor/index.tsx @@ -67,6 +67,7 @@ export interface IFormulaEditorProps { keyboradEventConfig?: IKeyboardEventConfig; onMoveInEditor?: (keyCode: KeyCode, metaKey?: MetaKeys) => void; resetSelectionOnBlur?: boolean; + isSingle?: boolean; autoScrollbar?: boolean; } @@ -92,6 +93,7 @@ export function FormulaEditor(props: IFormulaEditorProps) { onMoveInEditor, resetSelectionOnBlur = true, autoScrollbar = true, + isSingle = true, } = props; const editorService = useDependency(IEditorService); @@ -195,7 +197,7 @@ export function FormulaEditor(props: IFormulaEditorProps) { dispose = editorService.register({ autofocus: true, editorUnitId: editorId, - isSingle: true, + isSingle, initialSnapshot: { id: editorId, body: { @@ -231,7 +233,7 @@ export function FormulaEditor(props: IFormulaEditorProps) { } }, [_isFocus, editor, focus, resetSelection, resetSelectionOnBlur]); - const { checkScrollBar } = useResize(editor, autoScrollbar); + const { checkScrollBar } = useResize(editor, isSingle, autoScrollbar); useRefactorEffect(isFocus, Boolean(isSelecting && docFocusing), unitId); useLeftAndRightArrow(isFocus && moveCursor, shouldMoveRefSelection, editor, onMoveInEditor); diff --git a/packages/sheets-formula-ui/src/views/range-selector/hooks/useResize.ts b/packages/sheets-formula-ui/src/views/range-selector/hooks/useResize.ts index c542676eafb..74afcfe00d8 100644 --- a/packages/sheets-formula-ui/src/views/range-selector/hooks/useResize.ts +++ b/packages/sheets-formula-ui/src/views/range-selector/hooks/useResize.ts @@ -22,7 +22,7 @@ import { ScrollBar } from '@univerjs/engine-render'; import { useEffect, useMemo } from 'react'; // eslint-disable-next-line max-lines-per-function -export const useResize = (editor?: Editor, autoScrollbar?: boolean) => { +export const useResize = (editor?: Editor, isSingle = true, autoScrollbar?: boolean) => { const resize = () => { if (editor) { const { scene, mainComponent } = editor.render; @@ -44,11 +44,12 @@ export const useResize = (editor?: Editor, autoScrollbar?: boolean) => { if (!editor || !autoScrollbar) { return; } + const docSkeletonManagerService = editor.render.with(DocSkeletonManagerService); const skeleton = docSkeletonManagerService.getSkeleton(); const { scene, mainComponent } = editor.render; const viewportMain = scene.getViewport(VIEWPORT_KEY.VIEW_MAIN); - const { actualWidth } = skeleton.getActualSize(); + const { actualWidth, actualHeight } = skeleton.getActualSize(); const { width, height } = editor.getBoundingClientRect(); let scrollBar = viewportMain?.getScrollBar() as Nullable; const contentWidth = Math.max(actualWidth, width); @@ -61,17 +62,30 @@ export const useResize = (editor?: Editor, autoScrollbar?: boolean) => { }); mainComponent?.resize(contentWidth, contentHeight); - - if (actualWidth > width) { - if (scrollBar == null) { - viewportMain && new ScrollBar(viewportMain, { barSize: 8, enableVertical: false }); + if (!isSingle) { + if (actualHeight > height) { + if (scrollBar == null) { + viewportMain && new ScrollBar(viewportMain, { enableHorizontal: false, barSize: 8 }); + } else { + viewportMain?.resetCanvasSizeAndUpdateScroll(); + } } else { - viewportMain?.resetCanvasSizeAndUpdateScroll(); + scrollBar = null; + viewportMain?.scrollToBarPos({ x: 0, y: 0 }); + viewportMain?.getScrollBar()?.dispose(); } } else { - scrollBar = null; - viewportMain?.scrollToBarPos({ x: 0, y: 0 }); - viewportMain?.getScrollBar()?.dispose(); + if (actualWidth > width) { + if (scrollBar == null) { + viewportMain && new ScrollBar(viewportMain, { barSize: 8, enableVertical: false }); + } else { + viewportMain?.resetCanvasSizeAndUpdateScroll(); + } + } else { + scrollBar = null; + viewportMain?.scrollToBarPos({ x: 0, y: 0 }); + viewportMain?.getScrollBar()?.dispose(); + } } }, 30); }, [editor, autoScrollbar]); diff --git a/packages/sheets-formula-ui/src/views/range-selector/index.tsx b/packages/sheets-formula-ui/src/views/range-selector/index.tsx index b24f2ee819c..8c6b10bfd41 100644 --- a/packages/sheets-formula-ui/src/views/range-selector/index.tsx +++ b/packages/sheets-formula-ui/src/views/range-selector/index.tsx @@ -276,7 +276,6 @@ export function RangeSelector(props: IRangeSelectorProps) { dispose = editorService.register({ autofocus: true, editorUnitId: editorId, - isSingle: true, initialSnapshot: { id: editorId, body: { dataStream: `${rangeString}\r\n`, textRuns: [] }, diff --git a/packages/sheets-ui/src/services/editor/cell-editor-resize.service.ts b/packages/sheets-ui/src/services/editor/cell-editor-resize.service.ts index 8058838f297..e6eb1cb26ea 100644 --- a/packages/sheets-ui/src/services/editor/cell-editor-resize.service.ts +++ b/packages/sheets-ui/src/services/editor/cell-editor-resize.service.ts @@ -18,8 +18,8 @@ import type { DocumentDataModel, IPosition, Nullable, Workbook } from '@univerjs import type { DocumentSkeleton, IDocumentLayoutObject, IRenderContext, IRenderModule, Scene } from '@univerjs/engine-render'; import { Disposable, DOCS_NORMAL_EDITOR_UNIT_ID_KEY, HorizontalAlign, Inject, IUniverInstanceService, UniverInstanceType, VerticalAlign, WrapStrategy } from '@univerjs/core'; import { DocSkeletonManagerService } from '@univerjs/docs'; -import { VIEWPORT_KEY as DOC_VIEWPORT_KEY, DOCS_COMPONENT_MAIN_LAYER_INDEX } from '@univerjs/docs-ui'; -import { convertTextRotation, fixLineWidthByScale, IRenderManagerService, Rect, ScrollBar } from '@univerjs/engine-render'; +import { DOCS_COMPONENT_MAIN_LAYER_INDEX } from '@univerjs/docs-ui'; +import { convertTextRotation, fixLineWidthByScale, IRenderManagerService, Rect } from '@univerjs/engine-render'; import { ILayoutService } from '@univerjs/ui'; import { getEditorObject } from '../../basics/editor/get-editor-object'; import styles from '../../views/sheet-container/index.module.less'; @@ -229,7 +229,7 @@ export class SheetCellEditorResizeService extends Disposable implements IRenderM return { height: clientHeight, - width: clientWidth, + width: clientWidth - EDITOR_BORDER_SIZE, scaleAdjust, }; } @@ -263,30 +263,30 @@ export class SheetCellEditorResizeService extends Disposable implements IRenderM let { startX, startY } = actualRangeWithCoord; const { document: documentComponent, scene: editorScene, engine: docEngine } = editorObject; - const viewportMain = editorScene.getViewport(DOC_VIEWPORT_KEY.VIEW_MAIN); + // const viewportMain = editorScene.getViewport(DOC_VIEWPORT_KEY.VIEW_MAIN); const info = this._getEditorMaxSize(actualRangeWithCoord, canvasOffset, horizontalAlign); if (!info) return; const { height: clientHeight, width: clientWidth, scaleAdjust } = info; - let physicHeight = editorHeight; + const physicHeight = editorHeight; - let scrollBar = viewportMain?.getScrollBar() as Nullable; + // let scrollBar = viewportMain?.getScrollBar() as Nullable; - if (physicHeight > clientHeight) { - physicHeight = clientHeight; + // if (physicHeight > clientHeight) { + // physicHeight = clientHeight; - if (scrollBar == null) { - viewportMain && new ScrollBar(viewportMain, { enableHorizontal: false, barSize: 8 }); - } else { - viewportMain?.resetCanvasSizeAndUpdateScroll(); - } - } else { - scrollBar = null; - viewportMain?.getScrollBar()?.dispose(); - } + // if (scrollBar == null) { + // viewportMain && new ScrollBar(viewportMain, { enableHorizontal: false, barSize: 8 }); + // } else { + // viewportMain?.resetCanvasSizeAndUpdateScroll(); + // } + // } else { + // scrollBar = null; + // viewportMain?.getScrollBar()?.dispose(); + // } - editorWidth += scrollBar?.barSize || 0; + // editorWidth += scrollBar?.barSize || 0; if (editorWidth > clientWidth) { editorWidth = clientWidth; diff --git a/packages/sheets-ui/src/views/editor-container/EditorContainer.tsx b/packages/sheets-ui/src/views/editor-container/EditorContainer.tsx index e9f63ec32d6..70741c4a33d 100644 --- a/packages/sheets-ui/src/views/editor-container/EditorContainer.tsx +++ b/packages/sheets-ui/src/views/editor-container/EditorContainer.tsx @@ -161,6 +161,7 @@ export const EditorContainer: React.FC = () => { onMoveInEditor={onMoveInEditor} isSupportAcrossSheet resetSelectionOnBlur={false} + isSingle={false} onFormulaSelectingChange={(isSelecting: 0 | 1 | 2) => { isRefSelecting.current = isSelecting; if (isSelecting) { @@ -169,7 +170,6 @@ export const EditorContainer: React.FC = () => { editorBridgeService.disableForceKeepVisible(); } }} - autoScrollbar={false} /> )}
diff --git a/packages/sheets-ui/src/views/formula-bar/FormulaBar.tsx b/packages/sheets-ui/src/views/formula-bar/FormulaBar.tsx index 231c915a118..3aa990c37f0 100644 --- a/packages/sheets-ui/src/views/formula-bar/FormulaBar.tsx +++ b/packages/sheets-ui/src/views/formula-bar/FormulaBar.tsx @@ -229,7 +229,7 @@ export function FormulaBar() { subUnitId={editState?.sheetId} isSupportAcrossSheet resetSelectionOnBlur={false} - autoScrollbar={false} + isSingle={false} keyboradEventConfig={keyCodeConfig} onFormulaSelectingChange={(isSelecting: 0 | 1 | 2) => { isRefSelecting.current = isSelecting; From 1cd5d3ab3b5058626f5e6976657becd683eeae60 Mon Sep 17 00:00:00 2001 From: zhangw Date: Wed, 25 Dec 2024 21:20:00 +0800 Subject: [PATCH 088/134] feat: update --- .../commands/replace-content.command.ts | 2 +- .../editor/editing.render-controller.ts | 2 +- .../editor/cell-editor-resize.service.ts | 39 ++++++++++--------- .../editor-container/EditorContainer.tsx | 1 + 4 files changed, 24 insertions(+), 20 deletions(-) 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 97318b5a5dc..d88e8bda45a 100644 --- a/packages/docs-ui/src/commands/commands/replace-content.command.ts +++ b/packages/docs-ui/src/commands/commands/replace-content.command.ts @@ -46,7 +46,7 @@ export const ReplaceSnapshotCommand: ICommand = { return false; } - const { body, tableSource, footers, headers, lists, drawings, drawingsOrder, documentStyle } = snapshot; + const { body, tableSource, footers, headers, lists, drawings, drawingsOrder, documentStyle } = Tools.deepClone(snapshot); const { body: prevBody, tableSource: prevTableSource, 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 ec1856daad3..bed0ce4b2f6 100644 --- a/packages/sheets-ui/src/controllers/editor/editing.render-controller.ts +++ b/packages/sheets-ui/src/controllers/editor/editing.render-controller.ts @@ -268,7 +268,7 @@ export class EditingRenderController extends Disposable implements IRenderModule this._commandService.syncExecuteCommand(ReplaceSnapshotCommand.id, { unitId: editorUnitId, - snapshot: Tools.deepClone(documentModel!.getSnapshot()), + snapshot: (documentModel!.getSnapshot()), }); this._contextService.setContextValue(FOCUSING_EDITOR_BUT_HIDDEN, true); diff --git a/packages/sheets-ui/src/services/editor/cell-editor-resize.service.ts b/packages/sheets-ui/src/services/editor/cell-editor-resize.service.ts index e6eb1cb26ea..7c39a00cb9f 100644 --- a/packages/sheets-ui/src/services/editor/cell-editor-resize.service.ts +++ b/packages/sheets-ui/src/services/editor/cell-editor-resize.service.ts @@ -18,8 +18,8 @@ import type { DocumentDataModel, IPosition, Nullable, Workbook } from '@univerjs import type { DocumentSkeleton, IDocumentLayoutObject, IRenderContext, IRenderModule, Scene } from '@univerjs/engine-render'; import { Disposable, DOCS_NORMAL_EDITOR_UNIT_ID_KEY, HorizontalAlign, Inject, IUniverInstanceService, UniverInstanceType, VerticalAlign, WrapStrategy } from '@univerjs/core'; import { DocSkeletonManagerService } from '@univerjs/docs'; -import { DOCS_COMPONENT_MAIN_LAYER_INDEX } from '@univerjs/docs-ui'; -import { convertTextRotation, fixLineWidthByScale, IRenderManagerService, Rect } from '@univerjs/engine-render'; +import { DOCS_COMPONENT_MAIN_LAYER_INDEX, VIEWPORT_KEY } from '@univerjs/docs-ui'; +import { convertTextRotation, fixLineWidthByScale, IRenderManagerService, Rect, ScrollBar } from '@univerjs/engine-render'; import { ILayoutService } from '@univerjs/ui'; import { getEditorObject } from '../../basics/editor/get-editor-object'; import styles from '../../views/sheet-container/index.module.less'; @@ -147,7 +147,7 @@ export class SheetCellEditorResizeService extends Disposable implements IRenderM const { textRotation, wrapStrategy } = documentLayoutObject; - const documentDataModel = documentLayoutObject.documentModel; + const documentDataModel = this._univerInstanceService.getUnit(DOCS_NORMAL_EDITOR_UNIT_ID_KEY, UniverInstanceType.UNIVER_DOC); const { vertexAngle: angle } = convertTextRotation(textRotation); @@ -263,30 +263,33 @@ export class SheetCellEditorResizeService extends Disposable implements IRenderM let { startX, startY } = actualRangeWithCoord; const { document: documentComponent, scene: editorScene, engine: docEngine } = editorObject; - // const viewportMain = editorScene.getViewport(DOC_VIEWPORT_KEY.VIEW_MAIN); + const viewportMain = editorScene.getViewport(VIEWPORT_KEY.VIEW_MAIN); const info = this._getEditorMaxSize(actualRangeWithCoord, canvasOffset, horizontalAlign); if (!info) return; const { height: clientHeight, width: clientWidth, scaleAdjust } = info; - const physicHeight = editorHeight; + let physicHeight = editorHeight; - // let scrollBar = viewportMain?.getScrollBar() as Nullable; + let scrollBar = viewportMain?.getScrollBar() as Nullable; - // if (physicHeight > clientHeight) { - // physicHeight = clientHeight; + if (physicHeight > clientHeight) { + if (scrollBar == null) { + viewportMain && new ScrollBar(viewportMain, { enableHorizontal: false, barSize: 8 }); + } else { + viewportMain?.resetCanvasSizeAndUpdateScroll(); + } + viewportMain?.scrollToViewportPos({ + viewportScrollY: physicHeight - clientHeight, + }); - // if (scrollBar == null) { - // viewportMain && new ScrollBar(viewportMain, { enableHorizontal: false, barSize: 8 }); - // } else { - // viewportMain?.resetCanvasSizeAndUpdateScroll(); - // } - // } else { - // scrollBar = null; - // viewportMain?.getScrollBar()?.dispose(); - // } + physicHeight = clientHeight; + } else { + scrollBar = null; + viewportMain?.getScrollBar()?.dispose(); + } - // editorWidth += scrollBar?.barSize || 0; + editorWidth += scrollBar?.barSize || 0; if (editorWidth > clientWidth) { editorWidth = clientWidth; diff --git a/packages/sheets-ui/src/views/editor-container/EditorContainer.tsx b/packages/sheets-ui/src/views/editor-container/EditorContainer.tsx index 70741c4a33d..590da6bbb85 100644 --- a/packages/sheets-ui/src/views/editor-container/EditorContainer.tsx +++ b/packages/sheets-ui/src/views/editor-container/EditorContainer.tsx @@ -162,6 +162,7 @@ export const EditorContainer: React.FC = () => { isSupportAcrossSheet resetSelectionOnBlur={false} isSingle={false} + autoScrollbar={false} onFormulaSelectingChange={(isSelecting: 0 | 1 | 2) => { isRefSelecting.current = isSelecting; if (isSelecting) { From b15e392df2e661d79228c7ec0c959682c42d55a3 Mon Sep 17 00:00:00 2001 From: zhangw Date: Thu, 26 Dec 2024 11:56:25 +0800 Subject: [PATCH 089/134] feat: update --- .../render-controllers/editor-bridge.render-controller.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/sheets-ui/src/controllers/render-controllers/editor-bridge.render-controller.ts b/packages/sheets-ui/src/controllers/render-controllers/editor-bridge.render-controller.ts index 338d01b367f..2be9609d6f3 100644 --- a/packages/sheets-ui/src/controllers/render-controllers/editor-bridge.render-controller.ts +++ b/packages/sheets-ui/src/controllers/render-controllers/editor-bridge.render-controller.ts @@ -28,6 +28,7 @@ import { SetWorksheetActiveOperation, SheetsSelectionsService, } from '@univerjs/sheets'; +import { KeyCode } from '@univerjs/ui'; import { filter, merge } from 'rxjs'; import { SetZoomRatioCommand } from '../../commands/commands/set-zoom-ratio.command'; import { SetActivateCellEditOperation } from '../../commands/operations/activate-cell-edit.operation'; @@ -217,8 +218,10 @@ export class EditorBridgeRenderController extends RxDisposable implements IRende if (config == null) { return; } - const event = config.event as KeyboardEvent; + if (event.which === KeyCode.UNKNOWN) { + return; + } this._commandService.executeCommand(SetCellEditVisibleOperation.id, { visible: true, From ccdce1d7e63d078eed4d89d6c388b1c754b88b36 Mon Sep 17 00:00:00 2001 From: zhangw Date: Thu, 26 Dec 2024 11:59:00 +0800 Subject: [PATCH 090/134] feat: update --- .../render-controllers/editor-bridge.render-controller.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/sheets-ui/src/controllers/render-controllers/editor-bridge.render-controller.ts b/packages/sheets-ui/src/controllers/render-controllers/editor-bridge.render-controller.ts index 2be9609d6f3..143f5e1f265 100644 --- a/packages/sheets-ui/src/controllers/render-controllers/editor-bridge.render-controller.ts +++ b/packages/sheets-ui/src/controllers/render-controllers/editor-bridge.render-controller.ts @@ -28,7 +28,6 @@ import { SetWorksheetActiveOperation, SheetsSelectionsService, } from '@univerjs/sheets'; -import { KeyCode } from '@univerjs/ui'; import { filter, merge } from 'rxjs'; import { SetZoomRatioCommand } from '../../commands/commands/set-zoom-ratio.command'; import { SetActivateCellEditOperation } from '../../commands/operations/activate-cell-edit.operation'; @@ -219,9 +218,6 @@ export class EditorBridgeRenderController extends RxDisposable implements IRende return; } const event = config.event as KeyboardEvent; - if (event.which === KeyCode.UNKNOWN) { - return; - } this._commandService.executeCommand(SetCellEditVisibleOperation.id, { visible: true, From 09f0bba45919673cbc125886ee239486cb14ea2d Mon Sep 17 00:00:00 2001 From: zhangw Date: Thu, 26 Dec 2024 12:08:51 +0800 Subject: [PATCH 091/134] feat: update --- .../render-controllers/editor-bridge.render-controller.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/sheets-ui/src/controllers/render-controllers/editor-bridge.render-controller.ts b/packages/sheets-ui/src/controllers/render-controllers/editor-bridge.render-controller.ts index 143f5e1f265..597d458eb61 100644 --- a/packages/sheets-ui/src/controllers/render-controllers/editor-bridge.render-controller.ts +++ b/packages/sheets-ui/src/controllers/render-controllers/editor-bridge.render-controller.ts @@ -214,10 +214,10 @@ export class EditorBridgeRenderController extends RxDisposable implements IRende } private _showEditorByKeyboard(config: Nullable) { - if (config == null) { + const event = config?.event as InputEvent; + if (config == null || (!event.data && event.inputType !== 'InsertParagraph')) { return; } - const event = config.event as KeyboardEvent; this._commandService.executeCommand(SetCellEditVisibleOperation.id, { visible: true, From 83d9598a2b212ce7f7d8de67824b5b96810aaf78 Mon Sep 17 00:00:00 2001 From: zhangw Date: Thu, 26 Dec 2024 14:42:14 +0800 Subject: [PATCH 092/134] feat: update --- .../formula-input/custom-formula-input.tsx | 2 +- .../src/views/formula-editor/index.module.less | 14 ++++++-------- .../src/views/formula-editor/index.tsx | 2 +- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/packages/sheets-data-validation-ui/src/views/components/formula-input/custom-formula-input.tsx b/packages/sheets-data-validation-ui/src/views/components/formula-input/custom-formula-input.tsx index da5f0db20e9..35475afca8d 100644 --- a/packages/sheets-data-validation-ui/src/views/components/formula-input/custom-formula-input.tsx +++ b/packages/sheets-data-validation-ui/src/views/components/formula-input/custom-formula-input.tsx @@ -32,7 +32,7 @@ export function CustomFormulaInput(props: IFormulaInputProps) { return ( ; - const contentWidth = Math.max(actualWidth, width); const contentHeight = Math.max(actualHeight, height); From 24bc9a0ebf327dc7f18832e85caeadb61f30cfd6 Mon Sep 17 00:00:00 2001 From: zhangw Date: Thu, 26 Dec 2024 17:18:14 +0800 Subject: [PATCH 095/134] feat: update --- .../ref-selections.render-service.ts | 2 +- .../hooks/useFormulaSelection.ts | 18 ++++++++++++++++-- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/packages/sheets-formula-ui/src/services/render-services/ref-selections.render-service.ts b/packages/sheets-formula-ui/src/services/render-services/ref-selections.render-service.ts index b4fb61359cc..654522c82b2 100644 --- a/packages/sheets-formula-ui/src/services/render-services/ref-selections.render-service.ts +++ b/packages/sheets-formula-ui/src/services/render-services/ref-selections.render-service.ts @@ -263,7 +263,7 @@ export class RefSelectionsRenderService extends BaseSelectionRenderService imple * @param viewport * @param scrollTimerType */ - // eslint-disable-next-line complexity + // eslint-disable-next-line complexity, max-lines-per-function protected _onPointerDown( evt: IPointerEvent | IMouseEvent, _zIndex = 0, diff --git a/packages/sheets-formula-ui/src/views/formula-editor/hooks/useFormulaSelection.ts b/packages/sheets-formula-ui/src/views/formula-editor/hooks/useFormulaSelection.ts index 3c1ba785735..a2d15eae1d0 100644 --- a/packages/sheets-formula-ui/src/views/formula-editor/hooks/useFormulaSelection.ts +++ b/packages/sheets-formula-ui/src/views/formula-editor/hooks/useFormulaSelection.ts @@ -72,15 +72,17 @@ export function useFormulaSelecting(editorId: string, nodes: (string | ISequence const [isSelecting, setIsSelecting] = useState(FormulaSelectingType.NOT_SELECT); const nodesRef = useRef(nodes); nodesRef.current = nodes; + const isSelectingRef = useRef(isSelecting); + isSelectingRef.current = isSelecting; useEffect(() => { const sub = textSelections$.subscribe(() => { const char = getCurrentChar(injector); const activeRange = docSelectionRenderService?.getActiveTextRange(); const index = activeRange?.collapsed ? activeRange.startOffset! : -1; - const lastNode = nodesRef.current[nodesRef.current.length - 1]; const dataStream = getCurrentBodyDataStreamAndOffset(injector)?.dataStream; - const isFocusingLastNode = typeof lastNode === 'object' && lastNode.nodeType === sequenceNodeType.REFERENCE && index === (dataStream?.length ?? 2) - 2; + const isFocusingLastNode = nodesRef.current.some((node) => typeof node === 'object' && node.nodeType === sequenceNodeType.REFERENCE && index === node.endIndex + 2); + if (dataStream?.substring(0, 1) === '=' && ((char && matchRefDrawToken(char)) || isFocusingLastNode)) { if (isFocusingLastNode) { setIsSelecting(FormulaSelectingType.CAN_EDIT); @@ -95,5 +97,17 @@ export function useFormulaSelecting(editorId: string, nodes: (string | ISequence return () => sub.unsubscribe(); }, [docSelectionRenderService, injector, textSelections$]); + useEffect(() => { + const sub = renderer?.mainComponent?.onPointerUp$.subscribeEvent(() => { + if (isSelectingRef.current === FormulaSelectingType.CAN_EDIT) { + setTimeout(() => { + setIsSelecting(FormulaSelectingType.NOT_SELECT); + }); + } + }); + + return () => sub?.unsubscribe(); + }, [renderer?.mainComponent?.onPointerUp$]); + return isSelecting; } From 77def0e9ad6da4acaa57dc8178d92b33885505e7 Mon Sep 17 00:00:00 2001 From: zhangw Date: Fri, 27 Dec 2024 00:41:43 +0800 Subject: [PATCH 096/134] feat: update --- .../hooks/useFormulaSelection.ts | 8 +-- .../hooks/useSheetSelectionChange.ts | 56 +++++++++++++++---- .../src/views/formula-editor/hooks/util.ts | 25 +++++++++ .../src/views/formula-editor/index.tsx | 10 +++- .../range-selector/hooks/useHighlight.ts | 29 ++++++++-- .../src/views/range-selector/index.tsx | 10 +++- .../base-selection-render.service.ts | 16 +++++- 7 files changed, 126 insertions(+), 28 deletions(-) create mode 100644 packages/sheets-formula-ui/src/views/formula-editor/hooks/util.ts diff --git a/packages/sheets-formula-ui/src/views/formula-editor/hooks/useFormulaSelection.ts b/packages/sheets-formula-ui/src/views/formula-editor/hooks/useFormulaSelection.ts index a2d15eae1d0..18328741f82 100644 --- a/packages/sheets-formula-ui/src/views/formula-editor/hooks/useFormulaSelection.ts +++ b/packages/sheets-formula-ui/src/views/formula-editor/hooks/useFormulaSelection.ts @@ -81,10 +81,10 @@ export function useFormulaSelecting(editorId: string, nodes: (string | ISequence const activeRange = docSelectionRenderService?.getActiveTextRange(); const index = activeRange?.collapsed ? activeRange.startOffset! : -1; const dataStream = getCurrentBodyDataStreamAndOffset(injector)?.dataStream; - const isFocusingLastNode = nodesRef.current.some((node) => typeof node === 'object' && node.nodeType === sequenceNodeType.REFERENCE && index === node.endIndex + 2); + const focusingIndex = nodesRef.current.findIndex((node) => typeof node === 'object' && node.nodeType === sequenceNodeType.REFERENCE && index === node.endIndex + 2); - if (dataStream?.substring(0, 1) === '=' && ((char && matchRefDrawToken(char)) || isFocusingLastNode)) { - if (isFocusingLastNode) { + if (dataStream?.substring(0, 1) === '=' && ((char && matchRefDrawToken(char)) || focusingIndex > -1)) { + if (focusingIndex > -1) { setIsSelecting(FormulaSelectingType.CAN_EDIT); } else { setIsSelecting(FormulaSelectingType.NEED_ADD); @@ -109,5 +109,5 @@ export function useFormulaSelecting(editorId: string, nodes: (string | ISequence return () => sub?.unsubscribe(); }, [renderer?.mainComponent?.onPointerUp$]); - return isSelecting; + return { isSelecting }; } diff --git a/packages/sheets-formula-ui/src/views/formula-editor/hooks/useSheetSelectionChange.ts b/packages/sheets-formula-ui/src/views/formula-editor/hooks/useSheetSelectionChange.ts index bf19774b93b..2ee366bc4ee 100644 --- a/packages/sheets-formula-ui/src/views/formula-editor/hooks/useSheetSelectionChange.ts +++ b/packages/sheets-formula-ui/src/views/formula-editor/hooks/useSheetSelectionChange.ts @@ -18,10 +18,11 @@ import type { Workbook } from '@univerjs/core'; import type { Editor } from '@univerjs/docs-ui'; - import type { ISelectionWithCoord, ISetSelectionsOperationParams } from '@univerjs/sheets'; +import type { IRefSelection } from '../../range-selector/hooks/useHighlight'; import type { INode } from '../../range-selector/utils/filterReferenceNode'; import { DisposableCollection, ICommandService, IUniverInstanceService, useDependency, useObservable } from '@univerjs/core'; +import { DocSelectionManagerService } from '@univerjs/docs'; import { deserializeRangeWithSheet, sequenceNodeType, serializeRange, serializeRangeWithSheet } from '@univerjs/engine-formula'; import { IRenderManagerService } from '@univerjs/engine-render'; import { IRefSelectionsService, SetSelectionsOperation } from '@univerjs/sheets'; @@ -35,6 +36,7 @@ import { sequenceNodeToText } from '../../range-selector/utils/sequenceNodeToTex import { unitRangesToText } from '../../range-selector/utils/unitRangesToText'; import { useStateRef } from '../hooks/useStateRef'; import { useSelectionAdd } from './useSelectionAdd'; +import { getFocusingReference } from './util'; const noop = (() => { }) as any; export const useSheetSelectionChange = ( @@ -42,6 +44,7 @@ export const useSheetSelectionChange = ( unitId: string, subUnitId: string, sequenceNodes: INode[], + refSelectionRef: React.MutableRefObject, isSupportAcrossSheet: boolean, listenSelectionSet: boolean, editor?: Editor, @@ -51,6 +54,7 @@ export const useSheetSelectionChange = ( const univerInstanceService = useDependency(IUniverInstanceService); const commandService = useDependency(ICommandService); const sequenceNodesRef = useStateRef(sequenceNodes); + const docSelectionManagerService = useDependency(DocSelectionManagerService); const { getIsNeedAddSelection } = useSelectionAdd(unitId, sequenceNodes, editor); @@ -59,15 +63,15 @@ export const useSheetSelectionChange = ( const sheetName = useMemo(() => getSheetNameById(subUnitId), [subUnitId]); const activeSheet = useObservable(workbook?.activeSheet$); const contextRef = useStateRef({ activeSheet, sheetName }); - const render = renderManagerService.getRenderById(unitId); const refSelectionsRenderService = render?.with(RefSelectionsRenderService); const refSelectionsService = useDependency(IRefSelectionsService); - const isScalingRef = useRef(false); const scalingOptionRef = useRef<{ result: string; offset: number }>(); + useEffect(() => {}, []); + useEffect(() => { if (refSelectionsRenderService && isNeed) { let isFirst = true; @@ -84,14 +88,15 @@ export const useSheetSelectionChange = ( const docRange = currentDocSelections[0]; const offset = docRange.startOffset - 1; const sequenceNodes = [...sequenceNodesRef.current]; + const nodeIndex = findIndexFromSequenceNodes(sequenceNodes, offset, false); + if (getIsNeedAddSelection()) { + if (nodeIndex === -1 && sequenceNodes.length) { + return; + } if (offset !== 0) { - const index = findIndexFromSequenceNodes(sequenceNodes, offset, false); - if (index === -1 && sequenceNodes.length) { - return; - } const range = selections[selections.length - 1]; - const lastNodes = sequenceNodes.splice(index + 1); + const lastNodes = sequenceNodes.splice(nodeIndex + 1); const rangeSheetId = range.rangeWithCoord.sheetId ?? subUnitId; const unitRangeName = { range: range.rangeWithCoord, @@ -121,7 +126,7 @@ export const useSheetSelectionChange = ( } else { // 更新全部的 ref Selection let currentRefIndex = 0; - const currentText = sequenceNodes.map((item) => { + const newTokens = sequenceNodes.map((item) => { if (typeof item === 'string') { return item; } @@ -152,7 +157,15 @@ export const useSheetSelectionChange = ( return refRanges[0]; } return item.token; - }).join(''); + }); + let currentText = ''; + let newOffset; + newTokens.forEach((item, index) => { + currentText += item; + if (index === nodeIndex) { + newOffset = currentText.length; + } + }); const theLastList: string[] = []; for (let index = currentRefIndex; index <= selections.length - 1; index++) { const selection = selections[index]; @@ -169,7 +182,7 @@ export const useSheetSelectionChange = ( const preNode = sequenceNodes[sequenceNodes.length - 1]; const isPreNodeRef = preNode && (typeof preNode === 'string' ? false : preNode.nodeType === sequenceNodeType.REFERENCE); const result = `${currentText}${theLastList.length && isPreNodeRef ? ',' : ''}${theLastList.join(',')}`; - handleRangeChange(result, result.length, true); + handleRangeChange(result, newOffset ?? result.length, true); } }; const disposableCollection = new DisposableCollection(); @@ -338,4 +351,25 @@ export const useSheetSelectionChange = ( }; } }, [commandService, getSheetNameById, handleRangeChange, isSupportAcrossSheet, listenSelectionSet, sequenceNodesRef]); + + useEffect(() => { + if (!editor) { + return; + } + const sub = docSelectionManagerService.textSelection$.subscribe((e) => { + const { unitId } = e; + if (unitId !== editor.getEditorId()) { + return; + } + + const focusingRef = getFocusingReference(editor, refSelectionRef.current); + if (focusingRef) { + refSelectionsRenderService?.setActiveSelectionIndex(focusingRef.index); + } else { + refSelectionsRenderService?.resetActiveSelectionIndex(); + } + }); + + return () => sub.unsubscribe(); + }, [docSelectionManagerService.textSelection$, editor, refSelectionRef, refSelectionsRenderService, sequenceNodesRef]); }; diff --git a/packages/sheets-formula-ui/src/views/formula-editor/hooks/util.ts b/packages/sheets-formula-ui/src/views/formula-editor/hooks/util.ts new file mode 100644 index 00000000000..cd05f4dffdf --- /dev/null +++ b/packages/sheets-formula-ui/src/views/formula-editor/hooks/util.ts @@ -0,0 +1,25 @@ +/** + * 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 type { Editor } from '@univerjs/docs-ui'; +import type { IRefSelection } from '../../range-selector/hooks/useHighlight'; + +export function getFocusingReference(editor: Editor, refSelections: IRefSelection[]) { + const cursor = editor.getSelectionRanges()?.[0]?.startOffset; + if (cursor) { + return refSelections.find((node) => node.endIndex + 2 === cursor); + } +} diff --git a/packages/sheets-formula-ui/src/views/formula-editor/index.tsx b/packages/sheets-formula-ui/src/views/formula-editor/index.tsx index 4ce3eb97426..9c045677b5a 100644 --- a/packages/sheets-formula-ui/src/views/formula-editor/index.tsx +++ b/packages/sheets-formula-ui/src/views/formula-editor/index.tsx @@ -18,6 +18,7 @@ import type { DocumentDataModel, IDisposable } from '@univerjs/core'; import type { Editor } from '@univerjs/docs-ui'; import type { KeyCode, MetaKeys } from '@univerjs/ui'; import type { ReactNode } from 'react'; +import type { IRefSelection } from '../range-selector/hooks/useHighlight'; import type { IKeyboardEventConfig } from '../range-selector/hooks/useKeyboardEvent'; import { BuildTextUtils, createInternalEditorID, generateRandomId, IUniverInstanceService, UniverInstanceType, useDependency, useObservable } from '@univerjs/core'; import { DocBackScrollRenderController, DocSelectionRenderService, IEditorService } from '@univerjs/docs-ui'; @@ -42,6 +43,7 @@ import { useFormulaSearch } from './hooks/useFormulaSearch'; import { FormulaSelectingType, useFormulaSelecting } from './hooks/useFormulaSelection'; import { useSheetSelectionChange } from './hooks/useSheetSelectionChange'; import { useVerify } from './hooks/useVerify'; +import { getFocusingReference } from './hooks/util'; import styles from './index.module.less'; import { SearchFunction } from './search-function/SearchFunction'; import { getFormulaText } from './utils/getFormulaText'; @@ -124,7 +126,7 @@ export function FormulaEditor(props: IFormulaEditorProps) { const formulaText = BuildTextUtils.transform.getPlainText(document?.getBody()?.dataStream ?? ''); const formulaWithoutEqualSymbol = useMemo(() => getFormulaText(formulaText), [formulaText]); const sequenceNodes = useMemo(() => getFormulaToken(formulaWithoutEqualSymbol), [formulaWithoutEqualSymbol, getFormulaToken]); - const isSelecting = useFormulaSelecting(editorId, sequenceNodes); + const { isSelecting } = useFormulaSelecting(editorId, sequenceNodes); const [shouldMoveRefSelection, setShouldMoveRefSelection] = useState(false); const highTextRef = useRef(''); const renderManagerService = useDependency(IRenderManagerService); @@ -134,6 +136,7 @@ export function FormulaEditor(props: IFormulaEditorProps) { const currentDoc$ = useMemo(() => univerInstanceService.getCurrentTypeOfUnit$(UniverInstanceType.UNIVER_DOC), [univerInstanceService]); const currentDoc = useObservable(currentDoc$); const docFocusing = currentDoc?.getUnitId() === editorId; + const refSelections = useRef([] as IRefSelection[]); useEffect(() => { if (isSelecting === FormulaSelectingType.NEED_ADD) { @@ -164,9 +167,10 @@ export function FormulaEditor(props: IFormulaEditorProps) { // remove equals need to remove highlight style preText.slice(1) === text && preText[0] === '=' ); + refSelections.current = ranges; if (isEnd) { - highlightSheet(isFocus ? ranges : []); + highlightSheet(isFocus ? ranges : [], getFocusingReference(editorRef.current, ranges)); } }); @@ -257,7 +261,7 @@ export function FormulaEditor(props: IFormulaEditorProps) { } }); - useSheetSelectionChange(isFocus, unitId, subUnitId, sequenceNodes, isSupportAcrossSheet, shouldMoveRefSelection, editor, handleSelectionChange); + useSheetSelectionChange(isFocus, unitId, subUnitId, sequenceNodes, refSelections, isSupportAcrossSheet, shouldMoveRefSelection, editor, handleSelectionChange); useSwitchSheet(isFocus, unitId, isSupportAcrossSheet, isFocusSet, onBlur, noop); const { searchList, searchText, handlerFormulaReplace, reset: resetFormulaSearch } = useFormulaSearch(isFocus, sequenceNodes, editor); diff --git a/packages/sheets-formula-ui/src/views/range-selector/hooks/useHighlight.ts b/packages/sheets-formula-ui/src/views/range-selector/hooks/useHighlight.ts index a45f443f7e2..d695bf9dce8 100644 --- a/packages/sheets-formula-ui/src/views/range-selector/hooks/useHighlight.ts +++ b/packages/sheets-formula-ui/src/views/range-selector/hooks/useHighlight.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import type { ITextRun, Workbook } from '@univerjs/core'; +import type { ITextRun, Nullable, Workbook } from '@univerjs/core'; import type { Editor } from '@univerjs/docs-ui'; import type { ISequenceNode } from '@univerjs/engine-formula'; import type { ISelectionWithStyle } from '@univerjs/sheets'; @@ -26,14 +26,17 @@ import { IRenderManagerService } from '@univerjs/engine-render'; import { IRefSelectionsService, setEndForRange } from '@univerjs/sheets'; import { IDescriptionService } from '@univerjs/sheets-formula'; import { SheetSkeletonManagerService } from '@univerjs/sheets-ui'; -import { useCallback, useMemo } from 'react'; +import { useCallback, useEffect, useMemo } from 'react'; import { genFormulaRefSelectionStyle } from '../../../common/selection'; import { RefSelectionsRenderService } from '../../../services/render-services/ref-selections.render-service'; -interface IRefSelection { +export interface IRefSelection { refIndex: number; themeColor: string; token: string; + startIndex: number; + endIndex: number; + index: number; } /** @@ -51,7 +54,7 @@ export function useSheetHighlight(unitId: string) { const refSelectionsRenderService = render?.with(RefSelectionsRenderService); const sheetSkeletonManagerService = render?.with(SheetSkeletonManagerService); - const highlightSheet = useCallback((refSelections: IRefSelection[]) => { + const highlightSheet = useCallback((refSelections: IRefSelection[], focusingRef: Nullable) => { const workbook = univerInstanceService.getUnit(unitId); const worksheet = workbook?.getActiveSheet(); const selectionWithStyle: ISelectionWithStyle[] = []; @@ -95,7 +98,20 @@ export function useSheetHighlight(unitId: string) { } else { refSelectionsService.setSelections(selectionWithStyle); } + + if (focusingRef) { + refSelectionsRenderService?.setActiveSelectionIndex(focusingRef.index); + } else { + refSelectionsRenderService?.resetActiveSelectionIndex(); + } }, [refSelectionsRenderService, refSelectionsService, sheetSkeletonManagerService, themeService, unitId, univerInstanceService]); + + useEffect(() => { + return () => { + refSelectionsRenderService?.resetActiveSelectionIndex(); + }; + }, [refSelectionsRenderService]); + return highlightSheet; } @@ -119,8 +135,6 @@ export function useDocHight(_leadingCharacter: string = '') { if (sequenceNodes == null || sequenceNodes.length === 0) { if (clearTextRun) { cloneBody.textRuns = []; - // const cloneData = { ...data, body: cloneBody }; - // editor.setDocumentData(cloneData); commandService.syncExecuteCommand(ReplaceTextRunsCommand.id, { unitId: editorId, body: getBodySlice(cloneBody, 0, cloneBody.dataStream.length - 2), @@ -252,6 +266,9 @@ export function buildTextRuns(descriptionService: IDescriptionService, colorMap: refIndex: i, themeColor, token, + startIndex: node.startIndex, + endIndex: node.endIndex, + index: refSelections.length, }); } else if (nodeType === sequenceNodeType.NUMBER) { themeColor = numberColor; diff --git a/packages/sheets-formula-ui/src/views/range-selector/index.tsx b/packages/sheets-formula-ui/src/views/range-selector/index.tsx index 8c6b10bfd41..acebd23913d 100644 --- a/packages/sheets-formula-ui/src/views/range-selector/index.tsx +++ b/packages/sheets-formula-ui/src/views/range-selector/index.tsx @@ -17,6 +17,7 @@ import type { IDisposable, IUnitRangeName } from '@univerjs/core'; import type { Editor } from '@univerjs/docs-ui'; import type { ReactNode } from 'react'; +import type { IRefSelection } from './hooks/useHighlight'; import { createInternalEditorID, generateRandomId, ICommandService, IUniverInstanceService, LocaleService, UniverInstanceType, useDependency, useObservable } from '@univerjs/core'; import { Button, Dialog, Input, Tooltip } from '@univerjs/design'; import { DocBackScrollRenderController, IEditorService } from '@univerjs/docs-ui'; @@ -24,14 +25,15 @@ import { deserializeRangeWithSheet, LexerTreeBuilder, matchToken, sequenceNodeTy import { IRenderManagerService } from '@univerjs/engine-render'; import { CloseSingle, DeleteSingle, IncreaseSingle, SelectRangeSingle } from '@univerjs/icons'; import { IDescriptionService } from '@univerjs/sheets-formula'; -import { RANGE_SELECTOR_SYMBOLS, SetCellEditVisibleOperation } from '@univerjs/sheets-ui'; +import { RANGE_SELECTOR_SYMBOLS, SetCellEditVisibleOperation } from '@univerjs/sheets-ui'; import { useEvent } from '@univerjs/ui'; import cl from 'clsx'; import React, { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react'; -import { noop, throttleTime } from 'rxjs'; +import { noop, throttleTime } from 'rxjs'; import { RefSelectionsRenderService } from '../../services/render-services/ref-selections.render-service'; +import { getFocusingReference } from '../formula-editor/hooks/util'; import { useEditorInput } from './hooks/useEditorInput'; import { useEmitChange } from './hooks/useEmitChange'; import { useFirstHighlightDoc } from './hooks/useFirstHighlightDoc'; @@ -128,6 +130,7 @@ export function RangeSelector(props: IRangeSelectorProps) { const currentDoc$ = useMemo(() => univerInstanceService.getCurrentTypeOfUnit$(UniverInstanceType.UNIVER_DOC), [univerInstanceService]); const currentDoc = useObservable(currentDoc$); const docFocusing = currentDoc?.getUnitId() === editorId; + const refSelections = useRef([]); const clickOutside = useEvent((e: MouseEvent, cb: () => void) => { if (rangeSelectorWrapRef.current && !rangeDialogVisible) { @@ -194,8 +197,9 @@ export function RangeSelector(props: IRangeSelectorProps) { } const sequenceNodes = getFormulaToken(text); const ranges = highlightDoc(editorRef.current, sequenceNodes, isNeedResetSelection); + refSelections.current = ranges; if (showSelection) { - highlightSheet(ranges); + highlightSheet(ranges, getFocusingReference(editorRef.current, ranges)); } }); diff --git a/packages/sheets-ui/src/services/selection/base-selection-render.service.ts b/packages/sheets-ui/src/services/selection/base-selection-render.service.ts index 1d78b0f54c0..2e6a837418e 100644 --- a/packages/sheets-ui/src/services/selection/base-selection-render.service.ts +++ b/packages/sheets-ui/src/services/selection/base-selection-render.service.ts @@ -114,6 +114,8 @@ export class BaseSelectionRenderService extends Disposable implements ISheetSele endColumn: -1, }; + protected _activeControlIndex = -1; + /** * the posX of viewport when the pointer down */ @@ -401,6 +403,14 @@ export class BaseSelectionRenderService extends Disposable implements ISheetSele ); } + setActiveSelectionIndex(index: number) { + this._activeControlIndex = index; + } + + resetActiveSelectionIndex(): void { + this._activeControlIndex = -1; + } + /** * get active(actually last) selection control * @returns T extends SelectionControl @@ -408,7 +418,11 @@ export class BaseSelectionRenderService extends Disposable implements ISheetSele getActiveSelectionControl(): Nullable { const controls = this.getSelectionControls(); if (controls) { - return controls[controls.length - 1] as T; + if (this._activeControlIndex < 0) { + return controls[controls.length - 1] as T; + } + + return controls[this._activeControlIndex] as T; } } From e2f5db9ba1ec5a22c0252278dc0989c8606a00f6 Mon Sep 17 00:00:00 2001 From: zhangw Date: Fri, 27 Dec 2024 00:50:03 +0800 Subject: [PATCH 097/134] feat: update --- .../src/views/formula-editor/hooks/useSheetSelectionChange.ts | 1 - .../sheets-ui/src/commands/commands/set-selection.command.ts | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/sheets-formula-ui/src/views/formula-editor/hooks/useSheetSelectionChange.ts b/packages/sheets-formula-ui/src/views/formula-editor/hooks/useSheetSelectionChange.ts index 2ee366bc4ee..f0762756e29 100644 --- a/packages/sheets-formula-ui/src/views/formula-editor/hooks/useSheetSelectionChange.ts +++ b/packages/sheets-formula-ui/src/views/formula-editor/hooks/useSheetSelectionChange.ts @@ -308,7 +308,6 @@ export const useSheetSelectionChange = ( return; } const { selections } = params; - if (selections.length) { const last = selections[selections.length - 1]; if (last) { diff --git a/packages/sheets-ui/src/commands/commands/set-selection.command.ts b/packages/sheets-ui/src/commands/commands/set-selection.command.ts index 3c8d42c8429..dfefedd0c19 100644 --- a/packages/sheets-ui/src/commands/commands/set-selection.command.ts +++ b/packages/sheets-ui/src/commands/commands/set-selection.command.ts @@ -131,6 +131,7 @@ export const MoveSelectionCommand: ICommand = { subUnitId: worksheet.getSheetId(), selections, type: SelectionMoveType.MOVE_END, + extra, } as ISetSelectionsOperationParams); return rs; }, @@ -301,6 +302,7 @@ export const MoveSelectionEnterAndTabCommand: ICommand Date: Fri, 27 Dec 2024 01:34:49 +0800 Subject: [PATCH 098/134] feat: update --- .../hooks/useFormulaSelection.ts | 64 ++++++++----------- .../src/views/formula-editor/index.tsx | 23 ++----- 2 files changed, 29 insertions(+), 58 deletions(-) diff --git a/packages/sheets-formula-ui/src/views/formula-editor/hooks/useFormulaSelection.ts b/packages/sheets-formula-ui/src/views/formula-editor/hooks/useFormulaSelection.ts index 18328741f82..66a69b9d2b3 100644 --- a/packages/sheets-formula-ui/src/views/formula-editor/hooks/useFormulaSelection.ts +++ b/packages/sheets-formula-ui/src/views/formula-editor/hooks/useFormulaSelection.ts @@ -19,10 +19,9 @@ import type { ISequenceNode } from '@univerjs/engine-formula'; import { Injector, IUniverInstanceService, useDependency } from '@univerjs/core'; import { DocSelectionManagerService } from '@univerjs/docs'; import { DocSelectionRenderService } from '@univerjs/docs-ui'; -import { matchRefDrawToken, sequenceNodeType } from '@univerjs/engine-formula'; +import { isFormulaLexerToken, matchRefDrawToken, matchToken, sequenceNodeType } from '@univerjs/engine-formula'; import { IRenderManagerService } from '@univerjs/engine-render'; -import { useEffect, useMemo, useRef, useState } from 'react'; -import { of } from 'rxjs'; +import { useEffect, useRef, useState } from 'react'; function getCurrentBodyDataStreamAndOffset(accssor: IAccessor) { const univerInstanceService = accssor.get(IUniverInstanceService); @@ -36,27 +35,6 @@ function getCurrentBodyDataStreamAndOffset(accssor: IAccessor) { return { dataStream, offset: 0 }; } -function getCurrentChar(accssor: IAccessor) { - const docSelectionManagerService = accssor.get(DocSelectionManagerService); - const activeRange = docSelectionManagerService.getActiveTextRange(); - - if (activeRange == null) { - return; - } - - const { startOffset } = activeRange; - - const config = getCurrentBodyDataStreamAndOffset(accssor); - - if (config == null || startOffset == null) { - return; - } - - const dataStream = config.dataStream; - - return dataStream[startOffset - 1 + config.offset]; -} - export enum FormulaSelectingType { NOT_SELECT = 0, NEED_ADD = 1, @@ -67,26 +45,37 @@ export function useFormulaSelecting(editorId: string, nodes: (string | ISequence const renderManagerService = useDependency(IRenderManagerService); const renderer = renderManagerService.getRenderById(editorId); const docSelectionRenderService = renderer?.with(DocSelectionRenderService); + const docSelectionManagerService = useDependency(DocSelectionManagerService); const injector = useDependency(Injector); - const textSelections$ = useMemo(() => docSelectionRenderService?.textSelectionInner$ ?? of(), [docSelectionRenderService?.textSelectionInner$]); const [isSelecting, setIsSelecting] = useState(FormulaSelectingType.NOT_SELECT); const nodesRef = useRef(nodes); nodesRef.current = nodes; + const isDisabledByPointer = useRef(false); const isSelectingRef = useRef(isSelecting); isSelectingRef.current = isSelecting; useEffect(() => { - const sub = textSelections$.subscribe(() => { - const char = getCurrentChar(injector); + const sub = docSelectionManagerService.textSelection$.subscribe((param) => { + if (param.unitId !== editorId) return; const activeRange = docSelectionRenderService?.getActiveTextRange(); const index = activeRange?.collapsed ? activeRange.startOffset! : -1; - const dataStream = getCurrentBodyDataStreamAndOffset(injector)?.dataStream; + const config = getCurrentBodyDataStreamAndOffset(injector); + if (!config) return; + const dataStream = config?.dataStream?.slice(0, -2); + const char = dataStream[index - 1 + config.offset]; + const nextChar = dataStream[index + config.offset]; const focusingIndex = nodesRef.current.findIndex((node) => typeof node === 'object' && node.nodeType === sequenceNodeType.REFERENCE && index === node.endIndex + 2); - - if (dataStream?.substring(0, 1) === '=' && ((char && matchRefDrawToken(char)) || focusingIndex > -1)) { - if (focusingIndex > -1) { + const adding = (char && matchRefDrawToken(char)) && (!nextChar || (isFormulaLexerToken(nextChar) && nextChar !== matchToken.OPEN_BRACKET)); + const editing = focusingIndex > -1; + + if (dataStream?.substring(0, 1) === '=' && (adding || editing)) { + if (editing) { + if (isDisabledByPointer.current) { + return; + } setIsSelecting(FormulaSelectingType.CAN_EDIT); } else { + isDisabledByPointer.current = false; setIsSelecting(FormulaSelectingType.NEED_ADD); } } else { @@ -95,19 +84,16 @@ export function useFormulaSelecting(editorId: string, nodes: (string | ISequence }); return () => sub.unsubscribe(); - }, [docSelectionRenderService, injector, textSelections$]); + }, [docSelectionManagerService.textSelection$, docSelectionRenderService, editorId, injector]); useEffect(() => { - const sub = renderer?.mainComponent?.onPointerUp$.subscribeEvent(() => { - if (isSelectingRef.current === FormulaSelectingType.CAN_EDIT) { - setTimeout(() => { - setIsSelecting(FormulaSelectingType.NOT_SELECT); - }); - } + const sub = renderer?.mainComponent?.onPointerDown$.subscribeEvent(() => { + setIsSelecting(FormulaSelectingType.NOT_SELECT); + isDisabledByPointer.current = true; }); return () => sub?.unsubscribe(); - }, [renderer?.mainComponent?.onPointerUp$]); + }, [renderer?.mainComponent?.onPointerDown$]); return { isSelecting }; } diff --git a/packages/sheets-formula-ui/src/views/formula-editor/index.tsx b/packages/sheets-formula-ui/src/views/formula-editor/index.tsx index 9c045677b5a..600149f8fad 100644 --- a/packages/sheets-formula-ui/src/views/formula-editor/index.tsx +++ b/packages/sheets-formula-ui/src/views/formula-editor/index.tsx @@ -20,6 +20,7 @@ import type { KeyCode, MetaKeys } from '@univerjs/ui'; import type { ReactNode } from 'react'; import type { IRefSelection } from '../range-selector/hooks/useHighlight'; import type { IKeyboardEventConfig } from '../range-selector/hooks/useKeyboardEvent'; +import type { FormulaSelectingType } from './hooks/useFormulaSelection'; import { BuildTextUtils, createInternalEditorID, generateRandomId, IUniverInstanceService, UniverInstanceType, useDependency, useObservable } from '@univerjs/core'; import { DocBackScrollRenderController, DocSelectionRenderService, IEditorService } from '@univerjs/docs-ui'; import { IRenderManagerService } from '@univerjs/engine-render'; @@ -40,7 +41,7 @@ import { useSwitchSheet } from '../range-selector/hooks/useSwitchSheet'; import { HelpFunction } from './help-function/HelpFunction'; import { useFormulaDescribe } from './hooks/useFormulaDescribe'; import { useFormulaSearch } from './hooks/useFormulaSearch'; -import { FormulaSelectingType, useFormulaSelecting } from './hooks/useFormulaSelection'; +import { useFormulaSelecting } from './hooks/useFormulaSelection'; import { useSheetSelectionChange } from './hooks/useSheetSelectionChange'; import { useVerify } from './hooks/useVerify'; import { getFocusingReference } from './hooks/util'; @@ -127,7 +128,7 @@ export function FormulaEditor(props: IFormulaEditorProps) { const formulaWithoutEqualSymbol = useMemo(() => getFormulaText(formulaText), [formulaText]); const sequenceNodes = useMemo(() => getFormulaToken(formulaWithoutEqualSymbol), [formulaWithoutEqualSymbol, getFormulaToken]); const { isSelecting } = useFormulaSelecting(editorId, sequenceNodes); - const [shouldMoveRefSelection, setShouldMoveRefSelection] = useState(false); + // const [shouldMoveRefSelection, setShouldMoveRefSelection] = useState(false); const highTextRef = useRef(''); const renderManagerService = useDependency(IRenderManagerService); const renderer = renderManagerService.getRenderById(editorId); @@ -137,15 +138,7 @@ export function FormulaEditor(props: IFormulaEditorProps) { const currentDoc = useObservable(currentDoc$); const docFocusing = currentDoc?.getUnitId() === editorId; const refSelections = useRef([] as IRefSelection[]); - - useEffect(() => { - if (isSelecting === FormulaSelectingType.NEED_ADD) { - setShouldMoveRefSelection(true); - } - if (isSelecting === FormulaSelectingType.NOT_SELECT) { - setShouldMoveRefSelection(false); - } - }, [isSelecting]); + const shouldMoveRefSelection = Boolean(isSelecting); const needEmit = useEmitChange(sequenceNodes, (text: string) => { onChange(`=${text}`); @@ -287,14 +280,6 @@ export function FormulaEditor(props: IFormulaEditorProps) { }; const handleMouseUp = () => { - // 在进行多个 input 切换的时候,失焦必须快于获得焦点. - // 即使失焦是 mousedown 事件, - // 聚焦是 mouseup 事件, - // 但是 react 的 useEffect 无法保证顺序,无法确保失焦在聚焦之前. - if (isSelecting !== FormulaSelectingType.NEED_ADD) { - setShouldMoveRefSelection(false); - } - isFocusSet(true); onFocus(); focus(); From daed7abddd25698e80b24c19a675185011f3a5e5 Mon Sep 17 00:00:00 2001 From: zhangw Date: Fri, 27 Dec 2024 01:36:26 +0800 Subject: [PATCH 099/134] feat: update --- packages/sheets-formula-ui/src/views/formula-editor/index.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/sheets-formula-ui/src/views/formula-editor/index.tsx b/packages/sheets-formula-ui/src/views/formula-editor/index.tsx index 600149f8fad..e05b439308a 100644 --- a/packages/sheets-formula-ui/src/views/formula-editor/index.tsx +++ b/packages/sheets-formula-ui/src/views/formula-editor/index.tsx @@ -128,7 +128,6 @@ export function FormulaEditor(props: IFormulaEditorProps) { const formulaWithoutEqualSymbol = useMemo(() => getFormulaText(formulaText), [formulaText]); const sequenceNodes = useMemo(() => getFormulaToken(formulaWithoutEqualSymbol), [formulaWithoutEqualSymbol, getFormulaToken]); const { isSelecting } = useFormulaSelecting(editorId, sequenceNodes); - // const [shouldMoveRefSelection, setShouldMoveRefSelection] = useState(false); const highTextRef = useRef(''); const renderManagerService = useDependency(IRenderManagerService); const renderer = renderManagerService.getRenderById(editorId); From 5e2a534b54d09edd13a9cfba8eabcd192b717cdf Mon Sep 17 00:00:00 2001 From: zhangw Date: Fri, 27 Dec 2024 01:59:10 +0800 Subject: [PATCH 100/134] feat: update --- .../selection/doc-selection-render.service.ts | 9 +++++++- .../src/views/formula-editor/index.tsx | 23 +++++++++++-------- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/packages/docs-ui/src/services/selection/doc-selection-render.service.ts b/packages/docs-ui/src/services/selection/doc-selection-render.service.ts index 5d05ad52072..7e1553ed3f3 100644 --- a/packages/docs-ui/src/services/selection/doc-selection-render.service.ts +++ b/packages/docs-ui/src/services/selection/doc-selection-render.service.ts @@ -22,7 +22,7 @@ import { DataStreamTreeTokenType, DOC_RANGE_TYPE, ILogService, Inject, IUniverIn import { DocSkeletonManagerService } from '@univerjs/docs'; import { CURSOR_TYPE, getSystemHighlightColor, GlyphType, NORMAL_TEXT_SELECTION_PLUGIN_STYLE, PageLayoutType, ScrollTimer, Vector2 } from '@univerjs/engine-render'; import { ILayoutService } from '@univerjs/ui'; -import { BehaviorSubject, fromEvent, Subject, takeUntil } from 'rxjs'; +import { BehaviorSubject, fromEvent, merge, Subject, takeUntil } from 'rxjs'; import { getCanvasOffsetByEngine, getParagraphInfoByGlyph, getRangeListFromCharIndex, getRangeListFromSelection, getRectRangeFromCharIndex, getTextRangeFromCharIndex, serializeRectRange, serializeTextRange } from './selection-utils'; import { TextRange } from './text-range'; @@ -55,6 +55,13 @@ export class DocSelectionRenderService extends RxDisposable implements IRenderMo private readonly _onSelectionStart$ = new BehaviorSubject>(null); readonly onSelectionStart$ = this._onSelectionStart$.asObservable(); + readonly onChangeByEvent$ = merge( + this._onInput$, + this._onKeydown$, + this._onCompositionend$, + this._onKeydown$ + ); + private readonly _onPaste$ = new Subject(); readonly onPaste$ = this._onPaste$.asObservable(); diff --git a/packages/sheets-formula-ui/src/views/formula-editor/index.tsx b/packages/sheets-formula-ui/src/views/formula-editor/index.tsx index e05b439308a..968395e0599 100644 --- a/packages/sheets-formula-ui/src/views/formula-editor/index.tsx +++ b/packages/sheets-formula-ui/src/views/formula-editor/index.tsx @@ -145,7 +145,7 @@ export function FormulaEditor(props: IFormulaEditorProps) { const highlightDoc = useDocHight('='); const highlightSheet = useSheetHighlight(unitId); - const highligh = useEvent((text: string, isNeedResetSelection: boolean = true, isEnd?: boolean) => { + const highlight = useEvent((text: string, isNeedResetSelection: boolean = true, isEnd?: boolean) => { if (!editorRef.current) { return; } @@ -166,16 +166,21 @@ export function FormulaEditor(props: IFormulaEditorProps) { } }); - useEffect(() => { - highligh(formulaText, false); - }, [highligh, formulaText, isFocus]); - useEffect(() => { if (isFocus) { - highligh(formulaText, false, true); + highlight(formulaText, false, true); } }, [isFocus]); + useEffect(() => { + const sub = docSelectionRenderService?.onChangeByEvent$.subscribe(() => { + const formulaText = BuildTextUtils.transform.getPlainText(document?.getBody()?.dataStream ?? ''); + highlight(formulaText, false, true); + }); + + return () => sub?.unsubscribe(); + }, [docSelectionRenderService?.onChangeByEvent$, document, highlight]); + useVerify(isFocus, onVerify, formulaText); const focus = useFocus(editor); @@ -207,7 +212,7 @@ export function FormulaEditor(props: IFormulaEditorProps) { }, formulaEditorContainerRef.current); const editor = editorService.getEditor(editorId)! as Editor; editorRef.current = editor; - highligh(initValue, false, true); + highlight(initValue, false, true); } return () => { @@ -237,7 +242,7 @@ export function FormulaEditor(props: IFormulaEditorProps) { return; } needEmit(); - highligh(`=${refString}`, true, isEnd); + highlight(`=${refString}`, true, isEnd); if (isEnd) { focus(); if (offset !== -1) { @@ -274,7 +279,7 @@ export function FormulaEditor(props: IFormulaEditorProps) { } resetFormulaSearch(); focus(); - highligh(`=${res.text}`); + highlight(`=${res.text}`); } }; From 326ad73b5654784109dc6c7078298930144698b9 Mon Sep 17 00:00:00 2001 From: zhangw Date: Fri, 27 Dec 2024 02:30:42 +0800 Subject: [PATCH 101/134] feat: update --- .../editor/data-sync.controller.ts | 13 +++- .../editor/formula-editor.controller.ts | 11 +-- .../src/views/formula-bar/FormulaBar.tsx | 73 +++++++++++-------- .../src/views/formula-bar/index.module.less | 4 + 4 files changed, 62 insertions(+), 39 deletions(-) diff --git a/packages/sheets-ui/src/controllers/editor/data-sync.controller.ts b/packages/sheets-ui/src/controllers/editor/data-sync.controller.ts index 6d56059ed70..cc97e1ab715 100644 --- a/packages/sheets-ui/src/controllers/editor/data-sync.controller.ts +++ b/packages/sheets-ui/src/controllers/editor/data-sync.controller.ts @@ -25,6 +25,7 @@ import { ReplaceSnapshotCommand } from '@univerjs/docs-ui'; import { DeviceInputEventType, IRenderManagerService } from '@univerjs/engine-render'; import { MoveRangeMutation, RangeProtectionRuleModel, SetRangeValuesMutation, WorksheetProtectionRuleModel } from '@univerjs/sheets'; import { IEditorBridgeService } from '../../services/editor-bridge.service'; +import { IFormulaEditorManagerService } from '../../services/editor/formula-editor-manager.service'; import { FormulaEditorController } from './formula-editor.controller'; const formulaEditorStyle: IDocumentStyle = { @@ -59,7 +60,8 @@ export class EditorDataSyncController extends Disposable { @ICommandService private readonly _commandService: ICommandService, @Inject(RangeProtectionRuleModel) private readonly _rangeProtectionRuleModel: RangeProtectionRuleModel, @Inject(WorksheetProtectionRuleModel) private readonly _worksheetProtectionRuleModel: WorksheetProtectionRuleModel, - @Inject(FormulaEditorController) private readonly _formulaEditorController: FormulaEditorController + @Inject(FormulaEditorController) private readonly _formulaEditorController: FormulaEditorController, + @IFormulaEditorManagerService private readonly _formulaEditorManagerService: IFormulaEditorManagerService ) { super(); @@ -279,7 +281,14 @@ export class EditorDataSyncController extends Disposable { renderConfig = {}; snapshot.documentStyle.renderConfig = renderConfig; } - + const position = this._formulaEditorManagerService.getPosition(); + if (position) { + const width = position.width; + snapshot.documentStyle.pageSize = { + width, + height: Infinity, + }; + } if ((body?.dataStream ?? '').startsWith('=')) { renderConfig.isRenderStyle = BooleanNumber.TRUE; } else { diff --git a/packages/sheets-ui/src/controllers/editor/formula-editor.controller.ts b/packages/sheets-ui/src/controllers/editor/formula-editor.controller.ts index e97c9bc4775..cefe1a6353a 100644 --- a/packages/sheets-ui/src/controllers/editor/formula-editor.controller.ts +++ b/packages/sheets-ui/src/controllers/editor/formula-editor.controller.ts @@ -38,10 +38,10 @@ import { } from '@univerjs/docs'; import { CoverContentCommand, VIEWPORT_KEY as DOC_VIEWPORT_KEY } from '@univerjs/docs-ui'; import { DeviceInputEventType, IRenderManagerService, ScrollBar } from '@univerjs/engine-render'; -import { takeUntil } from 'rxjs'; +import { combineLatest, filter, takeUntil } from 'rxjs'; import { getEditorObject } from '../../basics/editor/get-editor-object'; -import { IFormulaEditorManagerService } from '../../services/editor/formula-editor-manager.service'; import { IEditorBridgeService } from '../../services/editor-bridge.service'; +import { IFormulaEditorManagerService } from '../../services/editor/formula-editor-manager.service'; export class FormulaEditorController extends RxDisposable { private _loadedMap = new WeakSet(); @@ -210,9 +210,10 @@ export class FormulaEditorController extends RxDisposable { // Listen to changes in the size of the formula editor container to set the size of the editor. private _syncEditorSize() { - this._formulaEditorManagerService.position$.pipe(takeUntil(this.dispose$)).subscribe((position) => { + // this._univerInstanceService. + const addFOrmulaBar$ = this._univerInstanceService.unitAdded$.pipe(filter((unit) => unit.getUnitId() === DOCS_FORMULA_BAR_EDITOR_UNIT_ID_KEY)); + this.disposeWithMe(combineLatest([this._formulaEditorManagerService.position$, addFOrmulaBar$]).subscribe(([position]) => { if (!position) return this._clearScheduledCallback(); - const editorObject = getEditorObject(DOCS_FORMULA_BAR_EDITOR_UNIT_ID_KEY, this._renderManagerService); const formulaEditorDataModel = this._univerInstanceService.getUniverDocInstance( DOCS_FORMULA_BAR_EDITOR_UNIT_ID_KEY @@ -227,7 +228,7 @@ export class FormulaEditorController extends RxDisposable { formulaEditorDataModel.updateDocumentDataPageSize(width); this.autoScroll(); this._scheduledCallback = requestIdleCallback(() => engine.resizeBySize(width, height)); - }); + })); } private _scheduledCallback: number = -1; diff --git a/packages/sheets-ui/src/views/formula-bar/FormulaBar.tsx b/packages/sheets-ui/src/views/formula-bar/FormulaBar.tsx index 3aa990c37f0..8368d77113b 100644 --- a/packages/sheets-ui/src/views/formula-bar/FormulaBar.tsx +++ b/packages/sheets-ui/src/views/formula-bar/FormulaBar.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ -import type { Nullable, Workbook } from '@univerjs/core'; +import type { Workbook } from '@univerjs/core'; import { DOCS_FORMULA_BAR_EDITOR_UNIT_ID_KEY, FOCUSING_FX_BAR_EDITOR, IContextService, IPermissionService, IUniverInstanceService, Rectangle, UniverInstanceType, useDependency, useObservable } from '@univerjs/core'; import { DeviceInputEventType } from '@univerjs/engine-render'; import { CheckMarkSingle, CloseSingle, DropdownSingle, FxSingle } from '@univerjs/icons'; @@ -60,6 +60,7 @@ export function FormulaBar() { const formulaAuxUIParts = useComponentsOfPart(SheetsUIPart.FORMULA_AUX); const contextService = useDependency(IContextService); const isFocusFxBar = contextService.getContextValue(FOCUSING_FX_BAR_EDITOR); + const ref = useRef(null); function getPermissionIds(unitId: string, subUnitId: string): string[] { return [ @@ -133,15 +134,20 @@ export function FormulaBar() { return () => subscription.unsubscribe(); }, [editorBridgeService.currentEditCellState$]); - function resizeCallBack(editor: Nullable) { - if (editor == null) { - return; - } + useEffect(() => { + if (ref.current) { + const handleResize = () => { + const editorRect = ref.current!.getBoundingClientRect(); + formulaEditorManagerService.setPosition(editorRect); + }; - const editorRect = editor.getBoundingClientRect(); + handleResize(); + const a = new ResizeObserver(handleResize); - formulaEditorManagerService.setPosition(editorRect); - } + a.observe(ref.current); + return () => a.disconnect(); + } + }, [formulaEditorManagerService]); function handleArrowClick() { setArrowDirection(arrowDirection === ArrowDirection.Down ? ArrowDirection.Up : ArrowDirection.Down); @@ -217,30 +223,33 @@ export function FormulaBar() {

-d!IsICWJ^+wbBG%qg}SRD^` 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 z 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 literal 73223 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

-
- {FormulaEditor && ( - {}} - isFocus={isFocusFxBar} - className={styles.formulaContent} - unitId={editState?.unitId} - subUnitId={editState?.sheetId} - isSupportAcrossSheet - resetSelectionOnBlur={false} - isSingle={false} - keyboradEventConfig={keyCodeConfig} - onFormulaSelectingChange={(isSelecting: 0 | 1 | 2) => { - isRefSelecting.current = isSelecting; - if (isSelecting) { - editorBridgeService.enableForceKeepVisible(); - } else { - editorBridgeService.disableForceKeepVisible(); - } - }} - /> - )} +
+
+ {FormulaEditor && ( + {}} + isFocus={isFocusFxBar} + className={styles.formulaContent} + unitId={editState?.unitId} + subUnitId={editState?.sheetId} + isSupportAcrossSheet + resetSelectionOnBlur={false} + isSingle={false} + keyboradEventConfig={keyCodeConfig} + onFormulaSelectingChange={(isSelecting: 0 | 1 | 2) => { + isRefSelecting.current = isSelecting; + if (isSelecting) { + editorBridgeService.enableForceKeepVisible(); + } else { + editorBridgeService.disableForceKeepVisible(); + } + }} + autoScrollbar={false} + /> + )} +
{arrowDirection === ArrowDirection.Down ? ( diff --git a/packages/sheets-ui/src/views/formula-bar/index.module.less b/packages/sheets-ui/src/views/formula-bar/index.module.less index ba6f7ec3afe..7ccaf5fdfab 100644 --- a/packages/sheets-ui/src/views/formula-bar/index.module.less +++ b/packages/sheets-ui/src/views/formula-bar/index.module.less @@ -87,6 +87,10 @@ } .formula-input { + flex: 1; + } + + .formula-container { overflow: hidden; display: flex; flex: 1; From 1862a71d74de1df8268a2f5170509a39b6dba594 Mon Sep 17 00:00:00 2001 From: zhangw Date: Fri, 27 Dec 2024 15:18:23 +0800 Subject: [PATCH 102/134] feat: update --- .../views/formula-editor/hooks/useSheetSelectionChange.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/sheets-formula-ui/src/views/formula-editor/hooks/useSheetSelectionChange.ts b/packages/sheets-formula-ui/src/views/formula-editor/hooks/useSheetSelectionChange.ts index f0762756e29..580ba5ee7c6 100644 --- a/packages/sheets-formula-ui/src/views/formula-editor/hooks/useSheetSelectionChange.ts +++ b/packages/sheets-formula-ui/src/views/formula-editor/hooks/useSheetSelectionChange.ts @@ -91,10 +91,10 @@ export const useSheetSelectionChange = ( const nodeIndex = findIndexFromSequenceNodes(sequenceNodes, offset, false); if (getIsNeedAddSelection()) { - if (nodeIndex === -1 && sequenceNodes.length) { - return; - } if (offset !== 0) { + if (nodeIndex === -1 && sequenceNodes.length) { + return; + } const range = selections[selections.length - 1]; const lastNodes = sequenceNodes.splice(nodeIndex + 1); const rangeSheetId = range.rangeWithCoord.sheetId ?? subUnitId; From b7c60b9f39f92ef38480e05babbe59e8cb5c593d Mon Sep 17 00:00:00 2001 From: zhangw Date: Fri, 27 Dec 2024 17:23:29 +0800 Subject: [PATCH 103/134] feat: update --- .../range-selector/hooks/useRefactorEffect.ts | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/packages/sheets-formula-ui/src/views/range-selector/hooks/useRefactorEffect.ts b/packages/sheets-formula-ui/src/views/range-selector/hooks/useRefactorEffect.ts index c291bf68250..385373cd359 100644 --- a/packages/sheets-formula-ui/src/views/range-selector/hooks/useRefactorEffect.ts +++ b/packages/sheets-formula-ui/src/views/range-selector/hooks/useRefactorEffect.ts @@ -30,27 +30,29 @@ export const useRefactorEffect = (isNeed: boolean, selecting: boolean, unitId: s const render = renderManagerService.getRenderById(unitId); const refSelectionsRenderService = render?.with(RefSelectionsRenderService); + useLayoutEffect(() => { - if (isNeed && selecting) { - const d1 = refSelectionsRenderService?.enableSelectionChanging(); - contextService.setContextValue(REF_SELECTIONS_ENABLED, true); + if (isNeed) { contextService.setContextValue(EDITOR_ACTIVATED, true); return () => { contextService.setContextValue(EDITOR_ACTIVATED, false); - contextService.setContextValue(REF_SELECTIONS_ENABLED, false); - d1?.dispose(); + refSelectionsService.clear(); }; } - }, [isNeed, selecting]); + }, [isNeed]); useLayoutEffect(() => { - if (isNeed) { + if (isNeed && selecting) { + const d1 = refSelectionsRenderService?.enableSelectionChanging(); + contextService.setContextValue(REF_SELECTIONS_ENABLED, true); + return () => { - refSelectionsService.clear(); + contextService.setContextValue(REF_SELECTIONS_ENABLED, false); + d1?.dispose(); }; } - }, [isNeed]); + }, [isNeed, selecting]); //right context controller useEffect(() => { From af5f7cd6d821982ac3ebad87d6884b43d77eadc6 Mon Sep 17 00:00:00 2001 From: zhangw Date: Fri, 27 Dec 2024 17:30:28 +0800 Subject: [PATCH 104/134] feat: update --- packages/sheets-formula-ui/src/views/formula-editor/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/sheets-formula-ui/src/views/formula-editor/index.tsx b/packages/sheets-formula-ui/src/views/formula-editor/index.tsx index 968395e0599..c385b539801 100644 --- a/packages/sheets-formula-ui/src/views/formula-editor/index.tsx +++ b/packages/sheets-formula-ui/src/views/formula-editor/index.tsx @@ -28,6 +28,7 @@ import { EMBEDDING_FORMULA_EDITOR } from '@univerjs/sheets-ui'; import { useEvent } from '@univerjs/ui'; import clsx from 'clsx'; import React, { useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react'; +import { debounceTime } from 'rxjs'; import { useEmitChange } from '../range-selector/hooks/useEmitChange'; import { useFocus } from '../range-selector/hooks/useFocus'; import { useFormulaToken } from '../range-selector/hooks/useFormulaToken'; @@ -138,7 +139,6 @@ export function FormulaEditor(props: IFormulaEditorProps) { const docFocusing = currentDoc?.getUnitId() === editorId; const refSelections = useRef([] as IRefSelection[]); const shouldMoveRefSelection = Boolean(isSelecting); - const needEmit = useEmitChange(sequenceNodes, (text: string) => { onChange(`=${text}`); }, editor); @@ -173,7 +173,7 @@ export function FormulaEditor(props: IFormulaEditorProps) { }, [isFocus]); useEffect(() => { - const sub = docSelectionRenderService?.onChangeByEvent$.subscribe(() => { + const sub = docSelectionRenderService?.onChangeByEvent$.pipe(debounceTime(30)).subscribe(() => { const formulaText = BuildTextUtils.transform.getPlainText(document?.getBody()?.dataStream ?? ''); highlight(formulaText, false, true); }); From 682d84efdf7cc51c9e4158b034ebbd4300c3a70e Mon Sep 17 00:00:00 2001 From: zhangw Date: Fri, 27 Dec 2024 17:48:38 +0800 Subject: [PATCH 105/134] feat: update --- .../src/views/thread-comment-editor/index.tsx | 6 ------ 1 file changed, 6 deletions(-) diff --git a/packages/thread-comment-ui/src/views/thread-comment-editor/index.tsx b/packages/thread-comment-ui/src/views/thread-comment-editor/index.tsx index 85ac407ffb1..73dfe73b7b4 100644 --- a/packages/thread-comment-ui/src/views/thread-comment-editor/index.tsx +++ b/packages/thread-comment-ui/src/views/thread-comment-editor/index.tsx @@ -19,7 +19,6 @@ import type { MentionProps } from '@univerjs/design'; import type { IThreadComment } from '@univerjs/thread-comment'; import { ICommandService, IMentionIOService, LocaleService, UniverInstanceType, useDependency } from '@univerjs/core'; import { Button, Mention, Mentions } from '@univerjs/design'; -import { DocSelectionManagerService } from '@univerjs/docs'; import { DocSelectionRenderService } from '@univerjs/docs-ui'; import { IRenderManagerService } from '@univerjs/engine-render'; import React, { forwardRef, useImperativeHandle, useRef, useState } from 'react'; @@ -61,7 +60,6 @@ export const ThreadCommentEditor = forwardRef { - const activeRange = docSelectionManagerService.getActiveTextRange(); - if (activeRange && activeRange.collapsed) { - docSelectionRenderService?.removeAllRanges(); - } docSelectionRenderService?.blur(); setEditing(true); }} From 76a8b9e99b67aa16e59f91581c097eaf3e79271f Mon Sep 17 00:00:00 2001 From: zhangw Date: Fri, 27 Dec 2024 20:01:21 +0800 Subject: [PATCH 106/134] feat: update --- .../docs-ui/src/services/editor/editor.ts | 4 + .../help-function/HelpFunction.tsx | 148 +++++++----------- .../formula-editor/hooks/useEditorPostion.ts | 74 +++++++++ .../search-function/SearchFunction.tsx | 31 ++-- .../src/views/components/popup/RectPopup.tsx | 2 + 5 files changed, 147 insertions(+), 112 deletions(-) create mode 100644 packages/sheets-formula-ui/src/views/formula-editor/hooks/useEditorPostion.ts diff --git a/packages/docs-ui/src/services/editor/editor.ts b/packages/docs-ui/src/services/editor/editor.ts index 709880b86d6..1459f716965 100644 --- a/packages/docs-ui/src/services/editor/editor.ts +++ b/packages/docs-ui/src/services/editor/editor.ts @@ -382,6 +382,10 @@ export class Editor extends Disposable implements IEditor { return this._param.editorDom.getBoundingClientRect(); } + get editorDOM() { + return this._param.editorDom; + } + isVisible() { return this._param.visible; } diff --git a/packages/sheets-formula-ui/src/views/formula-editor/help-function/HelpFunction.tsx b/packages/sheets-formula-ui/src/views/formula-editor/help-function/HelpFunction.tsx index 18395e8eeca..d66bb751f28 100644 --- a/packages/sheets-formula-ui/src/views/formula-editor/help-function/HelpFunction.tsx +++ b/packages/sheets-formula-ui/src/views/formula-editor/help-function/HelpFunction.tsx @@ -16,15 +16,12 @@ import type { IFunctionInfo, IFunctionParam } from '@univerjs/engine-formula'; import { LocaleService, useDependency } from '@univerjs/core'; -import { Popup } from '@univerjs/design'; -import { IEditorService } from '@univerjs/docs-ui'; import { CloseSingle, MoreSingle } from '@univerjs/icons'; -import { ISidebarService } from '@univerjs/ui'; +import { RectPopup } from '@univerjs/ui'; import React, { useEffect, useMemo, useState } from 'react'; -import { throttleTime } from 'rxjs'; import { generateParam } from '../../../services/utils'; -import { useResizeScrollObserver } from '../hooks/useResizeScrollObserver'; +import { useEditorPostion } from '../hooks/useEditorPostion'; import styles from './index.module.less'; interface IHelpFunctionProps { @@ -38,99 +35,67 @@ const noop = () => { }; export function HelpFunction(props: IHelpFunctionProps) { const { functionInfo, paramIndex, editorId, onParamsSwitch = noop, onClose = noop } = props; - const editorService = useDependency(IEditorService); - const sidebarService = useDependency(ISidebarService); - const visible = useMemo(() => !!functionInfo && paramIndex >= 0, [functionInfo, paramIndex]); - const [contentVisible, setContentVisible] = useState(true); - const [offset, setOffset] = useState<[number, number]>([0, 0]); const localeService = useDependency(LocaleService); const required = localeService.t('formula.prompt.required'); const optional = localeService.t('formula.prompt.optional'); - - useResizeScrollObserver(updatePosition); - - useEffect(() => { - const sidebarSubscription = sidebarService.scrollEvent$.pipe(throttleTime(100)).subscribe(updatePosition); - - return () => { - sidebarSubscription.unsubscribe(); - }; - }, []); - useEffect(() => { - const doc = editorService.getEditor(editorId); - if (!doc) { - return; - } - const position = doc.getBoundingClientRect(); - const { left, top, height } = position; - setOffset([left, top + height]); - }, [functionInfo, paramIndex, editorId]); - - function updatePosition() { - const doc = editorService.getEditor(editorId); - if (!doc) { - return; - } - const position = doc.getBoundingClientRect(); - const { left, top, height } = position; - setOffset([left, top + height]); - return position; - } + const [position$, refreshPosition] = useEditorPostion(editorId); function handleSwitchActive(paramIndex: number) { onParamsSwitch && onParamsSwitch(paramIndex); } - return ( - - {functionInfo - ? ( -
-
- -
-
setContentVisible(!contentVisible)} - > - -
-
- -
+ useEffect(() => { + refreshPosition(); + }, [functionInfo, paramIndex, editorId]); + + return visible && functionInfo + ? ( + +
+
+ +
+
setContentVisible(!contentVisible)} + > + +
+
+
- -
-
- item.example) - .join(',')})`} - /> - - {functionInfo && +
+
+
+ item.example) + .join(',')})`} + /> + + {functionInfo && functionInfo.functionParameter && functionInfo.functionParameter.map((item: IFunctionParam, i: number) => ( ))} -
- ) - : ( - <> - )} - - ); +
+ + ) + : null; } interface IParamsProps { diff --git a/packages/sheets-formula-ui/src/views/formula-editor/hooks/useEditorPostion.ts b/packages/sheets-formula-ui/src/views/formula-editor/hooks/useEditorPostion.ts new file mode 100644 index 00000000000..f06fab439eb --- /dev/null +++ b/packages/sheets-formula-ui/src/views/formula-editor/hooks/useEditorPostion.ts @@ -0,0 +1,74 @@ +/** + * 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 { DisposableCollection, IUniverInstanceService, useDependency } from '@univerjs/core'; +import { IEditorService } from '@univerjs/docs-ui'; +import { ISidebarService, useEvent } from '@univerjs/ui'; +import { useEffect, useMemo } from 'react'; +import { BehaviorSubject, throttleTime } from 'rxjs'; +import useResizeScrollObserver from './useResizeScrollObserver'; + +export function useEditorPostion(editorId: string) { + const editorService = useDependency(IEditorService); + const position$ = useMemo(() => new BehaviorSubject({ left: -999, top: -999, right: -999, bottom: -999 }), []); + const sidebarService = useDependency(ISidebarService); + const univerInstanceService = useDependency(IUniverInstanceService); + const updatePosition = useEvent(() => { + const doc = editorService.getEditor(editorId); + if (!doc) { + return; + } + const position = doc.getBoundingClientRect(); + const { left, top, right, bottom } = position; + const current = position$.getValue(); + if (current.left === left && current.top === top && current.right === right && current.bottom === bottom) { + return; + } + position$.next({ left, right, top, bottom }); + return position; + }); + + useEffect(() => { + const disposableCollection = new DisposableCollection(); + const handleEditor = () => { + updatePosition(); + }; + + handleEditor(); + const sub = univerInstanceService.unitAdded$.subscribe((unit) => { + if (unit.getUnitId() === editorId) { + handleEditor(); + } + }); + + return () => { + sub.unsubscribe(); + disposableCollection.dispose(); + }; + }, [editorId, editorService, univerInstanceService.unitAdded$, updatePosition]); + + useResizeScrollObserver(updatePosition); + + useEffect(() => { + const sidebarSubscription = sidebarService.scrollEvent$.pipe(throttleTime(100)).subscribe(updatePosition); + + return () => { + sidebarSubscription.unsubscribe(); + }; + }, []); + + return [position$, updatePosition] as const; +} diff --git a/packages/sheets-formula-ui/src/views/formula-editor/search-function/SearchFunction.tsx b/packages/sheets-formula-ui/src/views/formula-editor/search-function/SearchFunction.tsx index b93734b7498..705042530ea 100644 --- a/packages/sheets-formula-ui/src/views/formula-editor/search-function/SearchFunction.tsx +++ b/packages/sheets-formula-ui/src/views/formula-editor/search-function/SearchFunction.tsx @@ -16,11 +16,11 @@ import type { ISearchItem } from '@univerjs/sheets-formula'; import { CommandType, DisposableCollection, ICommandService, useDependency } from '@univerjs/core'; -import { Popup } from '@univerjs/design'; import { IEditorService } from '@univerjs/docs-ui'; import { DeviceInputEventType } from '@univerjs/engine-render'; -import { IShortcutService, KeyCode } from '@univerjs/ui'; +import { IShortcutService, KeyCode, RectPopup } from '@univerjs/ui'; import React, { forwardRef, useEffect, useMemo, useRef, useState } from 'react'; +import { useEditorPostion } from '../hooks/useEditorPostion'; import { useStateRef } from '../hooks/useStateRef'; import styles from './index.module.less'; @@ -43,24 +43,11 @@ function SearchFunctionFactory(props: ISearchFunctionProps, ref: any) { const visible = useMemo(() => !!searchList.length, [searchList]); const ulRef = useRef(); const [active, activeSet] = useState(0); - const [offset, setOffset] = useState<[number, number]>([0, 0]); const isEnableMouseEnterOrOut = useRef(false); - + const [position$, updatePosition] = useEditorPostion(editorId); const stateRef = useStateRef({ searchList, active }); const editor = editorService.getEditor(editorId); - useEffect(() => { - const editor = editorService.getEditor(editorId); - const position = editor?.getBoundingClientRect(); - if (position == null) { - return; - } - const { left, top, height } = position; - - setOffset([left, top + height]); - activeSet(0); // Reset active state - }, [searchText, searchList]); - function handleLiMouseEnter(index: number) { if (!isEnableMouseEnterOrOut.current) { return; @@ -144,6 +131,12 @@ function SearchFunctionFactory(props: ISearchFunctionProps, ref: any) { }; }, [searchList]); + useEffect(() => { + if (visible) { + updatePosition(); + } + }, [searchText, searchList, visible]); + function scrollToVisible(liIndex: number) { // Get the
  • element const liElement = ulRef.current?.querySelectorAll(`.${styles.formulaSearchFunctionItem}`)[ @@ -193,8 +186,8 @@ function SearchFunctionFactory(props: ISearchFunctionProps, ref: any) { }; }, []); - return searchList.length > 0 && ( - + return searchList.length > 0 && visible && ( +
      { @@ -231,6 +224,6 @@ function SearchFunctionFactory(props: ISearchFunctionProps, ref: any) { ))}
    -
    + ); } diff --git a/packages/ui/src/views/components/popup/RectPopup.tsx b/packages/ui/src/views/components/popup/RectPopup.tsx index 55003f9cea8..5921d9fc70f 100644 --- a/packages/ui/src/views/components/popup/RectPopup.tsx +++ b/packages/ui/src/views/components/popup/RectPopup.tsx @@ -70,7 +70,9 @@ function calcPopupPosition(layout: IPopupLayoutInfo): { top: number; left: numbe if (direction === 'vertical' || direction.includes('top') || direction.includes('bottom')) { const { left: startX, top: startY, right: endX, bottom: endY } = position; const verticalStyle = (direction === 'vertical' && endY > containerHeight - height - PUSHING_MINIMUM_GAP) || direction.indexOf('top') > -1 + // top ? { top: Math.max(startY - height, PUSHING_MINIMUM_GAP) } + // bottom : { top: Math.min(endY, containerHeight - height - PUSHING_MINIMUM_GAP) }; let horizontalStyle; From 8bf7383e3532e8afed7bf8b92e34c8bd12b646ac Mon Sep 17 00:00:00 2001 From: zhangw Date: Fri, 27 Dec 2024 22:17:30 +0800 Subject: [PATCH 107/134] feat: update --- .../help-function/HelpFunction.tsx | 8 ++---- .../formula-editor/hooks/useEditorPostion.ts | 27 ++++++------------- .../search-function/SearchFunction.tsx | 8 +----- 3 files changed, 11 insertions(+), 32 deletions(-) diff --git a/packages/sheets-formula-ui/src/views/formula-editor/help-function/HelpFunction.tsx b/packages/sheets-formula-ui/src/views/formula-editor/help-function/HelpFunction.tsx index d66bb751f28..649a5a45b36 100644 --- a/packages/sheets-formula-ui/src/views/formula-editor/help-function/HelpFunction.tsx +++ b/packages/sheets-formula-ui/src/views/formula-editor/help-function/HelpFunction.tsx @@ -19,7 +19,7 @@ import { LocaleService, useDependency } from '@univerjs/core'; import { CloseSingle, MoreSingle } from '@univerjs/icons'; import { RectPopup } from '@univerjs/ui'; -import React, { useEffect, useMemo, useState } from 'react'; +import React, { useMemo, useState } from 'react'; import { generateParam } from '../../../services/utils'; import { useEditorPostion } from '../hooks/useEditorPostion'; import styles from './index.module.less'; @@ -40,16 +40,12 @@ export function HelpFunction(props: IHelpFunctionProps) { const localeService = useDependency(LocaleService); const required = localeService.t('formula.prompt.required'); const optional = localeService.t('formula.prompt.optional'); - const [position$, refreshPosition] = useEditorPostion(editorId); + const [position$] = useEditorPostion(editorId, visible, [functionInfo, paramIndex]); function handleSwitchActive(paramIndex: number) { onParamsSwitch && onParamsSwitch(paramIndex); } - useEffect(() => { - refreshPosition(); - }, [functionInfo, paramIndex, editorId]); - return visible && functionInfo ? ( diff --git a/packages/sheets-formula-ui/src/views/formula-editor/hooks/useEditorPostion.ts b/packages/sheets-formula-ui/src/views/formula-editor/hooks/useEditorPostion.ts index f06fab439eb..46e44a1280c 100644 --- a/packages/sheets-formula-ui/src/views/formula-editor/hooks/useEditorPostion.ts +++ b/packages/sheets-formula-ui/src/views/formula-editor/hooks/useEditorPostion.ts @@ -14,14 +14,14 @@ * limitations under the License. */ -import { DisposableCollection, IUniverInstanceService, useDependency } from '@univerjs/core'; +import { IUniverInstanceService, useDependency } from '@univerjs/core'; import { IEditorService } from '@univerjs/docs-ui'; import { ISidebarService, useEvent } from '@univerjs/ui'; import { useEffect, useMemo } from 'react'; import { BehaviorSubject, throttleTime } from 'rxjs'; import useResizeScrollObserver from './useResizeScrollObserver'; -export function useEditorPostion(editorId: string) { +export function useEditorPostion(editorId: string, ready: boolean, deps?: any[]) { const editorService = useDependency(IEditorService); const position$ = useMemo(() => new BehaviorSubject({ left: -999, top: -999, right: -999, bottom: -999 }), []); const sidebarService = useDependency(ISidebarService); @@ -42,23 +42,12 @@ export function useEditorPostion(editorId: string) { }); useEffect(() => { - const disposableCollection = new DisposableCollection(); - const handleEditor = () => { - updatePosition(); - }; - - handleEditor(); - const sub = univerInstanceService.unitAdded$.subscribe((unit) => { - if (unit.getUnitId() === editorId) { - handleEditor(); - } - }); - - return () => { - sub.unsubscribe(); - disposableCollection.dispose(); - }; - }, [editorId, editorService, univerInstanceService.unitAdded$, updatePosition]); + if (!ready) { + return; + } + updatePosition(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [editorId, editorService, univerInstanceService.unitAdded$, updatePosition, ready, ...(deps ?? [])]); useResizeScrollObserver(updatePosition); diff --git a/packages/sheets-formula-ui/src/views/formula-editor/search-function/SearchFunction.tsx b/packages/sheets-formula-ui/src/views/formula-editor/search-function/SearchFunction.tsx index 705042530ea..461d6d5fd2e 100644 --- a/packages/sheets-formula-ui/src/views/formula-editor/search-function/SearchFunction.tsx +++ b/packages/sheets-formula-ui/src/views/formula-editor/search-function/SearchFunction.tsx @@ -44,7 +44,7 @@ function SearchFunctionFactory(props: ISearchFunctionProps, ref: any) { const ulRef = useRef(); const [active, activeSet] = useState(0); const isEnableMouseEnterOrOut = useRef(false); - const [position$, updatePosition] = useEditorPostion(editorId); + const [position$] = useEditorPostion(editorId, visible, [searchText, searchList]); const stateRef = useStateRef({ searchList, active }); const editor = editorService.getEditor(editorId); @@ -131,12 +131,6 @@ function SearchFunctionFactory(props: ISearchFunctionProps, ref: any) { }; }, [searchList]); - useEffect(() => { - if (visible) { - updatePosition(); - } - }, [searchText, searchList, visible]); - function scrollToVisible(liIndex: number) { // Get the
  • element const liElement = ulRef.current?.querySelectorAll(`.${styles.formulaSearchFunctionItem}`)[ From a9414f516a48f6b0636d8f54ba90889872a4d965 Mon Sep 17 00:00:00 2001 From: zhangw Date: Fri, 27 Dec 2024 23:45:44 +0800 Subject: [PATCH 108/134] feat: update --- .../services/selection/doc-selection-render.service.ts | 9 ++++----- .../sheets-formula-ui/src/views/formula-editor/index.tsx | 4 ++-- .../formula-editor/search-function/SearchFunction.tsx | 2 +- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/packages/docs-ui/src/services/selection/doc-selection-render.service.ts b/packages/docs-ui/src/services/selection/doc-selection-render.service.ts index 7e1553ed3f3..c2dcabac57a 100644 --- a/packages/docs-ui/src/services/selection/doc-selection-render.service.ts +++ b/packages/docs-ui/src/services/selection/doc-selection-render.service.ts @@ -21,8 +21,8 @@ import type { RectRange } from './rect-range'; import { DataStreamTreeTokenType, DOC_RANGE_TYPE, ILogService, Inject, IUniverInstanceService, RxDisposable, UniverInstanceType } from '@univerjs/core'; import { DocSkeletonManagerService } from '@univerjs/docs'; import { CURSOR_TYPE, getSystemHighlightColor, GlyphType, NORMAL_TEXT_SELECTION_PLUGIN_STYLE, PageLayoutType, ScrollTimer, Vector2 } from '@univerjs/engine-render'; -import { ILayoutService } from '@univerjs/ui'; -import { BehaviorSubject, fromEvent, merge, Subject, takeUntil } from 'rxjs'; +import { ILayoutService, KeyCode } from '@univerjs/ui'; +import { BehaviorSubject, filter, fromEvent, merge, Subject, takeUntil } from 'rxjs'; import { getCanvasOffsetByEngine, getParagraphInfoByGlyph, getRangeListFromCharIndex, getRangeListFromSelection, getRectRangeFromCharIndex, getTextRangeFromCharIndex, serializeRectRange, serializeTextRange } from './selection-utils'; import { TextRange } from './text-range'; @@ -57,9 +57,8 @@ export class DocSelectionRenderService extends RxDisposable implements IRenderMo readonly onChangeByEvent$ = merge( this._onInput$, - this._onKeydown$, - this._onCompositionend$, - this._onKeydown$ + this._onKeydown$.pipe(filter((e) => (e.event as KeyboardEvent).keyCode === KeyCode.BACKSPACE)), + this._onCompositionend$ ); private readonly _onPaste$ = new Subject(); diff --git a/packages/sheets-formula-ui/src/views/formula-editor/index.tsx b/packages/sheets-formula-ui/src/views/formula-editor/index.tsx index c385b539801..8f9940032de 100644 --- a/packages/sheets-formula-ui/src/views/formula-editor/index.tsx +++ b/packages/sheets-formula-ui/src/views/formula-editor/index.tsx @@ -28,7 +28,6 @@ import { EMBEDDING_FORMULA_EDITOR } from '@univerjs/sheets-ui'; import { useEvent } from '@univerjs/ui'; import clsx from 'clsx'; import React, { useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react'; -import { debounceTime } from 'rxjs'; import { useEmitChange } from '../range-selector/hooks/useEmitChange'; import { useFocus } from '../range-selector/hooks/useFocus'; import { useFormulaToken } from '../range-selector/hooks/useFormulaToken'; @@ -173,7 +172,7 @@ export function FormulaEditor(props: IFormulaEditorProps) { }, [isFocus]); useEffect(() => { - const sub = docSelectionRenderService?.onChangeByEvent$.pipe(debounceTime(30)).subscribe(() => { + const sub = docSelectionRenderService?.onChangeByEvent$.subscribe((e) => { const formulaText = BuildTextUtils.transform.getPlainText(document?.getBody()?.dataStream ?? ''); highlight(formulaText, false, true); }); @@ -320,6 +319,7 @@ export function FormulaEditor(props: IFormulaEditorProps) { editorId={editorId} searchList={searchList} onSelect={handleFunctionSelect} + onClose={() => resetFormulaSearch()} ref={searchFunctionRef} > diff --git a/packages/sheets-formula-ui/src/views/formula-editor/search-function/SearchFunction.tsx b/packages/sheets-formula-ui/src/views/formula-editor/search-function/SearchFunction.tsx index 461d6d5fd2e..8a1644568d5 100644 --- a/packages/sheets-formula-ui/src/views/formula-editor/search-function/SearchFunction.tsx +++ b/packages/sheets-formula-ui/src/views/formula-editor/search-function/SearchFunction.tsx @@ -97,7 +97,7 @@ function SearchFunctionFactory(props: ISearchFunctionProps, ref: any) { break; } case KeyCode.ESC: { - onSelect(''); + onClose(); break; } } From 4bc44b756c6f2b4cc5fac6dc3615c19a8a70467d Mon Sep 17 00:00:00 2001 From: zhangw Date: Sat, 28 Dec 2024 00:02:24 +0800 Subject: [PATCH 109/134] feat: update --- .../help-function/HelpFunction.tsx | 126 ++++++++++-------- .../src/views/formula-editor/index.tsx | 51 ++++--- .../search-function/SearchFunction.tsx | 38 ++++-- 3 files changed, 116 insertions(+), 99 deletions(-) diff --git a/packages/sheets-formula-ui/src/views/formula-editor/help-function/HelpFunction.tsx b/packages/sheets-formula-ui/src/views/formula-editor/help-function/HelpFunction.tsx index 649a5a45b36..731f013904e 100644 --- a/packages/sheets-formula-ui/src/views/formula-editor/help-function/HelpFunction.tsx +++ b/packages/sheets-formula-ui/src/views/formula-editor/help-function/HelpFunction.tsx @@ -14,38 +14,98 @@ * limitations under the License. */ -import type { IFunctionInfo, IFunctionParam } from '@univerjs/engine-formula'; +import type { Editor } from '@univerjs/docs-ui'; +import type { IFunctionParam } from '@univerjs/engine-formula'; import { LocaleService, useDependency } from '@univerjs/core'; import { CloseSingle, MoreSingle } from '@univerjs/icons'; -import { RectPopup } from '@univerjs/ui'; +import { RectPopup } from '@univerjs/ui'; import React, { useMemo, useState } from 'react'; import { generateParam } from '../../../services/utils'; import { useEditorPostion } from '../hooks/useEditorPostion'; +import { useFormulaDescribe } from '../hooks/useFormulaDescribe'; import styles from './index.module.less'; +interface IParamsProps { + className?: string; + title?: string; + value?: string; +} + +const Params = ({ className, title, value }: IParamsProps) => ( +
    +
    + {title} +
    +
    {value}
    +
    +); + +interface IHelpProps { + prefix?: string; + value?: IFunctionParam[]; + active: number; + onClick: (paramIndex: number) => void; +} + +const Help = (props: IHelpProps) => { + const { prefix, value, active, onClick } = props; + return ( +
    + + {prefix} + ( + + {value && + value.map((item: IFunctionParam, i: number) => ( + + onClick(i)} + > + {generateParam(item)} + + {i === value.length - 1 ? '' : ','} + + ))} + ) +
    + ); +}; + interface IHelpFunctionProps { - functionInfo?: IFunctionInfo; - paramIndex: number; - editorId: string; onParamsSwitch?: (index: number) => void; onClose?: () => void; + editor: Editor; + isFocus: boolean; + formulaText: string; }; + const noop = () => { }; export function HelpFunction(props: IHelpFunctionProps) { - const { functionInfo, paramIndex, editorId, onParamsSwitch = noop, onClose = noop } = props; - + const { onParamsSwitch = noop, onClose: propColose = noop, isFocus, editor, formulaText } = props; + const { functionInfo, paramIndex, reset } = useFormulaDescribe(isFocus, formulaText, editor); const visible = useMemo(() => !!functionInfo && paramIndex >= 0, [functionInfo, paramIndex]); const [contentVisible, setContentVisible] = useState(true); const localeService = useDependency(LocaleService); const required = localeService.t('formula.prompt.required'); const optional = localeService.t('formula.prompt.optional'); + const editorId = editor.getEditorId(); const [position$] = useEditorPostion(editorId, visible, [functionInfo, paramIndex]); function handleSwitchActive(paramIndex: number) { onParamsSwitch && onParamsSwitch(paramIndex); } + const onClose = () => { + reset(); + propColose(); + }; + return visible && functionInfo ? ( @@ -108,55 +168,3 @@ export function HelpFunction(props: IHelpFunctionProps) { ) : null; } - -interface IParamsProps { - className?: string; - title?: string; - value?: string; -} - -const Params = (props: IParamsProps) => ( -
    -
    - {props.title} -
    -
    {props.value}
    -
    -); - -interface IHelpProps { - prefix?: string; - value?: IFunctionParam[]; - active: number; - onClick: (paramIndex: number) => void; -} - -const Help = (props: IHelpProps) => { - const { prefix, value, active, onClick } = props; - return ( -
    - - {prefix} - ( - - {value && - value.map((item: IFunctionParam, i: number) => ( - // TODO@Dushusir: more params needs to be active - - onClick(i)} - > - {generateParam(item)} - - {i === value.length - 1 ? '' : ','} - - ))} - ) -
    - ); -}; diff --git a/packages/sheets-formula-ui/src/views/formula-editor/index.tsx b/packages/sheets-formula-ui/src/views/formula-editor/index.tsx index 8f9940032de..4cabca36303 100644 --- a/packages/sheets-formula-ui/src/views/formula-editor/index.tsx +++ b/packages/sheets-formula-ui/src/views/formula-editor/index.tsx @@ -39,8 +39,6 @@ import { useResetSelection } from '../range-selector/hooks/useResetSelection'; import { useResize } from '../range-selector/hooks/useResize'; import { useSwitchSheet } from '../range-selector/hooks/useSwitchSheet'; import { HelpFunction } from './help-function/HelpFunction'; -import { useFormulaDescribe } from './hooks/useFormulaDescribe'; -import { useFormulaSearch } from './hooks/useFormulaSearch'; import { useFormulaSelecting } from './hooks/useFormulaSelection'; import { useSheetSelectionChange } from './hooks/useSheetSelectionChange'; import { useVerify } from './hooks/useVerify'; @@ -260,11 +258,7 @@ export function FormulaEditor(props: IFormulaEditorProps) { useSheetSelectionChange(isFocus, unitId, subUnitId, sequenceNodes, refSelections, isSupportAcrossSheet, shouldMoveRefSelection, editor, handleSelectionChange); useSwitchSheet(isFocus, unitId, isSupportAcrossSheet, isFocusSet, onBlur, noop); - const { searchList, searchText, handlerFormulaReplace, reset: resetFormulaSearch } = useFormulaSearch(isFocus, sequenceNodes, editor); - const { functionInfo, paramIndex, reset } = useFormulaDescribe(isFocus, formulaText, editor); - - const handleFunctionSelect = (v: string) => { - const res = handlerFormulaReplace(v); + const handleFunctionSelect = (res: { text: string; offset: number }) => { if (res) { const selections = editor?.getSelectionRanges(); if (selections && selections.length === 1) { @@ -276,7 +270,6 @@ export function FormulaEditor(props: IFormulaEditorProps) { }, 30); } } - resetFormulaSearch(); focus(); highlight(`=${res.text}`); } @@ -304,25 +297,29 @@ export function FormulaEditor(props: IFormulaEditorProps) {
  • {errorText !== undefined ?
    {errorText}
    : null} - { - reset(); - focus(); - }} - > - - resetFormulaSearch()} - ref={searchFunctionRef} - > - + {editor + ? ( + { + focus(); + }} + /> + ) + : null} + {editor + ? ( + + ) + : null}
    ) ; diff --git a/packages/sheets-formula-ui/src/views/formula-editor/search-function/SearchFunction.tsx b/packages/sheets-formula-ui/src/views/formula-editor/search-function/SearchFunction.tsx index 8a1644568d5..7221ef37b1d 100644 --- a/packages/sheets-formula-ui/src/views/formula-editor/search-function/SearchFunction.tsx +++ b/packages/sheets-formula-ui/src/views/formula-editor/search-function/SearchFunction.tsx @@ -14,39 +14,50 @@ * limitations under the License. */ -import type { ISearchItem } from '@univerjs/sheets-formula'; +import type { Editor } from '@univerjs/docs-ui'; +import type { ISequenceNode } from '@univerjs/engine-formula'; import { CommandType, DisposableCollection, ICommandService, useDependency } from '@univerjs/core'; -import { IEditorService } from '@univerjs/docs-ui'; import { DeviceInputEventType } from '@univerjs/engine-render'; import { IShortcutService, KeyCode, RectPopup } from '@univerjs/ui'; import React, { forwardRef, useEffect, useMemo, useRef, useState } from 'react'; import { useEditorPostion } from '../hooks/useEditorPostion'; +import { useFormulaSearch } from '../hooks/useFormulaSearch'; import { useStateRef } from '../hooks/useStateRef'; import styles from './index.module.less'; interface ISearchFunctionProps { - searchList: ISearchItem[]; - searchText: string; - onSelect: (functionName: string) => void; + isFocus: boolean; + sequenceNodes: (string | ISequenceNode)[]; + onSelect: (data: { + text: string; + offset: number; + }) => void; onChange?: (functionName: string) => void; - editorId: string; + editor: Editor; onClose?: () => void; }; const noop = () => { }; export const SearchFunction = forwardRef(SearchFunctionFactory); function SearchFunctionFactory(props: ISearchFunctionProps, ref: any) { - const { searchText, searchList, onSelect, editorId, onClose = noop } = props; - const editorService = useDependency(IEditorService); + const { isFocus, sequenceNodes, onSelect, editor, onClose = noop } = props; + const editorId = editor.getEditorId(); const shortcutService = useDependency(IShortcutService); const commandService = useDependency(ICommandService); - + const { searchList, searchText, handlerFormulaReplace, reset: resetFormulaSearch } = useFormulaSearch(isFocus, sequenceNodes, editor); const visible = useMemo(() => !!searchList.length, [searchList]); const ulRef = useRef(); const [active, activeSet] = useState(0); const isEnableMouseEnterOrOut = useRef(false); const [position$] = useEditorPostion(editorId, visible, [searchText, searchList]); const stateRef = useStateRef({ searchList, active }); - const editor = editorService.getEditor(editorId); + + const handleFunctionSelect = (v: string) => { + const res = handlerFormulaReplace(v); + if (res) { + resetFormulaSearch(); + onSelect(res); + } + }; function handleLiMouseEnter(index: number) { if (!isEnableMouseEnterOrOut.current) { @@ -93,10 +104,11 @@ function SearchFunctionFactory(props: ISearchFunctionProps, ref: any) { case KeyCode.TAB: case KeyCode.ENTER: { const item = searchList[active]; - onSelect(item.name); + handleFunctionSelect(item.name); break; } case KeyCode.ESC: { + resetFormulaSearch(); onClose(); break; } @@ -193,7 +205,7 @@ function SearchFunctionFactory(props: ISearchFunctionProps, ref: any) { > {searchList.map((item, index) => (
  • { - onSelect(item.name); + handleFunctionSelect(item.name); if (editor) { editor.focus(); } From 64c544b924e9335f84e79b4c2fd8adef279d7b20 Mon Sep 17 00:00:00 2001 From: zhangw Date: Sat, 28 Dec 2024 00:45:03 +0800 Subject: [PATCH 110/134] feat: update --- packages/sheets-formula-ui/src/views/range-selector/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/sheets-formula-ui/src/views/range-selector/index.tsx b/packages/sheets-formula-ui/src/views/range-selector/index.tsx index acebd23913d..952de662298 100644 --- a/packages/sheets-formula-ui/src/views/range-selector/index.tsx +++ b/packages/sheets-formula-ui/src/views/range-selector/index.tsx @@ -185,7 +185,7 @@ export function RangeSelector(props: IRangeSelectorProps) { const focus = useFocus(editor); - const { checkScrollBar } = useResize(editor); + const { checkScrollBar } = useResize(editor, true, true); const getFormulaToken = useFormulaToken(); const sequenceNodes = useMemo(() => getFormulaToken(rangeString), [rangeString]); From 52cfb10ac16a963906fc294694bed16fd849c938 Mon Sep 17 00:00:00 2001 From: zhangw Date: Sat, 28 Dec 2024 01:27:12 +0800 Subject: [PATCH 111/134] feat: update --- .../views/formula-editor/hooks/useStateRef.ts | 4 ++-- .../editor-bridge.render-controller.ts | 17 +++++++++++++++-- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/packages/sheets-formula-ui/src/views/formula-editor/hooks/useStateRef.ts b/packages/sheets-formula-ui/src/views/formula-editor/hooks/useStateRef.ts index 37f2bff40ae..a9cdf3afc04 100644 --- a/packages/sheets-formula-ui/src/views/formula-editor/hooks/useStateRef.ts +++ b/packages/sheets-formula-ui/src/views/formula-editor/hooks/useStateRef.ts @@ -17,7 +17,7 @@ import { useRef } from 'react'; export const useStateRef = (value: T) => { - const cache = useRef(); + const cache = useRef(value); cache.current = value; - return cache as { current: T }; + return cache; }; diff --git a/packages/sheets-ui/src/controllers/render-controllers/editor-bridge.render-controller.ts b/packages/sheets-ui/src/controllers/render-controllers/editor-bridge.render-controller.ts index 597d458eb61..f7a25ea6113 100644 --- a/packages/sheets-ui/src/controllers/render-controllers/editor-bridge.render-controller.ts +++ b/packages/sheets-ui/src/controllers/render-controllers/editor-bridge.render-controller.ts @@ -17,7 +17,7 @@ import type { ICommandInfo, IDisposable, IExecutionOptions, ISelectionCell, Nullable, Workbook } from '@univerjs/core'; import type { IEditorInputConfig } from '@univerjs/docs-ui'; import type { IRender, IRenderContext, IRenderModule } from '@univerjs/engine-render'; -import type { ISelectionWithStyle } from '@univerjs/sheets'; +import type { ISelectionWithStyle, ISetRangeValuesMutationParams } from '@univerjs/sheets'; import type { ICurrentEditCellParam, IEditorBridgeServiceVisibleParam } from '../../services/editor-bridge.service'; import { DisposableCollection, DOCS_NORMAL_EDITOR_UNIT_ID_KEY, FOCUSING_FX_BAR_EDITOR, FOCUSING_SHEET, ICommandService, IContextService, Inject, IUniverInstanceService, RxDisposable, toDisposable, UniverInstanceType } from '@univerjs/core'; import { DocSelectionRenderService, IEditorService, IRangeSelectorService } from '@univerjs/docs-ui'; @@ -197,12 +197,25 @@ export class EditorBridgeRenderController extends RxDisposable implements IRende } private _commandExecutedListener(d: DisposableCollection) { - const refreshCommandSet = new Set([ClearSelectionFormatCommand.id, SetRangeValuesMutation.id, SetZoomRatioCommand.id]); + const refreshCommandSet = new Set([ClearSelectionFormatCommand.id, SetZoomRatioCommand.id]); d.add(this._commandService.onCommandExecuted((command: ICommandInfo) => { if (refreshCommandSet.has(command.id)) { if (this._editorBridgeService.isVisible().visible) return; this._editorBridgeService.refreshEditCellState(); } + + if (command.id === SetRangeValuesMutation.id) { + const params = command.params as ISetRangeValuesMutationParams; + const { cellValue, unitId, subUnitId } = params; + if (!cellValue) return; + const editCell = this._editorBridgeService.getEditLocation(); + if (editCell) { + const { unitId: editingUnitId, sheetId: editingSheetId, row, column } = editCell; + if (unitId === editingUnitId && subUnitId === editingSheetId && cellValue?.[row]?.[column]) { + this._editorBridgeService.refreshEditCellState(); + } + } + } })); d.add(this._commandService.beforeCommandExecuted((command: ICommandInfo, options?: IExecutionOptions) => { From 4da5269a095609fff1762c3bc37932e516b7230e Mon Sep 17 00:00:00 2001 From: zhangw Date: Sat, 28 Dec 2024 01:32:53 +0800 Subject: [PATCH 112/134] feat: update --- .../src/views/components/detail/index.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/sheets-data-validation-ui/src/views/components/detail/index.tsx b/packages/sheets-data-validation-ui/src/views/components/detail/index.tsx index 62bca6e8fa6..b775a97e66b 100644 --- a/packages/sheets-data-validation-ui/src/views/components/detail/index.tsx +++ b/packages/sheets-data-validation-ui/src/views/components/detail/index.tsx @@ -117,7 +117,7 @@ export function DataValidationDetail() { }; const handleUpdateRuleRanges = useEvent((rangeText: string) => { - const unitRanges = rangeText.split(',').map(deserializeRangeWithSheet).map((unitRange) => { + const unitRanges = rangeText.split(',').filter(Boolean).map(deserializeRangeWithSheet).map((unitRange) => { const sheetName = unitRange.sheetName; if (sheetName) { const sheetId = getSheetIdByName(univerInstanceService, unitRange.unitId, sheetName); @@ -127,7 +127,6 @@ export function DataValidationDetail() { ...unitRange, sheetId: '', }; }); - if (isUnitRangesEqual(unitRanges, localRanges)) { return; } From ebedbc67f3dfa49454c388f528ab0f7e29454e7f Mon Sep 17 00:00:00 2001 From: zhangw Date: Sat, 28 Dec 2024 01:38:54 +0800 Subject: [PATCH 113/134] feat: update --- packages/sheets-data-validation/src/models/rule-matrix.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/sheets-data-validation/src/models/rule-matrix.ts b/packages/sheets-data-validation/src/models/rule-matrix.ts index 008f34f6865..de6271755ec 100644 --- a/packages/sheets-data-validation/src/models/rule-matrix.ts +++ b/packages/sheets-data-validation/src/models/rule-matrix.ts @@ -86,7 +86,7 @@ export class RuleMatrix { return; } - const ranges = _ranges.map((range) => Range.transformRange(range, this._worksheet!)); + const ranges = Rectangle.mergeRanges(_ranges.map((range) => Range.transformRange(range, this._worksheet!))); this._map.forEach((value, key) => { const newRanges = Rectangle.subtractMulti(value, ranges); From 9bf591de782bb6dc52210f96a12a54141e569c70 Mon Sep 17 00:00:00 2001 From: zhangw Date: Sat, 28 Dec 2024 11:08:39 +0800 Subject: [PATCH 114/134] feat: update --- .../sheets-formula-ui/src/views/formula-editor/index.tsx | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/packages/sheets-formula-ui/src/views/formula-editor/index.tsx b/packages/sheets-formula-ui/src/views/formula-editor/index.tsx index 4cabca36303..66e72faa388 100644 --- a/packages/sheets-formula-ui/src/views/formula-editor/index.tsx +++ b/packages/sheets-formula-ui/src/views/formula-editor/index.tsx @@ -255,8 +255,8 @@ export function FormulaEditor(props: IFormulaEditorProps) { } }); - useSheetSelectionChange(isFocus, unitId, subUnitId, sequenceNodes, refSelections, isSupportAcrossSheet, shouldMoveRefSelection, editor, handleSelectionChange); - useSwitchSheet(isFocus, unitId, isSupportAcrossSheet, isFocusSet, onBlur, noop); + useSheetSelectionChange(isFocus && Boolean(isSelecting && docFocusing), unitId, subUnitId, sequenceNodes, refSelections, isSupportAcrossSheet, shouldMoveRefSelection, editor, handleSelectionChange); + useSwitchSheet(isFocus && Boolean(isSelecting && docFocusing), unitId, isSupportAcrossSheet, isFocusSet, onBlur, noop); const handleFunctionSelect = (res: { text: string; offset: number }) => { if (res) { @@ -303,9 +303,7 @@ export function FormulaEditor(props: IFormulaEditorProps) { editor={editor} isFocus={isFocus} formulaText={formulaText} - onClose={() => { - focus(); - }} + onClose={() => focus()} /> ) : null} From a9ce58152aa488c9cc614bd6c09b6c810463f1f2 Mon Sep 17 00:00:00 2001 From: zhangw Date: Sat, 28 Dec 2024 14:16:26 +0800 Subject: [PATCH 115/134] feat: update --- packages/docs-ui/src/index.ts | 3 +- .../docs-ui/src/services/editor/editor.ts | 7 + .../src/views/rich-text-editor/hooks/index.ts | 1 + .../views/rich-text-editor/hooks/useEditor.ts | 83 ++++++++++++ .../hooks/useKeyboardEvent.ts | 9 +- .../views/rich-text-editor/hooks/useResize.ts | 119 +++++++++++++++++ .../src/views/rich-text-editor/index.tsx | 97 ++++++-------- .../views/range-selector/hooks/useResize.ts | 104 +-------------- .../src/views/thread-comment-editor/index.tsx | 122 +++++++----------- .../src/views/thread-comment-editor/util.ts | 19 ++- .../src/views/thread-comment-tree/index.tsx | 32 +++-- 11 files changed, 336 insertions(+), 260 deletions(-) create mode 100644 packages/docs-ui/src/views/rich-text-editor/hooks/useEditor.ts create mode 100644 packages/docs-ui/src/views/rich-text-editor/hooks/useResize.ts diff --git a/packages/docs-ui/src/index.ts b/packages/docs-ui/src/index.ts index 6226fd91c7f..f6bee8c5f11 100644 --- a/packages/docs-ui/src/index.ts +++ b/packages/docs-ui/src/index.ts @@ -24,7 +24,7 @@ export { addCustomDecorationBySelectionFactory, addCustomDecorationFactory, dele export * from './basics/docs-view-key'; export { hasParagraphInTable } from './basics/paragraph'; export { docDrawingPositionToTransform, transformToDocDrawingPosition } from './basics/transform-position'; -export { type IKeyboardEventConfig, useKeyboardEvent } from './views/rich-text-editor/hooks'; +export { type IKeyboardEventConfig, useKeyboardEvent, useResize } from './views/rich-text-editor/hooks'; export { RichTextEditor } from './views/rich-text-editor'; export { getCommandSkeleton, getRichTextEditPath } from './commands/util'; // export { TextEditor } from './components/editor/TextEditor'; @@ -32,7 +32,6 @@ export { getCommandSkeleton, getRichTextEditPath } from './commands/util'; export { DocUIController } from './controllers/doc-ui.controller'; export { menuSchema as DocsUIMenuSchema } from './controllers/menu.schema'; export { DocBackScrollRenderController } from './controllers/render-controllers/back-scroll.render-controller'; - export { DocRenderController } from './controllers/render-controllers/doc.render-controller'; export * from './docs-ui-plugin'; export * from './services'; diff --git a/packages/docs-ui/src/services/editor/editor.ts b/packages/docs-ui/src/services/editor/editor.ts index 1459f716965..5bb9f8d1b9e 100644 --- a/packages/docs-ui/src/services/editor/editor.ts +++ b/packages/docs-ui/src/services/editor/editor.ts @@ -318,6 +318,13 @@ export class Editor extends Disposable implements IEditor { ...data, body: { dataStream: `${text}\r\n`, + paragraphs: [{ + startIndex: 0, + }], + customRanges: [], + sectionBreaks: [], + tables: [], + textRuns: [], }, }, resetCursor diff --git a/packages/docs-ui/src/views/rich-text-editor/hooks/index.ts b/packages/docs-ui/src/views/rich-text-editor/hooks/index.ts index f31ab518f60..5e0a9a86e6b 100644 --- a/packages/docs-ui/src/views/rich-text-editor/hooks/index.ts +++ b/packages/docs-ui/src/views/rich-text-editor/hooks/index.ts @@ -15,3 +15,4 @@ */ export { type IKeyboardEventConfig, useKeyboardEvent } from './useKeyboardEvent'; +export { useResize } from './useResize'; diff --git a/packages/docs-ui/src/views/rich-text-editor/hooks/useEditor.ts b/packages/docs-ui/src/views/rich-text-editor/hooks/useEditor.ts new file mode 100644 index 00000000000..393717be09d --- /dev/null +++ b/packages/docs-ui/src/views/rich-text-editor/hooks/useEditor.ts @@ -0,0 +1,83 @@ +/** + * 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 type { IDocumentData, Nullable } from '@univerjs/core'; +import type { RefObject } from 'react'; +import type { Editor } from '../../../services/editor/editor'; +import { useDependency } from '@univerjs/core'; +import { useLayoutEffect, useMemo, useState } from 'react'; +import { IEditorService } from '../../../services/editor/editor-manager.service'; + +export interface IUseEditorProps { + editorId: string; + initialValue: Nullable; + container: RefObject; + autoFocus?: boolean; + isSingle?: boolean; +} + +export function useEditor(opts: IUseEditorProps) { + const { editorId, initialValue, container, autoFocus: _autoFocus, isSingle } = opts; + const autoFocus = useMemo(() => _autoFocus ?? false, []); + const [editor, setEditor] = useState(); + const editorService = useDependency(IEditorService); + + useLayoutEffect(() => { + if (container.current) { + const snapshot: IDocumentData = { + body: { + dataStream: '\r\n', + textRuns: [], + customBlocks: [], + customDecorations: [], + customRanges: [], + paragraphs: [{ + startIndex: 0, + }], + }, + ...initialValue, + documentStyle: { + ...initialValue?.documentStyle, + pageSize: { + width: !isSingle ? container.current.clientWidth : Infinity, + height: Infinity, + }, + }, + id: editorId, + }; + const dispose = editorService.register( + { + autofocus: true, + editorUnitId: editorId, + initialSnapshot: snapshot, + }, + container.current + ); + const editor = editorService.getEditor(editorId)! as Editor; + setEditor(editor); + + if (autoFocus) { + editor.focus(); + } + + return () => { + dispose?.dispose(); + }; + } + }, []); + + return editor; +} diff --git a/packages/docs-ui/src/views/rich-text-editor/hooks/useKeyboardEvent.ts b/packages/docs-ui/src/views/rich-text-editor/hooks/useKeyboardEvent.ts index e501e226266..7f31211a9df 100644 --- a/packages/docs-ui/src/views/rich-text-editor/hooks/useKeyboardEvent.ts +++ b/packages/docs-ui/src/views/rich-text-editor/hooks/useKeyboardEvent.ts @@ -16,10 +16,10 @@ import type { KeyCode, MetaKeys } from '@univerjs/ui'; import type { Editor } from '../../../services/editor/editor'; -import { CommandType, DisposableCollection, ICommandService, useDependency } from '@univerjs/core'; +import { CommandType, DisposableCollection, generateRandomId, ICommandService, useDependency } from '@univerjs/core'; import { DeviceInputEventType } from '@univerjs/engine-render'; import { IShortcutService } from '@univerjs/ui'; -import { useEffect } from 'react'; +import { useEffect, useMemo } from 'react'; export interface IKeyboardEventConfig { keyCodes: { keyCode: KeyCode; metaKey?: MetaKeys }[]; @@ -29,13 +29,14 @@ export interface IKeyboardEventConfig { export function useKeyboardEvent(isNeed: boolean, config?: IKeyboardEventConfig, editor?: Editor) { const commandService = useDependency(ICommandService); const shortcutService = useDependency(IShortcutService); + const key = useMemo(() => generateRandomId(4), []); useEffect(() => { if (!editor || !isNeed || !config) { return; } const editorId = editor.getEditorId(); - const operationId = `sheet.operation.formula-embedding-editor-${editorId}-keyboard-event`; + const operationId = `sheet.operation.editor-${editorId}-keyboard-${key}`; const d = new DisposableCollection(); d.add(commandService.registerCommand({ @@ -66,5 +67,5 @@ export function useKeyboardEvent(isNeed: boolean, config?: IKeyboardEventConfig, return () => { d.dispose(); }; - }, [commandService, config, editor, isNeed, shortcutService]); + }, [commandService, config, editor, isNeed, key, shortcutService]); } diff --git a/packages/docs-ui/src/views/rich-text-editor/hooks/useResize.ts b/packages/docs-ui/src/views/rich-text-editor/hooks/useResize.ts new file mode 100644 index 00000000000..dd8b56e5c58 --- /dev/null +++ b/packages/docs-ui/src/views/rich-text-editor/hooks/useResize.ts @@ -0,0 +1,119 @@ +/** + * 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 type { Nullable } from '@univerjs/core'; +import type { Editor } from '../../../services/editor/editor'; +import { debounce } from '@univerjs/core'; +import { DocSkeletonManagerService } from '@univerjs/docs'; +import { ScrollBar } from '@univerjs/engine-render'; +import { useCallback, useEffect, useMemo } from 'react'; +import { VIEWPORT_KEY } from '../../../basics/docs-view-key'; + +// eslint-disable-next-line max-lines-per-function +export const useResize = (editor?: Editor, isSingle = true, autoScrollbar?: boolean) => { + const resize = useCallback(() => { + if (editor) { + const { scene, mainComponent } = editor.render; + const docSkeletonManagerService = editor.render.with(DocSkeletonManagerService); + const { width, height } = editor.getBoundingClientRect(); + + docSkeletonManagerService.getViewModel().getDataModel().updateDocumentDataPageSize(isSingle ? Infinity : width, Infinity); + scene.transformByState({ + width, + height, + }); + + mainComponent?.resize(width, height); + } + }, [editor, isSingle]); + + const checkScrollBar = useMemo(() => { + return debounce(() => { + if (!editor || !autoScrollbar) { + return; + } + + const docSkeletonManagerService = editor.render.with(DocSkeletonManagerService); + const skeleton = docSkeletonManagerService.getSkeleton(); + const { scene, mainComponent } = editor.render; + const viewportMain = scene.getViewport(VIEWPORT_KEY.VIEW_MAIN); + const { actualWidth, actualHeight } = skeleton.getActualSize(); + const { width, height } = editor.getBoundingClientRect(); + let scrollBar = viewportMain?.getScrollBar() as Nullable; + const contentWidth = Math.max(actualWidth, width); + const contentHeight = Math.max(actualHeight, height); + + scene.transformByState({ + width: contentWidth, + height: contentHeight, + }); + + mainComponent?.resize(contentWidth, contentHeight); + if (!isSingle) { + if (actualHeight > height) { + if (scrollBar == null) { + viewportMain && new ScrollBar(viewportMain, { enableHorizontal: false, enableVertical: true, barSize: 8 }); + } else { + viewportMain?.resetCanvasSizeAndUpdateScroll(); + } + } else { + scrollBar = null; + viewportMain?.scrollToBarPos({ x: 0, y: 0 }); + viewportMain?.getScrollBar()?.dispose(); + } + } else { + if (actualWidth > width) { + if (scrollBar == null) { + viewportMain && new ScrollBar(viewportMain, { barSize: 8, enableVertical: false, enableHorizontal: true }); + } else { + viewportMain?.resetCanvasSizeAndUpdateScroll(); + } + } else { + scrollBar = null; + viewportMain?.scrollToBarPos({ x: 0, y: 0 }); + viewportMain?.getScrollBar()?.dispose(); + } + } + }, 30); + }, [editor, autoScrollbar, isSingle]); + + useEffect(() => { + if (!autoScrollbar) return; + if (editor) { + const time = setTimeout(() => { + resize(); + checkScrollBar(); + }, 500); + return () => { + clearTimeout(time); + }; + } + }, [editor, autoScrollbar, resize, checkScrollBar]); + + useEffect(() => { + if (!autoScrollbar) return; + if (editor) { + const d = editor.input$.subscribe(() => { + checkScrollBar(); + }); + return () => { + d.unsubscribe(); + }; + } + }, [editor, autoScrollbar, checkScrollBar]); + + return { resize, checkScrollBar }; +}; diff --git a/packages/docs-ui/src/views/rich-text-editor/index.tsx b/packages/docs-ui/src/views/rich-text-editor/index.tsx index 46cb664181e..c435dfce7ae 100644 --- a/packages/docs-ui/src/views/rich-text-editor/index.tsx +++ b/packages/docs-ui/src/views/rich-text-editor/index.tsx @@ -16,14 +16,14 @@ import type { Editor } from '../../services/editor/editor'; import type { IKeyboardEventConfig } from './hooks'; -import { createInternalEditorID, generateRandomId, type IDisposable, type IDocumentData, useDependency, useObservable } from '@univerjs/core'; +import { createInternalEditorID, generateRandomId, type IDocumentData, useDependency, useObservable } from '@univerjs/core'; import { IRenderManagerService } from '@univerjs/engine-render'; import { useEvent } from '@univerjs/ui'; import clsx from 'clsx'; -import React, { useEffect, useLayoutEffect, useMemo, useState } from 'react'; -import { IEditorService } from '../../services/editor/editor-manager.service'; +import React, { forwardRef, useEffect, useImperativeHandle, useMemo } from 'react'; import { DocSelectionRenderService } from '../../services/selection/doc-selection-render.service'; -import { useKeyboardEvent } from './hooks'; +import { useKeyboardEvent, useResize } from './hooks'; +import { useEditor } from './hooks/useEditor'; import { useLeftAndRightArrow } from './hooks/useLeftAndRightArrow'; import styles from './index.module.less'; @@ -33,27 +33,37 @@ export interface IRichTextEditorProps { onFocusChange?: (isFocus: boolean) => void; initialValue?: IDocumentData; onClickOutside?: () => void; - keyboradEventConfig?: IKeyboardEventConfig; + keyboardEventConfig?: IKeyboardEventConfig; moveCursor?: boolean; + style?: React.CSSProperties; + isSingle?: boolean; + placeholder?: string; } -export const RichTextEditor = (props: IRichTextEditorProps) => { +export const RichTextEditor = forwardRef((props, ref) => { const { className, - autoFocus: _autoFocus, + autoFocus, onFocusChange: _onFocusChange, initialValue, onClickOutside: _onClickOutside, - keyboradEventConfig, + keyboardEventConfig, moveCursor = true, + style, + isSingle, } = props; - const autoFocus = useMemo(() => _autoFocus ?? false, []); + const onFocusChange = useEvent(_onFocusChange); const onClickOutside = useEvent(_onClickOutside); - const editorService = useDependency(IEditorService); const formulaEditorContainerRef = React.useRef(null); const editorId = useMemo(() => createInternalEditorID(`RICH_TEXT_EDITOR-${generateRandomId(4)}`), []); - const [editor, setEditor] = useState(); + const editor = useEditor({ + editorId, + initialValue, + container: formulaEditorContainerRef, + autoFocus, + isSingle, + }); const renderManagerService = useDependency(IRenderManagerService); const renderer = renderManagerService.getRenderById(editorId); const docSelectionRenderService = renderer?.with(DocSelectionRenderService); @@ -61,7 +71,7 @@ export const RichTextEditor = (props: IRichTextEditorProps) => { const sheetEmbeddingRef = React.useRef(null); useObservable(editor?.blur$); useObservable(editor?.focus$); - + useResize(editor, isSingle, true); useEffect(() => { onFocusChange?.(isFocusing); }, [isFocusing, onFocusChange]); @@ -78,56 +88,25 @@ export const RichTextEditor = (props: IRichTextEditorProps) => { }; }, [onClickOutside]); - useLayoutEffect(() => { - let dispose: IDisposable; - if (formulaEditorContainerRef.current) { - dispose = editorService.register({ - autofocus: true, - editorUnitId: editorId, - initialSnapshot: { - body: { - dataStream: '\r\n', - textRuns: [], - customBlocks: [], - customDecorations: [], - customRanges: [], - }, - documentStyle: {}, - ...initialValue, - id: editorId, - }, - }, formulaEditorContainerRef.current); - const editor = editorService.getEditor(editorId)! as Editor; - setEditor(editor); - - if (autoFocus) { - editor.focus(); - } - } - - return () => { - dispose?.dispose(); - }; - }, []); - useLeftAndRightArrow(isFocusing && moveCursor, false, editor); - useKeyboardEvent(isFocusing, keyboradEventConfig, editor); + useKeyboardEvent(isFocusing, keyboardEventConfig, editor); + useImperativeHandle(ref, () => editor!, [editor]); -
    -
    + return ( +
    { - editor?.focus(); - }} + className={clsx(styles.richTextEditorWrap, { + [styles.richTextEditorActive]: isFocusing, + })} + ref={sheetEmbeddingRef} > +
    editor?.focus()} + > +
    -
    ; -}; + ); +}); diff --git a/packages/sheets-formula-ui/src/views/range-selector/hooks/useResize.ts b/packages/sheets-formula-ui/src/views/range-selector/hooks/useResize.ts index 74afcfe00d8..aaf61e3869c 100644 --- a/packages/sheets-formula-ui/src/views/range-selector/hooks/useResize.ts +++ b/packages/sheets-formula-ui/src/views/range-selector/hooks/useResize.ts @@ -14,106 +14,4 @@ * limitations under the License. */ -import type { Nullable } from '@univerjs/core'; -import { debounce } from '@univerjs/core'; -import { DocSkeletonManagerService } from '@univerjs/docs'; -import { type Editor, VIEWPORT_KEY } from '@univerjs/docs-ui'; -import { ScrollBar } from '@univerjs/engine-render'; -import { useEffect, useMemo } from 'react'; - -// eslint-disable-next-line max-lines-per-function -export const useResize = (editor?: Editor, isSingle = true, autoScrollbar?: boolean) => { - const resize = () => { - if (editor) { - const { scene, mainComponent } = editor.render; - const docSkeletonManagerService = editor.render.with(DocSkeletonManagerService); - const { width, height } = editor.getBoundingClientRect(); - - docSkeletonManagerService.getViewModel().getDataModel().updateDocumentDataPageSize(Infinity); - scene.transformByState({ - width, - height, - }); - - mainComponent?.resize(width, height); - } - }; - - const checkScrollBar = useMemo(() => { - return debounce(() => { - if (!editor || !autoScrollbar) { - return; - } - - const docSkeletonManagerService = editor.render.with(DocSkeletonManagerService); - const skeleton = docSkeletonManagerService.getSkeleton(); - const { scene, mainComponent } = editor.render; - const viewportMain = scene.getViewport(VIEWPORT_KEY.VIEW_MAIN); - const { actualWidth, actualHeight } = skeleton.getActualSize(); - const { width, height } = editor.getBoundingClientRect(); - let scrollBar = viewportMain?.getScrollBar() as Nullable; - const contentWidth = Math.max(actualWidth, width); - - const contentHeight = height; - - scene.transformByState({ - width: contentWidth, - height: contentHeight, - }); - - mainComponent?.resize(contentWidth, contentHeight); - if (!isSingle) { - if (actualHeight > height) { - if (scrollBar == null) { - viewportMain && new ScrollBar(viewportMain, { enableHorizontal: false, barSize: 8 }); - } else { - viewportMain?.resetCanvasSizeAndUpdateScroll(); - } - } else { - scrollBar = null; - viewportMain?.scrollToBarPos({ x: 0, y: 0 }); - viewportMain?.getScrollBar()?.dispose(); - } - } else { - if (actualWidth > width) { - if (scrollBar == null) { - viewportMain && new ScrollBar(viewportMain, { barSize: 8, enableVertical: false }); - } else { - viewportMain?.resetCanvasSizeAndUpdateScroll(); - } - } else { - scrollBar = null; - viewportMain?.scrollToBarPos({ x: 0, y: 0 }); - viewportMain?.getScrollBar()?.dispose(); - } - } - }, 30); - }, [editor, autoScrollbar]); - - useEffect(() => { - if (!autoScrollbar) return; - if (editor) { - const time = setTimeout(() => { - resize(); - checkScrollBar(); - }, 500); - return () => { - clearTimeout(time); - }; - } - }, [editor, autoScrollbar]); - - useEffect(() => { - if (!autoScrollbar) return; - if (editor) { - const d = editor.input$.subscribe(() => { - checkScrollBar(); - }); - return () => { - d.unsubscribe(); - }; - } - }, [editor, autoScrollbar]); - - return { resize, checkScrollBar }; -}; +export { useResize } from '@univerjs/docs-ui'; diff --git a/packages/thread-comment-ui/src/views/thread-comment-editor/index.tsx b/packages/thread-comment-ui/src/views/thread-comment-editor/index.tsx index 73dfe73b7b4..020737fa233 100644 --- a/packages/thread-comment-ui/src/views/thread-comment-editor/index.tsx +++ b/packages/thread-comment-ui/src/views/thread-comment-editor/index.tsx @@ -14,17 +14,16 @@ * limitations under the License. */ -import type { IDocumentBody } from '@univerjs/core'; -import type { MentionProps } from '@univerjs/design'; +import type { IDocumentBody, IDocumentData } from '@univerjs/core'; +import type { Editor, IKeyboardEventConfig } from '@univerjs/docs-ui'; import type { IThreadComment } from '@univerjs/thread-comment'; -import { ICommandService, IMentionIOService, LocaleService, UniverInstanceType, useDependency } from '@univerjs/core'; -import { Button, Mention, Mentions } from '@univerjs/design'; -import { DocSelectionRenderService } from '@univerjs/docs-ui'; -import { IRenderManagerService } from '@univerjs/engine-render'; -import React, { forwardRef, useImperativeHandle, useRef, useState } from 'react'; +import { ICommandService, LocaleService, Tools, useDependency } from '@univerjs/core'; +import { Button } from '@univerjs/design'; +import { BreakLineCommand, RichTextEditor } from '@univerjs/docs-ui'; +import { KeyCode } from '@univerjs/ui'; +import React, { forwardRef, useImperativeHandle, useMemo, useRef, useState } from 'react'; import { SetActiveCommentOperation } from '../../commands/operations/comment.operations'; import styles from './index.module.less'; -import { parseMentions, transformDocument2TextNodes, transformTextNode2Text, transformTextNodes2Document } from './util'; export interface IThreadCommentEditorProps { id?: string; @@ -40,93 +39,66 @@ export interface IThreadCommentEditorInstance { reply: (text: IDocumentBody) => void; } -const defaultRenderSuggestion: MentionProps['renderSuggestion'] = (mention, search, highlightedDisplay, index, focused) => { - const icon = (mention as any).raw?.icon; - return ( -
    - {icon ? : null} -
    - {mention.display ?? mention.id} -
    -
    - ); -}; +function getSnapshot(body: IDocumentBody): IDocumentData { + return { + id: 'd', + body, + documentStyle: {}, + }; +} export const ThreadCommentEditor = forwardRef((props, ref) => { - const { comment, onSave, id, onCancel, autoFocus, unitId } = props; - const mentionIOService = useDependency(IMentionIOService); + const { comment, onSave, id, onCancel, autoFocus } = props; const commandService = useDependency(ICommandService); const localeService = useDependency(LocaleService); - const [localComment, setLocalComment] = useState({ ...comment }); const [editing, setEditing] = useState(false); - const inputRef = useRef(null); - const renderManagerService = useDependency(IRenderManagerService); - const docSelectionRenderService = renderManagerService.getCurrentTypeOfRenderer(UniverInstanceType.UNIVER_DOC)?.with(DocSelectionRenderService); + const editor = useRef(null); + + const keyboardEventConfig: IKeyboardEventConfig = useMemo(() => ( + { + keyCodes: [{ keyCode: KeyCode.ENTER }], + handler: (keyCode) => { + if (keyCode === KeyCode.ENTER) { + commandService.executeCommand( + BreakLineCommand.id + ); + } + }, + } + ), [commandService]); useImperativeHandle(ref, () => ({ reply(text) { - setLocalComment({ - ...comment, - text, - attachments: [], - }); - (inputRef.current as any)?.inputElement.focus(); + editor.current?.focus(); + editor.current?.setDocumentData(getSnapshot(text)); }, })); const handleSave = () => { - if (localComment.text) { + if (editor.current) { + const newText = Tools.deepClone(editor.current.getDocumentData().body); + setEditing(false); + editor.current.blur(); onSave?.({ - ...localComment, - text: localComment.text, + ...comment, + text: newText!, }); - setEditing(false); - setLocalComment({ text: undefined }); - (inputRef.current as any)?.inputElement.blur(); + editor.current?.replaceText('', true); } }; return (
    e.preventDefault()}> - { - const text = e.target.value; - if (!text) { - setLocalComment({ ...comment, text: undefined }); - } - setLocalComment?.({ ...comment, text: transformTextNodes2Document(parseMentions(e.target.value)) }); - }} - onFocus={() => { - docSelectionRenderService?.blur(); - setEditing(true); - }} - > - mentionIOService.list({ search: query, unitId }) - .then((res) => res.list.map( - (typeMentions) => ( - typeMentions.mentions.map( - (mention) => ({ - id: mention.objectId, - display: mention.label, - raw: mention, - }) - ) - ) - ).flat()) - .then(callback) as any} - displayTransform={(id, label) => `@${label} `} - renderSuggestion={defaultRenderSuggestion} - - /> - + initialValue={comment?.text && getSnapshot(comment.text)} + onFocusChange={(isFocus) => setEditing(isFocus)} + isSingle={false} + /> {editing ? (
    @@ -135,7 +107,7 @@ export const ThreadCommentEditor = forwardRef { onCancel?.(); setEditing(false); - setLocalComment({ text: undefined }); + editor.current?.replaceText('', true); commandService.executeCommand(SetActiveCommentOperation.id); }} > @@ -143,7 +115,7 @@ export const ThreadCommentEditor = forwardRef
    From f22637ec87bfcaa6c2e43e5dc09605516f31e35b Mon Sep 17 00:00:00 2001 From: zhangw Date: Sat, 28 Dec 2024 14:24:43 +0800 Subject: [PATCH 116/134] feat: update --- .../thread-comment-ui/src/views/thread-comment-editor/util.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/thread-comment-ui/src/views/thread-comment-editor/util.ts b/packages/thread-comment-ui/src/views/thread-comment-editor/util.ts index 78c681a15a0..c3c7ca6f8d4 100644 --- a/packages/thread-comment-ui/src/views/thread-comment-editor/util.ts +++ b/packages/thread-comment-ui/src/views/thread-comment-editor/util.ts @@ -83,7 +83,7 @@ export const transformTextNode2Text = (nodes: TextNode[]) => { const transformDocument2TextNodesInParagraph = (doc: IDocumentBody) => { const { dataStream, customRanges } = doc; - const end = dataStream.endsWith('\r\n') ? dataStream.length : dataStream.length - 2; + const end = dataStream.endsWith('\r\n') ? dataStream.length - 2 : dataStream.length; const textNodes: TextNode[] = []; let lastIndex = 0; From 5c6742aedb5286db93ea0bca5c893d10d78f8ceb Mon Sep 17 00:00:00 2001 From: zhangw Date: Sat, 28 Dec 2024 14:49:35 +0800 Subject: [PATCH 117/134] feat: placeholder --- .../views/rich-text-editor/index.module.less | 8 ++++-- .../src/views/rich-text-editor/index.tsx | 25 ++++++++++++++++--- 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/packages/docs-ui/src/views/rich-text-editor/index.module.less b/packages/docs-ui/src/views/rich-text-editor/index.module.less index 73c41e878ff..063fb869af5 100644 --- a/packages/docs-ui/src/views/rich-text-editor/index.module.less +++ b/packages/docs-ui/src/views/rich-text-editor/index.module.less @@ -31,7 +31,11 @@ } } - &-error { - border: 1px solid rgb(var(--red-500)) !important; + &-placeholder { + font-size: 14px; + color: rgb(var(--grey-500)); + position: absolute; + left: 5px; + top: 5px; } } diff --git a/packages/docs-ui/src/views/rich-text-editor/index.tsx b/packages/docs-ui/src/views/rich-text-editor/index.tsx index c435dfce7ae..66057c6e47f 100644 --- a/packages/docs-ui/src/views/rich-text-editor/index.tsx +++ b/packages/docs-ui/src/views/rich-text-editor/index.tsx @@ -16,11 +16,11 @@ import type { Editor } from '../../services/editor/editor'; import type { IKeyboardEventConfig } from './hooks'; -import { createInternalEditorID, generateRandomId, type IDocumentData, useDependency, useObservable } from '@univerjs/core'; +import { BuildTextUtils, createInternalEditorID, generateRandomId, type IDocumentData, useDependency, useObservable } from '@univerjs/core'; import { IRenderManagerService } from '@univerjs/engine-render'; import { useEvent } from '@univerjs/ui'; import clsx from 'clsx'; -import React, { forwardRef, useEffect, useImperativeHandle, useMemo } from 'react'; +import React, { forwardRef, useEffect, useImperativeHandle, useMemo, useState } from 'react'; import { DocSelectionRenderService } from '../../services/selection/doc-selection-render.service'; import { useKeyboardEvent, useResize } from './hooks'; import { useEditor } from './hooks/useEditor'; @@ -69,6 +69,17 @@ export const RichTextEditor = forwardRef((props, r const docSelectionRenderService = renderer?.with(DocSelectionRenderService); const isFocusing = docSelectionRenderService?.isFocusing ?? false; const sheetEmbeddingRef = React.useRef(null); + const [showPlaceholder, setShowPlaceholder] = useState(() => !BuildTextUtils.transform.getPlainText(editor?.getDocumentData().body?.dataStream ?? '')); + + useEffect(() => { + setShowPlaceholder(!BuildTextUtils.transform.getPlainText(editor?.getDocumentData().body?.dataStream ?? '')); + + const sub = editor?.selectionChange$.subscribe(() => { + setShowPlaceholder(!BuildTextUtils.transform.getPlainText(editor?.getDocumentData().body?.dataStream ?? '')); + }); + + return () => sub?.unsubscribe(); + }, [editor]); useObservable(editor?.blur$); useObservable(editor?.focus$); useResize(editor, isSingle, true); @@ -104,8 +115,14 @@ export const RichTextEditor = forwardRef((props, r className={styles.richTextEditorText} ref={formulaEditorContainerRef} onMouseUp={() => editor?.focus()} - > -
    + /> + {!showPlaceholder + ? null + : ( +
    + {props.placeholder} +
    + )}
  • ); From ef8f08cb165e7b7f9bdfa2d66217b72c90c0a645 Mon Sep 17 00:00:00 2001 From: zhangw Date: Sat, 28 Dec 2024 15:36:29 +0800 Subject: [PATCH 118/134] feat: update --- ...doc-thread-comment-selection.controller.ts | 3 +- .../src/views/rich-text-editor/index.tsx | 6 +++- .../src/views/thread-comment-editor/index.tsx | 34 +++++++++++++++---- .../src/views/thread-comment-tree/index.tsx | 7 +++- 4 files changed, 41 insertions(+), 9 deletions(-) diff --git a/packages/docs-thread-comment-ui/src/controllers/doc-thread-comment-selection.controller.ts b/packages/docs-thread-comment-ui/src/controllers/doc-thread-comment-selection.controller.ts index 496f1fc7b8c..9efb7f86649 100644 --- a/packages/docs-thread-comment-ui/src/controllers/doc-thread-comment-selection.controller.ts +++ b/packages/docs-thread-comment-ui/src/controllers/doc-thread-comment-selection.controller.ts @@ -17,7 +17,7 @@ import type { DocumentDataModel, ITextRange } from '@univerjs/core'; import type { ISetTextSelectionsOperationParams } from '@univerjs/docs'; import type { ITextRangeWithStyle } from '@univerjs/engine-render'; -import { Disposable, ICommandService, Inject, IUniverInstanceService, UniverInstanceType } from '@univerjs/core'; +import { Disposable, ICommandService, Inject, isInternalEditorID, IUniverInstanceService, UniverInstanceType } from '@univerjs/core'; import { SetTextSelectionsOperation } from '@univerjs/docs'; import { DocBackScrollRenderController } from '@univerjs/docs-ui'; import { IRenderManagerService } from '@univerjs/engine-render'; @@ -49,6 +49,7 @@ export class DocThreadCommentSelectionController extends Disposable { if (commandInfo.id === SetTextSelectionsOperation.id) { const params = commandInfo.params as ISetTextSelectionsOperationParams; const { unitId, ranges } = params; + if (isInternalEditorID(unitId)) return; const doc = this._univerInstanceService.getUnit(unitId, UniverInstanceType.UNIVER_DOC); const primary = ranges[0] as ITextRangeWithStyle | undefined; if (lastSelection?.startOffset === primary?.startOffset && lastSelection?.endOffset === primary?.endOffset) { diff --git a/packages/docs-ui/src/views/rich-text-editor/index.tsx b/packages/docs-ui/src/views/rich-text-editor/index.tsx index 66057c6e47f..0c8c41dc3eb 100644 --- a/packages/docs-ui/src/views/rich-text-editor/index.tsx +++ b/packages/docs-ui/src/views/rich-text-editor/index.tsx @@ -89,11 +89,15 @@ export const RichTextEditor = forwardRef((props, r useEffect(() => { const handleClickOutside = (event: MouseEvent) => { + if (!editor?.isFocus()) return; if (sheetEmbeddingRef.current && !sheetEmbeddingRef.current.contains(event.target as any)) { onClickOutside?.(); } }; - document.addEventListener('click', handleClickOutside); + + setTimeout(() => { + document.addEventListener('click', handleClickOutside); + }, 100); return () => { document.removeEventListener('click', handleClickOutside); }; diff --git a/packages/thread-comment-ui/src/views/thread-comment-editor/index.tsx b/packages/thread-comment-ui/src/views/thread-comment-editor/index.tsx index 020737fa233..a58ac202043 100644 --- a/packages/thread-comment-ui/src/views/thread-comment-editor/index.tsx +++ b/packages/thread-comment-ui/src/views/thread-comment-editor/index.tsx @@ -17,11 +17,12 @@ import type { IDocumentBody, IDocumentData } from '@univerjs/core'; import type { Editor, IKeyboardEventConfig } from '@univerjs/docs-ui'; import type { IThreadComment } from '@univerjs/thread-comment'; -import { ICommandService, LocaleService, Tools, useDependency } from '@univerjs/core'; +import { BuildTextUtils, DOCS_NORMAL_EDITOR_UNIT_ID_KEY, ICommandService, LocaleService, Tools, UniverInstanceType, useDependency } from '@univerjs/core'; import { Button } from '@univerjs/design'; -import { BreakLineCommand, RichTextEditor } from '@univerjs/docs-ui'; +import { BreakLineCommand, DocSelectionRenderService, RichTextEditor } from '@univerjs/docs-ui'; +import { IRenderManagerService } from '@univerjs/engine-render'; import { KeyCode } from '@univerjs/ui'; -import React, { forwardRef, useImperativeHandle, useMemo, useRef, useState } from 'react'; +import React, { forwardRef, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react'; import { SetActiveCommentOperation } from '../../commands/operations/comment.operations'; import styles from './index.module.less'; @@ -33,6 +34,7 @@ export interface IThreadCommentEditorProps { autoFocus?: boolean; unitId: string; subUnitId: string; + type: UniverInstanceType; } export interface IThreadCommentEditorInstance { @@ -48,11 +50,24 @@ function getSnapshot(body: IDocumentBody): IDocumentData { } export const ThreadCommentEditor = forwardRef((props, ref) => { - const { comment, onSave, id, onCancel, autoFocus } = props; + const { comment, onSave, id, onCancel, autoFocus, unitId, type } = props; const commandService = useDependency(ICommandService); const localeService = useDependency(LocaleService); const [editing, setEditing] = useState(false); const editor = useRef(null); + const renderManagerService = useDependency(IRenderManagerService); + const renderer = type === UniverInstanceType.UNIVER_SHEET ? renderManagerService.getRenderById(DOCS_NORMAL_EDITOR_UNIT_ID_KEY) : renderManagerService.getRenderById(unitId); + const docSelectionRenderService = renderer?.with(DocSelectionRenderService); + const [canSubmit, setCanSubmit] = useState(() => BuildTextUtils.transform.getPlainText(editor.current?.getDocumentData().body?.dataStream ?? '')); + useEffect(() => { + setCanSubmit(BuildTextUtils.transform.getPlainText(editor.current?.getDocumentData().body?.dataStream ?? '')); + + const sub = editor.current?.selectionChange$.subscribe(() => { + setCanSubmit(BuildTextUtils.transform.getPlainText(editor.current?.getDocumentData().body?.dataStream ?? '')); + }); + + return () => sub?.unsubscribe(); + }, [editor.current?.selectionChange$]); const keyboardEventConfig: IKeyboardEventConfig = useMemo(() => ( { @@ -83,7 +98,10 @@ export const ThreadCommentEditor = forwardRef { + editor.current?.setSelectionRanges([]); + }, 30); } }; @@ -98,6 +116,10 @@ export const ThreadCommentEditor = forwardRef setEditing(isFocus)} isSingle={false} + onClickOutside={() => { + editor.current?.blur(); + docSelectionRenderService?.focus(); + }} /> {editing ? ( @@ -115,7 +137,7 @@ export const ThreadCommentEditor = forwardRef