From 8d06bb22756ca54e246da54a2c53f096f1139e15 Mon Sep 17 00:00:00 2001 From: zw Date: Sat, 17 Aug 2024 01:56:33 +0800 Subject: [PATCH 01/43] feat: support event manager service --- .../doc-formula-input.controller.ts | 10 +- .../commands/operations/popup.operation.ts | 22 +++ .../hyper-link-hover.render-controller.ts | 67 ++++++++ .../src/controllers/ui.controller.ts | 3 +- packages/docs-hyper-link-ui/src/plugin.ts | 8 +- .../src/services/hyper-link-popup.service.ts | 4 +- .../doc-checklist.render-controller.ts | 25 ++- .../doc-hover.render-controller.ts | 56 ------ packages/docs-ui/src/docs-ui-plugin.ts | 6 +- packages/docs-ui/src/index.ts | 2 +- .../src/services/doc-event-manager.service.ts | 159 ++++++++++++++++++ .../src/services/doc-hover-manager.service.ts | 138 --------------- .../src/services/doc-popup-manager.service.ts | 95 +++++++---- .../components/docs/layout/doc-skeleton.ts | 5 + 14 files changed, 343 insertions(+), 257 deletions(-) create mode 100644 packages/docs-hyper-link-ui/src/controllers/render-controllers/hyper-link-hover.render-controller.ts delete mode 100644 packages/docs-ui/src/controllers/render-controllers/doc-hover.render-controller.ts create mode 100644 packages/docs-ui/src/services/doc-event-manager.service.ts delete mode 100644 packages/docs-ui/src/services/doc-hover-manager.service.ts diff --git a/packages-experimental/uni-formula-ui/src/controllers/doc-formula-input.controller.ts b/packages-experimental/uni-formula-ui/src/controllers/doc-formula-input.controller.ts index f7872b182b4..cade1acf530 100644 --- a/packages-experimental/uni-formula-ui/src/controllers/doc-formula-input.controller.ts +++ b/packages-experimental/uni-formula-ui/src/controllers/doc-formula-input.controller.ts @@ -18,7 +18,7 @@ import { CustomRangeType, Disposable, ICommandService, ILogService, Inject, IUni import type { IInsertCommandParams } from '@univerjs/docs'; import { DeleteLeftCommand, InsertCommand, MoveCursorOperation, TextSelectionManagerService } from '@univerjs/docs'; import { IEditorService } from '@univerjs/ui'; -import { DocHoverManagerService } from '@univerjs/docs-ui'; +import { DocEventManagerService } from '@univerjs/docs-ui'; import { AddDocUniFormulaCommand, RemoveDocUniFormulaCommand, UpdateDocUniFormulaCommand } from '../commands/commands/doc.command'; import type { IShowFormulaPopupOperationParams } from '../commands/operations/operation'; @@ -35,7 +35,7 @@ export class DocUniFormulaInputController extends Disposable { @IUniverInstanceService private readonly _instanceSrv: IUniverInstanceService, @IEditorService private readonly _editorService: IEditorService, @ILogService private readonly _logService: ILogService, - @Inject(DocHoverManagerService) private readonly _docHoverManagerSrv: DocHoverManagerService, + @Inject(DocEventManagerService) private readonly _docEventManagerService: DocEventManagerService, @Inject(UniFormulaPopupService) private readonly _formulaPopupSrv: UniFormulaPopupService, @Inject(TextSelectionManagerService) private readonly _textSelectionManagerService: TextSelectionManagerService ) { @@ -43,7 +43,7 @@ export class DocUniFormulaInputController extends Disposable { this._initKeyboardListeners(); this._initCommands(); - this._initHoverListener(); + // this._initHoverListener(); } private _initCommands(): void { @@ -91,7 +91,7 @@ export class DocUniFormulaInputController extends Disposable { } private _initHoverListener(): void { - this.disposeWithMe(this._docHoverManagerSrv.activeCustomRanges$.subscribe((customRanges) => { + this.disposeWithMe(this._docEventManagerService.hoverCustomRanges$.subscribe((customRanges) => { const focusedUnit = this._instanceSrv.getFocusedUnit(); if ( @@ -102,7 +102,7 @@ export class DocUniFormulaInputController extends Disposable { return; } - const formulaCustomRange = customRanges.find((range) => range.rangeType === CustomRangeType.UNI_FORMULA); + const formulaCustomRange = customRanges.find((range) => range.range.rangeType === CustomRangeType.UNI_FORMULA)?.range; if (formulaCustomRange) { const { startIndex, rangeId } = formulaCustomRange; this._logService.debug('[DocUniFormulaController]: activeCustomRanges', customRanges); diff --git a/packages/docs-hyper-link-ui/src/commands/operations/popup.operation.ts b/packages/docs-hyper-link-ui/src/commands/operations/popup.operation.ts index 068cf356380..496f9f84991 100644 --- a/packages/docs-hyper-link-ui/src/commands/operations/popup.operation.ts +++ b/packages/docs-hyper-link-ui/src/commands/operations/popup.operation.ts @@ -81,3 +81,25 @@ export const ShowDocHyperLinkEditPopupOperation: ICommand = { + type: CommandType.OPERATION, + id: 'docs.operation.show-hyper-link-info-popup', + handler(accessor, params) { + const hyperLinkService = accessor.get(DocHyperLinkPopupService); + if (!params) { + hyperLinkService.hideInfoPopup(); + return true; + } + + hyperLinkService.showInfoPopup(params); + return true; + }, +}; diff --git a/packages/docs-hyper-link-ui/src/controllers/render-controllers/hyper-link-hover.render-controller.ts b/packages/docs-hyper-link-ui/src/controllers/render-controllers/hyper-link-hover.render-controller.ts new file mode 100644 index 00000000000..83a5db4662c --- /dev/null +++ b/packages/docs-hyper-link-ui/src/controllers/render-controllers/hyper-link-hover.render-controller.ts @@ -0,0 +1,67 @@ +/** + * 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 } from '@univerjs/core'; +import { CustomRangeType, Disposable, ICommandService, Inject } from '@univerjs/core'; +import { DocEventManagerService } from '@univerjs/docs-ui'; +import type { IRenderContext, IRenderModule } from '@univerjs/engine-render'; +import { ToggleDocHyperLinkInfoPopupOperation } from '../../commands/operations/popup.operation'; +import { DocHyperLinkPopupService } from '../../services/hyper-link-popup.service'; + +export class DocHyperLinkHoverRenderController extends Disposable implements IRenderModule { + constructor( + private readonly _context: IRenderContext, + @Inject(DocEventManagerService) private readonly _docEventManagerService: DocEventManagerService, + @ICommandService private readonly _commandService: ICommandService, + @Inject(DocHyperLinkPopupService) private readonly _hyperLinkPopupService: DocHyperLinkPopupService + ) { + super(); + + this._init(); + } + + private _init() { + this.disposeWithMe( + this._docEventManagerService.hoverCustomRanges$.subscribe((ranges) => { + const link = ranges.find((range) => range.range.rangeType === CustomRangeType.HYPERLINK); + if (link) { + // const linkIndex + const customRanges = this._context.unit.getSelfOrHeaderFooterModel(link.segmentId).getBody()?.customRanges; + if (customRanges) { + const linkIndex = customRanges.findIndex((range) => range.rangeId === link.range.rangeId); + if (linkIndex !== -1) { + this._commandService.executeCommand( + ToggleDocHyperLinkInfoPopupOperation.id, + { + unitId: this._context.unitId, + linkId: link.range.rangeId, + segmentId: link.segmentId, + rangeIndex: linkIndex, + } + ); + } + } + } else { + if (this._hyperLinkPopupService.showing) { + this._commandService.executeCommand( + ToggleDocHyperLinkInfoPopupOperation.id + ); + } + } + }) + ); + } +} diff --git a/packages/docs-hyper-link-ui/src/controllers/ui.controller.ts b/packages/docs-hyper-link-ui/src/controllers/ui.controller.ts index b8558560dd0..c4e8938d616 100644 --- a/packages/docs-hyper-link-ui/src/controllers/ui.controller.ts +++ b/packages/docs-hyper-link-ui/src/controllers/ui.controller.ts @@ -22,7 +22,7 @@ import { DocHyperLinkEdit } from '../views/hyper-link-edit'; import { AddDocHyperLinkCommand } from '../commands/commands/add-link.command'; import { UpdateDocHyperLinkCommand } from '../commands/commands/update-link.command'; import { DeleteDocHyperLinkCommand } from '../commands/commands/delete-link.command'; -import { ShowDocHyperLinkEditPopupOperation } from '../commands/operations/popup.operation'; +import { ShowDocHyperLinkEditPopupOperation, ToggleDocHyperLinkInfoPopupOperation } from '../commands/operations/popup.operation'; import { DocLinkPopup } from '../views/hyper-link-popup'; import { AddHyperLinkMenuItemFactory, addLinkShortcut, DOC_LINK_ICON } from './menu'; @@ -64,6 +64,7 @@ export class DocHyperLinkUIController extends Disposable { UpdateDocHyperLinkCommand, DeleteDocHyperLinkCommand, ShowDocHyperLinkEditPopupOperation, + ToggleDocHyperLinkInfoPopupOperation, ].forEach((command) => { this._commandService.registerCommand(command); }); diff --git a/packages/docs-hyper-link-ui/src/plugin.ts b/packages/docs-hyper-link-ui/src/plugin.ts index 1be51ae6461..850388d3553 100644 --- a/packages/docs-hyper-link-ui/src/plugin.ts +++ b/packages/docs-hyper-link-ui/src/plugin.ts @@ -26,6 +26,7 @@ import { DocHyperLinkSelectionController } from './controllers/doc-hyper-link-se import { DocHyperLinkRenderController } from './controllers/render-controllers/render.controller'; import { DocHyperLinkClipboardController } from './controllers/doc-hyper-link-clipboard.controller'; import { DocHyperLinkCustomRangeController } from './controllers/doc-hyper-link-custom-range.controller'; +import { DocHyperLinkHoverRenderController } from './controllers/render-controllers/hyper-link-hover.render-controller'; @DependentOn(UniverDocsHyperLinkPlugin) export class UniverDocsHyperLinkUIPlugin extends Plugin { @@ -63,8 +64,11 @@ export class UniverDocsHyperLinkUIPlugin extends Plugin { } private _initRenderModule() { - [DocHyperLinkRenderController].forEach((dep) => { - this._renderManagerSrv.registerRenderModule(UniverInstanceType.UNIVER_DOC, dep as unknown as Dependency); + ([ + [DocHyperLinkRenderController], + [DocHyperLinkHoverRenderController], + ] as Dependency[]).forEach((dep) => { + this._renderManagerSrv.registerRenderModule(UniverInstanceType.UNIVER_DOC, dep); }); } } diff --git a/packages/docs-hyper-link-ui/src/services/hyper-link-popup.service.ts b/packages/docs-hyper-link-ui/src/services/hyper-link-popup.service.ts index 9c044d512d9..4962e96508d 100644 --- a/packages/docs-hyper-link-ui/src/services/hyper-link-popup.service.ts +++ b/packages/docs-hyper-link-ui/src/services/hyper-link-popup.service.ts @@ -29,7 +29,9 @@ import { DocLinkPopup } from '../views/hyper-link-popup'; const SKIT_PLACEHOLDER = 2; export interface ILinkInfo { - unitId: string; linkId: string; rangeIndex: number; + unitId: string; + linkId: string; + rangeIndex: number; } export class DocHyperLinkPopupService extends Disposable { diff --git a/packages/docs-ui/src/controllers/render-controllers/doc-checklist.render-controller.ts b/packages/docs-ui/src/controllers/render-controllers/doc-checklist.render-controller.ts index e3cdc20af2b..5d8b3dbcedd 100644 --- a/packages/docs-ui/src/controllers/render-controllers/doc-checklist.render-controller.ts +++ b/packages/docs-ui/src/controllers/render-controllers/doc-checklist.render-controller.ts @@ -15,18 +15,17 @@ */ import type { Documents, IRenderContext, IRenderModule, Viewport } from '@univerjs/engine-render'; -import { CURSOR_TYPE, getParagraphByGlyph, GlyphType, PageLayoutType, Vector2 } from '@univerjs/engine-render'; +import { getParagraphByGlyph, GlyphType, PageLayoutType, Vector2 } from '@univerjs/engine-render'; import type { DocumentDataModel } from '@univerjs/core'; import { Disposable, ICommandService, Inject, PresetListType } from '@univerjs/core'; import { DocSkeletonManagerService, ToggleCheckListCommand, VIEWPORT_KEY } from '@univerjs/docs'; -import { DocHoverManagerService } from '../../services/doc-hover-manager.service'; +// import { DocEventManagerService } from '../../services/doc-event-manager.service'; export class DocChecklistRenderController extends Disposable implements IRenderModule { constructor( private _context: IRenderContext, @Inject(DocSkeletonManagerService) private readonly _docSkeletonManagerService: DocSkeletonManagerService, - @ICommandService private readonly _commandService: ICommandService, - @Inject(DocHoverManagerService) private readonly _docHoverManagerService: DocHoverManagerService + @ICommandService private readonly _commandService: ICommandService ) { super(); @@ -76,15 +75,15 @@ export class DocChecklistRenderController extends Disposable implements IRenderM } private _initHoverCursor() { - this.disposeWithMe( - this._docHoverManagerService.bullet$.subscribe((paragraph) => { - if (paragraph) { - this._context.mainComponent!.setCursor(CURSOR_TYPE.POINTER); - } else { - this._context.mainComponent!.setCursor(CURSOR_TYPE.TEXT); - } - }) - ); + // this.disposeWithMe( + // this._docHoverManagerService.bullet$.subscribe((paragraph) => { + // if (paragraph) { + // this._context.mainComponent!.setCursor(CURSOR_TYPE.POINTER); + // } else { + // this._context.mainComponent!.setCursor(CURSOR_TYPE.TEXT); + // } + // }) + // ); } private _getTransformCoordForDocumentOffset(document: Documents, viewport: Viewport, evtOffsetX: number, evtOffsetY: number) { diff --git a/packages/docs-ui/src/controllers/render-controllers/doc-hover.render-controller.ts b/packages/docs-ui/src/controllers/render-controllers/doc-hover.render-controller.ts deleted file mode 100644 index 40e753f2cde..00000000000 --- a/packages/docs-ui/src/controllers/render-controllers/doc-hover.render-controller.ts +++ /dev/null @@ -1,56 +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 { Disposable, fromEventSubject, Inject } from '@univerjs/core'; -import type { IRenderContext, IRenderModule } from '@univerjs/engine-render'; -import { VIEWPORT_KEY } from '@univerjs/docs'; -import { debounceTime } from 'rxjs'; -import { DocHoverManagerService } from '../../services/doc-hover-manager.service'; - -export class DocHoverRenderController extends Disposable implements IRenderModule { - constructor( - private _context: IRenderContext, - @Inject(DocHoverManagerService) private readonly _docHoverManagerService: DocHoverManagerService - ) { - super(); - - this._initPointerDown(); - this._initScroll(); - } - - private _initPointerDown() { - this.disposeWithMe( - fromEventSubject(this._context.scene.onPointerMove$) - .pipe(debounceTime(100)) - .subscribe((evt) => { - this._docHoverManagerService.onMouseMove(evt); - }) - ); - } - - private _initScroll() { - const viewMain = this._context.scene.getViewport(VIEWPORT_KEY.VIEW_MAIN); - if (viewMain) { - this.disposeWithMe(fromEventSubject(viewMain.onScrollAfter$).pipe(debounceTime(60)).subscribe(() => { - this._docHoverManagerService.endScroll(); - })); - - this.disposeWithMe(viewMain.onScrollBefore$.subscribeEvent(() => { - this._docHoverManagerService.startScroll(); - })); - } - } -} diff --git a/packages/docs-ui/src/docs-ui-plugin.ts b/packages/docs-ui/src/docs-ui-plugin.ts index 35666fed258..56133b84b2f 100644 --- a/packages/docs-ui/src/docs-ui-plugin.ts +++ b/packages/docs-ui/src/docs-ui-plugin.ts @@ -58,8 +58,7 @@ import { DocHeaderFooterController } from './controllers/doc-header-footer.contr import { DocContextMenuRenderController } from './controllers/render-controllers/contextmenu.render-controller'; import { DocPageLayoutService } from './services/doc-page-layout.service'; import { DocResizeRenderController } from './controllers/render-controllers/doc-resize.render-controller'; -import { DocHoverManagerService } from './services/doc-hover-manager.service'; -import { DocHoverRenderController } from './controllers/render-controllers/doc-hover.render-controller'; +import { DocEventManagerService } from './services/doc-event-manager.service'; import { DocAutoFormatController } from './controllers/doc-auto-format.controller'; import { ShiftTabShortCut } from './shortcuts/format.shortcut'; import { DocChecklistRenderController } from './controllers/render-controllers/doc-checklist.render-controller'; @@ -138,7 +137,6 @@ export class UniverDocsUIPlugin extends Plugin { [AppUIController, { useFactory: () => this._injector.createInstance(AppUIController, this._config) }], [IDocClipboardService, { useClass: DocClipboardService }], [DocCanvasPopManagerService], - [DocHoverManagerService], [DocParagraphSettingController], ]; @@ -181,11 +179,11 @@ export class UniverDocsUIPlugin extends Plugin { private _initRenderModules(): void { ([ + [DocEventManagerService], [DocBackScrollRenderController], [DocTextSelectionRenderController], [DocHeaderFooterController], [DocResizeRenderController], - [DocHoverRenderController], [DocContextMenuRenderController], [DocChecklistRenderController], ] as Dependency[]).forEach((m) => { diff --git a/packages/docs-ui/src/index.ts b/packages/docs-ui/src/index.ts index eb4ce537399..a44dace3c91 100644 --- a/packages/docs-ui/src/index.ts +++ b/packages/docs-ui/src/index.ts @@ -24,7 +24,7 @@ export * from './services'; export { DocsRenderService } from './services/docs-render.service'; export { DocCanvasPopManagerService } from './services/doc-popup-manager.service'; export { docDrawingPositionToTransform, transformToDocDrawingPosition } from './basics/transform-position'; -export { DocHoverManagerService } from './services/doc-hover-manager.service'; +export { DocEventManagerService } from './services/doc-event-manager.service'; export { DocUIController } from './controllers/doc-ui.controller'; // #region - all commands diff --git a/packages/docs-ui/src/services/doc-event-manager.service.ts b/packages/docs-ui/src/services/doc-event-manager.service.ts new file mode 100644 index 00000000000..90fe0fa2199 --- /dev/null +++ b/packages/docs-ui/src/services/doc-event-manager.service.ts @@ -0,0 +1,159 @@ +/** + * 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 { CustomRangeType, DocumentDataModel, ICustomRange, ITextRange } from '@univerjs/core'; +import { Disposable, fromEventSubject, ILogService, Inject } from '@univerjs/core'; +import { DocSkeletonManagerService } from '@univerjs/docs'; +import type { Documents, DocumentSkeleton, IBoundRectNoAngle, IMouseEvent, IPointerEvent, IRender, IRenderContext, IRenderModule } from '@univerjs/engine-render'; +import { getLineBounding, NodePositionConvertToCursor, pxToNum, TRANSFORM_CHANGE_OBSERVABLE_TYPE } from '@univerjs/engine-render'; +import { distinctUntilChanged, filter, Subject, throttleTime } from 'rxjs'; +import { transformOffset2Bound } from './doc-popup-manager.service'; + +interface ICustomRangeLayout { + customRange: ICustomRange; + rects: IBoundRectNoAngle[]; + segmentId?: string; +} + +const calcDocRangePositions = (range: ITextRange, documents: Documents, skeleton: DocumentSkeleton): IBoundRectNoAngle[] | undefined => { + const startPosition = skeleton.findNodePositionByCharIndex(range.startOffset); + const endPosition = skeleton.findNodePositionByCharIndex(range.endOffset); + + if (!endPosition || !startPosition) { + return; + } + + const documentOffsetConfig = documents.getOffsetConfig(); + const convertor = new NodePositionConvertToCursor(documentOffsetConfig, skeleton); + const { borderBoxPointGroup } = convertor.getRangePointData(startPosition, endPosition); + const bounds = getLineBounding(borderBoxPointGroup); + + return bounds; +}; + +export class DocEventManagerService extends Disposable implements IRenderModule { + private _hoverCustomRanges$ = new Subject<{ range: ICustomRange; segmentId?: string }[]>(); + readonly hoverCustomRanges$ = this._hoverCustomRanges$.pipe(distinctUntilChanged((pre, aft) => pre.length === aft.length && pre.every((item, i) => aft[i].range.rangeId === item.range.rangeId && aft[i].segmentId === item.segmentId))); + + private _dirty = true; + private _customRangeLayouts: ICustomRangeLayout[] = []; + + private get _skeleton() { + return this._docSkeletonManagerService.getSkeleton(); + } + + private get _documents() { + return this._context.mainComponent as Documents; + } + + constructor( + private _context: IRenderContext, + @Inject(DocSkeletonManagerService) private readonly _docSkeletonManagerService: DocSkeletonManagerService, + @ILogService private readonly _logService: ILogService + ) { + super(); + + this._initCustomRanges(); + } + + private _initCustomRanges() { + this.disposeWithMe(this._skeleton.dirty$.subscribe(() => { + this._dirty = true; + })); + + this.disposeWithMe( + fromEventSubject(this._context.engine.onTransformChange$).pipe( + filter((evt) => evt.type === TRANSFORM_CHANGE_OBSERVABLE_TYPE.resize) + ).subscribe(() => { + this._dirty = true; + }) + ); + + this.disposeWithMe(fromEventSubject(this._context.scene.onPointerMove$).pipe(throttleTime(16)).subscribe((evt) => { + this._hoverCustomRanges$.next( + this._calcActiveRanges(evt) + ); + })); + } + + private _buildCustomRangeLayoutsBySegment(segmentId?: string) { + const customRanges = this._context.unit.getSelfOrHeaderFooterModel(segmentId)?.getBody()?.customRanges ?? []; + const layouts: ICustomRangeLayout[] = []; + customRanges.forEach((range) => { + const textRange = { + startOffset: range.startIndex, + endOffset: range.endIndex, + collapsed: false, + }; + const rects = calcDocRangePositions(textRange, this._documents, this._skeleton); + if (!rects) { + return null; + } + const documentOffsetConfig = this._documents.getOffsetConfig(); + + layouts.push({ + customRange: range, + rects: rects.map((rect) => ({ + top: rect.top + documentOffsetConfig.docsTop, + bottom: rect.bottom + documentOffsetConfig.docsTop, + left: rect.left + documentOffsetConfig.docsLeft, + right: rect.right + documentOffsetConfig.docsLeft, + })), + segmentId, + }); + }); + + return layouts; + } + + private _buildCustomRangeLayouts() { + if (!this._dirty) { + return; + } + + const headerKeys = this._context.unit.headerModelMap.keys(); + const footerKeys = this._context.unit.footerModelMap.keys(); + + this._customRangeLayouts = [ + ...this._buildCustomRangeLayoutsBySegment(), + ...Array.from(headerKeys).flatMap((key) => this._buildCustomRangeLayoutsBySegment(key)), + ...Array.from(footerKeys).flatMap((key) => this._buildCustomRangeLayoutsBySegment(key)), + ]; + } + + private _calcActiveRanges(evt: IPointerEvent | IMouseEvent) { + this._buildCustomRangeLayouts(); + + const { offsetX, offsetY } = evt; + const { x, y } = transformOffset2Bound(offsetX, offsetY, this._context.scene); + const matchedRanges = this._customRangeLayouts.filter((layout) => { + return layout.rects.some((rect) => { + const { left, right, top, bottom } = rect; + if (x >= left && x <= right && y >= top && y <= bottom) { + return true; + } + return false; + }); + }); + + return matchedRanges.map( + (range) => ({ + segmentId: range.segmentId, + range: range.customRange, + }) + ); + } +} diff --git a/packages/docs-ui/src/services/doc-hover-manager.service.ts b/packages/docs-ui/src/services/doc-hover-manager.service.ts deleted file mode 100644 index 09f9c3f55d5..00000000000 --- a/packages/docs-ui/src/services/doc-hover-manager.service.ts +++ /dev/null @@ -1,138 +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, ICustomRange, IParagraph, Nullable } from '@univerjs/core'; -import { Disposable, IUniverInstanceService, UniverInstanceType } from '@univerjs/core'; -import { DocSkeletonManagerService, VIEWPORT_KEY } from '@univerjs/docs'; -import type { Documents, IMouseEvent, IPointerEvent, Viewport } from '@univerjs/engine-render'; -import { getParagraphByGlyph, IRenderManagerService, PageLayoutType, Vector2 } from '@univerjs/engine-render'; -import { Subject } from 'rxjs'; - -// TODO: this service need to be remove, calc cost to much time -export class DocHoverManagerService extends Disposable { - private readonly _activeCustomRanges$ = new Subject(); - readonly activeCustomRanges$ = this._activeCustomRanges$.asObservable(); - - private readonly _activeIndex$ = new Subject>(); - readonly activeIndex$ = this._activeIndex$.asObservable(); - - private readonly _bullet$ = new Subject>(); - readonly bullet$ = this._bullet$.asObservable(); - - private _scrolling = false; - - constructor( - @IUniverInstanceService private readonly _univerInstanceService: IUniverInstanceService, - @IRenderManagerService private readonly _renderManagerService: IRenderManagerService - ) { - super(); - - this.disposeWithMe(() => { - this._activeCustomRanges$.complete(); - this._activeIndex$.complete(); - }); - } - - private _getTransformCoordForDocumentOffset(document: Documents, viewport: Viewport, evtOffsetX: number, evtOffsetY: number) { - const { documentTransform } = document.getOffsetConfig(); - const originCoord = viewport.transformVector2SceneCoord(Vector2.FromArray([evtOffsetX, evtOffsetY])); - - if (!originCoord) { - return; - } - - return documentTransform.clone().invert().applyPoint(originCoord); - } - - private _calcActiveCustomRanges(offsetX: number, offsetY: number) { - const document = this._univerInstanceService.getCurrentUnitForType(UniverInstanceType.UNIVER_DOC); - if (!document) { - this._activeCustomRanges$.next([]); - return; - } - - const currentRender = this._renderManagerService.getRenderById(document.getUnitId()); - - if (!currentRender) { - return null; - } - const documentComponent = currentRender.mainComponent as Documents; - const skeleton = currentRender.with(DocSkeletonManagerService).getSkeleton(); - const { pageLayoutType = PageLayoutType.VERTICAL, pageMarginLeft, pageMarginTop, docsLeft, docsTop } = documentComponent.getOffsetConfig(); - const coord = this._getTransformCoordForDocumentOffset( - documentComponent, - currentRender.scene.getViewport(VIEWPORT_KEY.VIEW_MAIN)!, - offsetX, - offsetY - ); - if (!coord) { - return; - } - const node = skeleton.findNodeByCoord( - coord, - pageLayoutType, - pageMarginLeft, - pageMarginTop - ); - if (node && node.node) { - const left = node.node.left + pageMarginLeft; - const right = node.node.left + node.node.width + pageMarginLeft; - let index = node.node.parent!.st! + node.node.parent!.glyphGroup.indexOf(node.node)!; - const paragraph = getParagraphByGlyph(node.node, document.getBody()); - if (paragraph && paragraph.bullet && index === paragraph.paragraphStart) { - this._bullet$.next(paragraph); - this._activeIndex$.next(null); - this._activeCustomRanges$.next([]); - return; - } - - if (coord.x < left || coord.x > right) { - this._activeIndex$.next(null); - this._activeCustomRanges$.next([]); - this._bullet$.next(null); - return; - } - - if (paragraph && paragraph.bullet) { - index = index - 1; - } - - this._bullet$.next(null); - this._activeIndex$.next(index); - const ranges = document.getCustomRanges()?.filter((range) => range.startIndex <= index && range.endIndex >= index) ?? []; - this._activeCustomRanges$.next(ranges); - } - } - - onMouseMove(evt: IPointerEvent | IMouseEvent) { - if (this._scrolling) { - return; - } - const { offsetX, offsetY } = evt; - this._calcActiveCustomRanges(offsetX, offsetY); - } - - startScroll() { - if (this._scrolling) { - return; - } - this._scrolling = true; - } - - endScroll() { - this._scrolling = false; - } -} diff --git a/packages/docs-ui/src/services/doc-popup-manager.service.ts b/packages/docs-ui/src/services/doc-popup-manager.service.ts index ef5b41bf476..255d5c14cb0 100644 --- a/packages/docs-ui/src/services/doc-popup-manager.service.ts +++ b/packages/docs-ui/src/services/doc-popup-manager.service.ts @@ -57,11 +57,67 @@ export function transformPosition2Offset(x: number, y: number, scene: Scene) { }; } +// write a revert function for transformPosition2Offset +export function transformOffset2Bound(offsetX: number, offsetY: number, scene: Scene) { + const { scaleX, scaleY } = scene.getAncestorScale(); + const viewMain = scene.getViewport(VIEWPORT_KEY.VIEW_MAIN); + if (!viewMain) { + return { + x: offsetX, + y: offsetY, + }; + } + + const { viewportScrollX: actualScrollX, viewportScrollY: actualScrollY } = viewMain; + + const x = offsetX / scaleX + actualScrollX; + + const y = offsetY / scaleY + actualScrollY; + + return { + x, + y, + }; +} + export interface IDocCanvasPopup extends Pick { mask?: boolean; extraProps?: Record; } +export const calcDocRangePositions = (range: ITextRange, currentRender: IRender): IBoundRectNoAngle[] | undefined => { + const { scene, mainComponent, engine } = currentRender; + const skeleton = currentRender.with(DocSkeletonManagerService).getSkeleton(); + const startPosition = skeleton.findNodePositionByCharIndex(range.startOffset); + const endPosition = skeleton.findNodePositionByCharIndex(range.endOffset); + const document = mainComponent as Documents; + + if (!endPosition || !startPosition) { + return; + } + + const documentOffsetConfig = document.getOffsetConfig(); + const { docsLeft, docsTop } = documentOffsetConfig; + const canvasElement = engine.getCanvasElement(); + const canvasClientRect = canvasElement.getBoundingClientRect(); + const widthOfCanvas = pxToNum(canvasElement.style.width); // declared width + const { top, left, width } = canvasClientRect; // real width affected by scale + const scaleAdjust = width / widthOfCanvas; + + const { scaleX, scaleY } = scene.getAncestorScale(); + const convertor = new NodePositionConvertToCursor(documentOffsetConfig, skeleton); + const { contentBoxPointGroup } = convertor.getRangePointData(startPosition, endPosition); + const bounds = getLineBounding(contentBoxPointGroup); + const res = bounds.map((bound) => transformBound2OffsetBound(bound, scene)).map((i) => ({ + left: (i.left + docsLeft * scaleX) * scaleAdjust + left, + right: (i.right + docsLeft * scaleX) * scaleAdjust + left, + top: (i.top + docsTop * scaleY) * scaleAdjust + top, + bottom: (i.bottom + docsTop * scaleY) * scaleAdjust + top, + })); + + return res; +}; + export class DocCanvasPopManagerService extends Disposable { constructor( @Inject(ICanvasPopupService) private readonly _globalPopupManagerService: ICanvasPopupService, @@ -130,46 +186,13 @@ export class DocCanvasPopManagerService extends Disposable { } private _createRangePositionObserver(range: ITextRange, currentRender: IRender) { - const calc = (): IBoundRectNoAngle[] | undefined => { - const { scene, mainComponent, engine } = currentRender; - const skeleton = currentRender.with(DocSkeletonManagerService).getSkeleton(); - const startPosition = skeleton.findNodePositionByCharIndex(range.startOffset); - const endPosition = skeleton.findNodePositionByCharIndex(range.endOffset); - const document = mainComponent as Documents; - - if (!endPosition || !startPosition) { - return; - } - - const documentOffsetConfig = document.getOffsetConfig(); - const { docsLeft, docsTop } = documentOffsetConfig; - const canvasElement = engine.getCanvasElement(); - const canvasClientRect = canvasElement.getBoundingClientRect(); - const widthOfCanvas = pxToNum(canvasElement.style.width); // declared width - const { top, left, width } = canvasClientRect; // real width affected by scale - const scaleAdjust = width / widthOfCanvas; - - const { scaleX, scaleY } = scene.getAncestorScale(); - const convertor = new NodePositionConvertToCursor(documentOffsetConfig, skeleton); - const { contentBoxPointGroup } = convertor.getRangePointData(startPosition, endPosition); - const bounds = getLineBounding(contentBoxPointGroup); - const res = bounds.map((bound) => transformBound2OffsetBound(bound, scene)).map((i) => ({ - left: (i.left + docsLeft * scaleX) * scaleAdjust + left, - right: (i.right + docsLeft * scaleX) * scaleAdjust + left, - top: (i.top + docsTop * scaleY) * scaleAdjust + top, - bottom: (i.bottom + docsTop * scaleY) * scaleAdjust + top, - })); - - return res; - }; - - const positions = calc() ?? []; + const positions = calcDocRangePositions(range, currentRender) ?? []; const positions$ = new BehaviorSubject(positions); const disposable = new DisposableCollection(); disposable.add(this._commandService.onCommandExecuted((commandInfo) => { if (commandInfo.id === SetDocZoomRatioOperation.id) { - const position = calc(); + const position = calcDocRangePositions(range, currentRender); if (position) { positions$.next(position); } @@ -179,7 +202,7 @@ export class DocCanvasPopManagerService extends Disposable { const viewMain = currentRender.scene.getViewport(VIEWPORT_KEY.VIEW_MAIN); if (viewMain) { disposable.add(viewMain.onScrollAfter$.subscribeEvent(() => { - const position = calc(); + const position = calcDocRangePositions(range, currentRender); if (position) { positions$.next(position); } diff --git a/packages/engine-render/src/components/docs/layout/doc-skeleton.ts b/packages/engine-render/src/components/docs/layout/doc-skeleton.ts index c6682245197..583bc5679f3 100644 --- a/packages/engine-render/src/components/docs/layout/doc-skeleton.ts +++ b/packages/engine-render/src/components/docs/layout/doc-skeleton.ts @@ -16,6 +16,7 @@ import type { ColumnSeparatorType, ISectionColumnProperties, LocaleService, Nullable } from '@univerjs/core'; import { PRESET_LIST_TYPE, SectionType } from '@univerjs/core'; +import { Subject } from 'rxjs'; import type { IDocumentSkeletonCached, IDocumentSkeletonGlyph, @@ -112,6 +113,9 @@ function getPagePath(page: IDocumentSkeletonPage) { } export class DocumentSkeleton extends Skeleton { + private _dirty$ = new Subject(); + readonly dirty$ = this._dirty$.asObservable(); + private _skeletonData: Nullable; private _findLiquid: Liquid = new Liquid(); @@ -156,6 +160,7 @@ export class DocumentSkeleton extends Skeleton { // const start = +new Date(); this._skeletonData = this._createSkeleton(ctx, bounds); // console.log('skeleton calculate cost', +new Date() - start); + this._dirty$.next(true); } getSkeletonData() { From b7d054d4ca5e63ec7fdb07451b82618abc944d45 Mon Sep 17 00:00:00 2001 From: zw Date: Sat, 17 Aug 2024 02:09:55 +0800 Subject: [PATCH 02/43] feat: update click --- .../commands/operations/popup.operation.ts | 19 ++++++++++++++ ... => hyper-link-event.render-controller.ts} | 26 ++++++++++++++++--- .../src/controllers/ui.controller.ts | 3 ++- packages/docs-hyper-link-ui/src/plugin.ts | 4 +-- .../src/services/doc-event-manager.service.ts | 10 +++++++ 5 files changed, 55 insertions(+), 7 deletions(-) rename packages/docs-hyper-link-ui/src/controllers/render-controllers/{hyper-link-hover.render-controller.ts => hyper-link-event.render-controller.ts} (76%) diff --git a/packages/docs-hyper-link-ui/src/commands/operations/popup.operation.ts b/packages/docs-hyper-link-ui/src/commands/operations/popup.operation.ts index 496f9f84991..1ebeed73111 100644 --- a/packages/docs-hyper-link-ui/src/commands/operations/popup.operation.ts +++ b/packages/docs-hyper-link-ui/src/commands/operations/popup.operation.ts @@ -18,6 +18,7 @@ import type { DocumentDataModel, IAccessor, ICommand } from '@univerjs/core'; import { CommandType, IUniverInstanceService, UniverInstanceType } from '@univerjs/core'; import { DocSkeletonManagerService, serializeDocRange, TextSelectionManagerService } from '@univerjs/docs'; import { DocumentEditArea, IRenderManagerService } from '@univerjs/engine-render'; +import { DocHyperLinkModel } from '@univerjs/docs-hyper-link'; import { DocHyperLinkPopupService } from '../../services/hyper-link-popup.service'; export const shouldDisableAddLink = (accessor: IAccessor) => { @@ -103,3 +104,21 @@ export const ToggleDocHyperLinkInfoPopupOperation: ICommand = { + type: CommandType.OPERATION, + id: 'docs.operation.click-hyper-link', + handler(accessor, params) { + if (!params) { + return false; + } + const { unitId, linkId } = params; + const docLinkModel = accessor.get(DocHyperLinkModel); + const link = docLinkModel.getLink(unitId, linkId); + if (!link) { + return false; + } + window.open(link.payload, '_blank', 'noopener noreferrer'); + return true; + }, +}; diff --git a/packages/docs-hyper-link-ui/src/controllers/render-controllers/hyper-link-hover.render-controller.ts b/packages/docs-hyper-link-ui/src/controllers/render-controllers/hyper-link-event.render-controller.ts similarity index 76% rename from packages/docs-hyper-link-ui/src/controllers/render-controllers/hyper-link-hover.render-controller.ts rename to packages/docs-hyper-link-ui/src/controllers/render-controllers/hyper-link-event.render-controller.ts index 83a5db4662c..abfe0a81054 100644 --- a/packages/docs-hyper-link-ui/src/controllers/render-controllers/hyper-link-hover.render-controller.ts +++ b/packages/docs-hyper-link-ui/src/controllers/render-controllers/hyper-link-event.render-controller.ts @@ -18,10 +18,10 @@ import type { DocumentDataModel } from '@univerjs/core'; import { CustomRangeType, Disposable, ICommandService, Inject } from '@univerjs/core'; import { DocEventManagerService } from '@univerjs/docs-ui'; import type { IRenderContext, IRenderModule } from '@univerjs/engine-render'; -import { ToggleDocHyperLinkInfoPopupOperation } from '../../commands/operations/popup.operation'; +import { ClickDocHyperLinkOperation, ToggleDocHyperLinkInfoPopupOperation } from '../../commands/operations/popup.operation'; import { DocHyperLinkPopupService } from '../../services/hyper-link-popup.service'; -export class DocHyperLinkHoverRenderController extends Disposable implements IRenderModule { +export class DocHyperLinkEventRenderController extends Disposable implements IRenderModule { constructor( private readonly _context: IRenderContext, @Inject(DocEventManagerService) private readonly _docEventManagerService: DocEventManagerService, @@ -30,10 +30,11 @@ export class DocHyperLinkHoverRenderController extends Disposable implements IRe ) { super(); - this._init(); + this._initHover(); + this._initClick(); } - private _init() { + private _initHover() { this.disposeWithMe( this._docEventManagerService.hoverCustomRanges$.subscribe((ranges) => { const link = ranges.find((range) => range.range.rangeType === CustomRangeType.HYPERLINK); @@ -64,4 +65,21 @@ export class DocHyperLinkHoverRenderController extends Disposable implements IRe }) ); } + + private _initClick() { + this.disposeWithMe( + this._docEventManagerService.clickCustomRanges$.subscribe((ranges) => { + const link = ranges.find((range) => range.range.rangeType === CustomRangeType.HYPERLINK); + if (link) { + this._commandService.executeCommand( + ClickDocHyperLinkOperation.id, + { + unitId: this._context.unitId, + linkId: link.range.rangeId, + } + ); + } + }) + ); + } } diff --git a/packages/docs-hyper-link-ui/src/controllers/ui.controller.ts b/packages/docs-hyper-link-ui/src/controllers/ui.controller.ts index c4e8938d616..7c0972f4f35 100644 --- a/packages/docs-hyper-link-ui/src/controllers/ui.controller.ts +++ b/packages/docs-hyper-link-ui/src/controllers/ui.controller.ts @@ -22,7 +22,7 @@ import { DocHyperLinkEdit } from '../views/hyper-link-edit'; import { AddDocHyperLinkCommand } from '../commands/commands/add-link.command'; import { UpdateDocHyperLinkCommand } from '../commands/commands/update-link.command'; import { DeleteDocHyperLinkCommand } from '../commands/commands/delete-link.command'; -import { ShowDocHyperLinkEditPopupOperation, ToggleDocHyperLinkInfoPopupOperation } from '../commands/operations/popup.operation'; +import { ClickDocHyperLinkOperation, ShowDocHyperLinkEditPopupOperation, ToggleDocHyperLinkInfoPopupOperation } from '../commands/operations/popup.operation'; import { DocLinkPopup } from '../views/hyper-link-popup'; import { AddHyperLinkMenuItemFactory, addLinkShortcut, DOC_LINK_ICON } from './menu'; @@ -65,6 +65,7 @@ export class DocHyperLinkUIController extends Disposable { DeleteDocHyperLinkCommand, ShowDocHyperLinkEditPopupOperation, ToggleDocHyperLinkInfoPopupOperation, + ClickDocHyperLinkOperation, ].forEach((command) => { this._commandService.registerCommand(command); }); diff --git a/packages/docs-hyper-link-ui/src/plugin.ts b/packages/docs-hyper-link-ui/src/plugin.ts index 850388d3553..bc67ec721a0 100644 --- a/packages/docs-hyper-link-ui/src/plugin.ts +++ b/packages/docs-hyper-link-ui/src/plugin.ts @@ -26,7 +26,7 @@ import { DocHyperLinkSelectionController } from './controllers/doc-hyper-link-se import { DocHyperLinkRenderController } from './controllers/render-controllers/render.controller'; import { DocHyperLinkClipboardController } from './controllers/doc-hyper-link-clipboard.controller'; import { DocHyperLinkCustomRangeController } from './controllers/doc-hyper-link-custom-range.controller'; -import { DocHyperLinkHoverRenderController } from './controllers/render-controllers/hyper-link-hover.render-controller'; +import { DocHyperLinkEventRenderController } from './controllers/render-controllers/hyper-link-event.render-controller'; @DependentOn(UniverDocsHyperLinkPlugin) export class UniverDocsHyperLinkUIPlugin extends Plugin { @@ -66,7 +66,7 @@ export class UniverDocsHyperLinkUIPlugin extends Plugin { private _initRenderModule() { ([ [DocHyperLinkRenderController], - [DocHyperLinkHoverRenderController], + [DocHyperLinkEventRenderController], ] as Dependency[]).forEach((dep) => { this._renderManagerSrv.registerRenderModule(UniverInstanceType.UNIVER_DOC, dep); }); diff --git a/packages/docs-ui/src/services/doc-event-manager.service.ts b/packages/docs-ui/src/services/doc-event-manager.service.ts index 90fe0fa2199..65da2411efd 100644 --- a/packages/docs-ui/src/services/doc-event-manager.service.ts +++ b/packages/docs-ui/src/services/doc-event-manager.service.ts @@ -48,6 +48,9 @@ export class DocEventManagerService extends Disposable implements IRenderModule private _hoverCustomRanges$ = new Subject<{ range: ICustomRange; segmentId?: string }[]>(); readonly hoverCustomRanges$ = this._hoverCustomRanges$.pipe(distinctUntilChanged((pre, aft) => pre.length === aft.length && pre.every((item, i) => aft[i].range.rangeId === item.range.rangeId && aft[i].segmentId === item.segmentId))); + private _clickCustomRanges$ = new Subject<{ range: ICustomRange; segmentId?: string }[]>(); + readonly clickCustomRanges$ = this._clickCustomRanges$.asObservable(); + private _dirty = true; private _customRangeLayouts: ICustomRangeLayout[] = []; @@ -87,6 +90,13 @@ export class DocEventManagerService extends Disposable implements IRenderModule this._calcActiveRanges(evt) ); })); + + this.disposeWithMe(this._context.scene.onPointerUp$.subscribeEvent((evt) => { + const ranges = this._calcActiveRanges(evt); + if (ranges.length) { + this._clickCustomRanges$.next(ranges); + } + })); } private _buildCustomRangeLayoutsBySegment(segmentId?: string) { From 5c53a1e8b3adb78531a066d85420cb47da82c4af Mon Sep 17 00:00:00 2001 From: zw Date: Sat, 17 Aug 2024 02:15:09 +0800 Subject: [PATCH 03/43] feat: update --- .../hyper-link-event.render-controller.ts | 6 +++--- packages/docs-ui/src/services/doc-event-manager.service.ts | 7 +++---- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/packages/docs-hyper-link-ui/src/controllers/render-controllers/hyper-link-event.render-controller.ts b/packages/docs-hyper-link-ui/src/controllers/render-controllers/hyper-link-event.render-controller.ts index abfe0a81054..063801cda4d 100644 --- a/packages/docs-hyper-link-ui/src/controllers/render-controllers/hyper-link-event.render-controller.ts +++ b/packages/docs-hyper-link-ui/src/controllers/render-controllers/hyper-link-event.render-controller.ts @@ -68,14 +68,14 @@ export class DocHyperLinkEventRenderController extends Disposable implements IRe private _initClick() { this.disposeWithMe( - this._docEventManagerService.clickCustomRanges$.subscribe((ranges) => { - const link = ranges.find((range) => range.range.rangeType === CustomRangeType.HYPERLINK); + this._docEventManagerService.clickCustomRanges$.subscribe((range) => { + const link = range.range; if (link) { this._commandService.executeCommand( ClickDocHyperLinkOperation.id, { unitId: this._context.unitId, - linkId: link.range.rangeId, + linkId: link.rangeId, } ); } diff --git a/packages/docs-ui/src/services/doc-event-manager.service.ts b/packages/docs-ui/src/services/doc-event-manager.service.ts index 65da2411efd..5f450828519 100644 --- a/packages/docs-ui/src/services/doc-event-manager.service.ts +++ b/packages/docs-ui/src/services/doc-event-manager.service.ts @@ -48,7 +48,7 @@ export class DocEventManagerService extends Disposable implements IRenderModule private _hoverCustomRanges$ = new Subject<{ range: ICustomRange; segmentId?: string }[]>(); readonly hoverCustomRanges$ = this._hoverCustomRanges$.pipe(distinctUntilChanged((pre, aft) => pre.length === aft.length && pre.every((item, i) => aft[i].range.rangeId === item.range.rangeId && aft[i].segmentId === item.segmentId))); - private _clickCustomRanges$ = new Subject<{ range: ICustomRange; segmentId?: string }[]>(); + private _clickCustomRanges$ = new Subject<{ range: ICustomRange; segmentId?: string }>(); readonly clickCustomRanges$ = this._clickCustomRanges$.asObservable(); private _dirty = true; @@ -64,8 +64,7 @@ export class DocEventManagerService extends Disposable implements IRenderModule constructor( private _context: IRenderContext, - @Inject(DocSkeletonManagerService) private readonly _docSkeletonManagerService: DocSkeletonManagerService, - @ILogService private readonly _logService: ILogService + @Inject(DocSkeletonManagerService) private readonly _docSkeletonManagerService: DocSkeletonManagerService ) { super(); @@ -94,7 +93,7 @@ export class DocEventManagerService extends Disposable implements IRenderModule this.disposeWithMe(this._context.scene.onPointerUp$.subscribeEvent((evt) => { const ranges = this._calcActiveRanges(evt); if (ranges.length) { - this._clickCustomRanges$.next(ranges); + this._clickCustomRanges$.next(ranges.pop()!); } })); } From a6c46d613012754ad1e752e4cf5c06789c3beaa9 Mon Sep 17 00:00:00 2001 From: zw Date: Sat, 17 Aug 2024 02:22:53 +0800 Subject: [PATCH 04/43] feat: update --- .../src/services/doc-event-manager.service.ts | 59 +++++++++++++------ 1 file changed, 41 insertions(+), 18 deletions(-) diff --git a/packages/docs-ui/src/services/doc-event-manager.service.ts b/packages/docs-ui/src/services/doc-event-manager.service.ts index 5f450828519..5118234763c 100644 --- a/packages/docs-ui/src/services/doc-event-manager.service.ts +++ b/packages/docs-ui/src/services/doc-event-manager.service.ts @@ -14,20 +14,26 @@ * limitations under the License. */ -import type { CustomRangeType, DocumentDataModel, ICustomRange, ITextRange } from '@univerjs/core'; -import { Disposable, fromEventSubject, ILogService, Inject } from '@univerjs/core'; +import type { DocumentDataModel, ICustomRange, IParagraph, ITextRange } from '@univerjs/core'; +import { Disposable, fromEventSubject, Inject } from '@univerjs/core'; import { DocSkeletonManagerService } from '@univerjs/docs'; import type { Documents, DocumentSkeleton, IBoundRectNoAngle, IMouseEvent, IPointerEvent, IRender, IRenderContext, IRenderModule } from '@univerjs/engine-render'; import { getLineBounding, NodePositionConvertToCursor, pxToNum, TRANSFORM_CHANGE_OBSERVABLE_TYPE } from '@univerjs/engine-render'; import { distinctUntilChanged, filter, Subject, throttleTime } from 'rxjs'; import { transformOffset2Bound } from './doc-popup-manager.service'; -interface ICustomRangeLayout { +interface ICustomRangeBound { customRange: ICustomRange; rects: IBoundRectNoAngle[]; segmentId?: string; } +interface IBulletBound { + rect: IBoundRectNoAngle; + segmentId?: string; + paragraph: IParagraph; +} + const calcDocRangePositions = (range: ITextRange, documents: Documents, skeleton: DocumentSkeleton): IBoundRectNoAngle[] | undefined => { const startPosition = skeleton.findNodePositionByCharIndex(range.startOffset); const endPosition = skeleton.findNodePositionByCharIndex(range.endOffset); @@ -51,8 +57,19 @@ export class DocEventManagerService extends Disposable implements IRenderModule private _clickCustomRanges$ = new Subject<{ range: ICustomRange; segmentId?: string }>(); readonly clickCustomRanges$ = this._clickCustomRanges$.asObservable(); - private _dirty = true; - private _customRangeLayouts: ICustomRangeLayout[] = []; + private _customRangeDirty = true; + private _bulletDirty = true; + + /** + * cache the bounding of custom ranges, + * it will be updated when the doc-skeleton is recalculated + */ + private _customRangeBounds: ICustomRangeBound[] = []; + /** + * cache the bounding of bullets, + * it will be updated when the doc-skeleton is recalculated + */ + private _bulletBounds: IBulletBound[] = []; private get _skeleton() { return this._docSkeletonManagerService.getSkeleton(); @@ -68,22 +85,27 @@ export class DocEventManagerService extends Disposable implements IRenderModule ) { super(); + this._initResetDirty(); this._initCustomRanges(); } - private _initCustomRanges() { + private _initResetDirty() { this.disposeWithMe(this._skeleton.dirty$.subscribe(() => { - this._dirty = true; + this._customRangeDirty = true; + this._bulletDirty = true; })); this.disposeWithMe( fromEventSubject(this._context.engine.onTransformChange$).pipe( filter((evt) => evt.type === TRANSFORM_CHANGE_OBSERVABLE_TYPE.resize) ).subscribe(() => { - this._dirty = true; + this._customRangeDirty = true; + this._bulletDirty = true; }) ); + } + private _initCustomRanges() { this.disposeWithMe(fromEventSubject(this._context.scene.onPointerMove$).pipe(throttleTime(16)).subscribe((evt) => { this._hoverCustomRanges$.next( this._calcActiveRanges(evt) @@ -98,9 +120,9 @@ export class DocEventManagerService extends Disposable implements IRenderModule })); } - private _buildCustomRangeLayoutsBySegment(segmentId?: string) { + private _buildCustomRangeBoundsBySegment(segmentId?: string) { const customRanges = this._context.unit.getSelfOrHeaderFooterModel(segmentId)?.getBody()?.customRanges ?? []; - const layouts: ICustomRangeLayout[] = []; + const layouts: ICustomRangeBound[] = []; customRanges.forEach((range) => { const textRange = { startOffset: range.startIndex, @@ -128,27 +150,28 @@ export class DocEventManagerService extends Disposable implements IRenderModule return layouts; } - private _buildCustomRangeLayouts() { - if (!this._dirty) { + private _buildCustomRangeBounds() { + if (!this._customRangeDirty) { return; } + this._customRangeDirty = false; const headerKeys = this._context.unit.headerModelMap.keys(); const footerKeys = this._context.unit.footerModelMap.keys(); - this._customRangeLayouts = [ - ...this._buildCustomRangeLayoutsBySegment(), - ...Array.from(headerKeys).flatMap((key) => this._buildCustomRangeLayoutsBySegment(key)), - ...Array.from(footerKeys).flatMap((key) => this._buildCustomRangeLayoutsBySegment(key)), + this._customRangeBounds = [ + ...this._buildCustomRangeBoundsBySegment(), + ...Array.from(headerKeys).flatMap((key) => this._buildCustomRangeBoundsBySegment(key)), + ...Array.from(footerKeys).flatMap((key) => this._buildCustomRangeBoundsBySegment(key)), ]; } private _calcActiveRanges(evt: IPointerEvent | IMouseEvent) { - this._buildCustomRangeLayouts(); + this._buildCustomRangeBounds(); const { offsetX, offsetY } = evt; const { x, y } = transformOffset2Bound(offsetX, offsetY, this._context.scene); - const matchedRanges = this._customRangeLayouts.filter((layout) => { + const matchedRanges = this._customRangeBounds.filter((layout) => { return layout.rects.some((rect) => { const { left, right, top, bottom } = rect; if (x >= left && x <= right && y >= top && y <= bottom) { From e5d7e5d68b3b6b2e05e46243911d3f9ef64c07e5 Mon Sep 17 00:00:00 2001 From: zw Date: Sat, 17 Aug 2024 17:20:57 +0800 Subject: [PATCH 05/43] feat: temp --- .../src/services/hyper-link-popup.service.ts | 6 +- .../src/services/doc-event-manager.service.ts | 67 ++++++++++++------- .../src/services/doc-popup-manager.service.ts | 12 ++-- 3 files changed, 53 insertions(+), 32 deletions(-) diff --git a/packages/docs-hyper-link-ui/src/services/hyper-link-popup.service.ts b/packages/docs-hyper-link-ui/src/services/hyper-link-popup.service.ts index 4962e96508d..ffd5b1a9467 100644 --- a/packages/docs-hyper-link-ui/src/services/hyper-link-popup.service.ts +++ b/packages/docs-hyper-link-ui/src/services/hyper-link-popup.service.ts @@ -32,6 +32,8 @@ export interface ILinkInfo { unitId: string; linkId: string; rangeIndex: number; + segmentId?: string; + segmentPage?: number; } export class DocHyperLinkPopupService extends Disposable { @@ -127,7 +129,7 @@ export class DocHyperLinkPopupService extends Disposable { if (!doc || !link) { return; } - const range = doc.getBody()?.customRanges?.[rangeIndex]; + const range = doc.getSelfOrHeaderFooterModel(info.segmentId).getBody()?.customRanges?.[rangeIndex]; this._showingLink$.next({ unitId, linkId, rangeIndex }); if (!range) { return; @@ -138,6 +140,8 @@ export class DocHyperLinkPopupService extends Disposable { collapsed: false, startOffset: range.startIndex + SKIT_PLACEHOLDER, endOffset: range.endIndex + 1, + segmentId: info.segmentId, + segmentPage: info.segmentPage, }, { componentKey: DocLinkPopup.componentKey, diff --git a/packages/docs-ui/src/services/doc-event-manager.service.ts b/packages/docs-ui/src/services/doc-event-manager.service.ts index 5118234763c..19fa0c83ee6 100644 --- a/packages/docs-ui/src/services/doc-event-manager.service.ts +++ b/packages/docs-ui/src/services/doc-event-manager.service.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import type { DocumentDataModel, ICustomRange, IParagraph, ITextRange } from '@univerjs/core'; +import type { DocumentDataModel, ICustomRange, IParagraph, ITextRangeParam } from '@univerjs/core'; import { Disposable, fromEventSubject, Inject } from '@univerjs/core'; import { DocSkeletonManagerService } from '@univerjs/docs'; import type { Documents, DocumentSkeleton, IBoundRectNoAngle, IMouseEvent, IPointerEvent, IRender, IRenderContext, IRenderModule } from '@univerjs/engine-render'; @@ -26,18 +26,20 @@ interface ICustomRangeBound { customRange: ICustomRange; rects: IBoundRectNoAngle[]; segmentId?: string; + segmentPageIndex: number; } interface IBulletBound { rect: IBoundRectNoAngle; segmentId?: string; + segmentPageIndex: number; paragraph: IParagraph; } -const calcDocRangePositions = (range: ITextRange, documents: Documents, skeleton: DocumentSkeleton): IBoundRectNoAngle[] | undefined => { - const startPosition = skeleton.findNodePositionByCharIndex(range.startOffset); - const endPosition = skeleton.findNodePositionByCharIndex(range.endOffset); - +const calcDocRangePositions = (range: ITextRangeParam, documents: Documents, skeleton: DocumentSkeleton, pageIndex: number): IBoundRectNoAngle[] | undefined => { + const startPosition = skeleton.findNodePositionByCharIndex(range.startOffset, true, range.segmentId, pageIndex); + const endPosition = skeleton.findNodePositionByCharIndex(range.endOffset, true, range.segmentId, pageIndex); + // console.log('===startPosition', range, startPosition, endPosition); if (!endPosition || !startPosition) { return; } @@ -123,28 +125,43 @@ export class DocEventManagerService extends Disposable implements IRenderModule private _buildCustomRangeBoundsBySegment(segmentId?: string) { const customRanges = this._context.unit.getSelfOrHeaderFooterModel(segmentId)?.getBody()?.customRanges ?? []; const layouts: ICustomRangeBound[] = []; + customRanges.forEach((range) => { - const textRange = { + const textRange: ITextRangeParam = { startOffset: range.startIndex, endOffset: range.endIndex, collapsed: false, + segmentId, + }; + + const calcRect = (pageIndex: number) => { + const rects = calcDocRangePositions(textRange, this._documents, this._skeleton, pageIndex); + if (!rects) { + return null; + } + const documentOffsetConfig = this._documents.getOffsetConfig(); + + layouts.push({ + customRange: range, + rects: rects.map((rect) => ({ + top: rect.top + documentOffsetConfig.docsTop, + bottom: rect.bottom + documentOffsetConfig.docsTop, + left: rect.left + documentOffsetConfig.docsLeft, + right: rect.right + documentOffsetConfig.docsLeft, + })), + segmentId, + segmentPageIndex: pageIndex, + }); }; - const rects = calcDocRangePositions(textRange, this._documents, this._skeleton); - if (!rects) { - return null; + + if (segmentId) { + const pageSize = (this._skeleton.getSkeletonData()?.pages.length ?? 0); + for (let i = 0; i < pageSize; i++) { + calcRect(i); + } + } else { + calcRect(-1); } - const documentOffsetConfig = this._documents.getOffsetConfig(); - - layouts.push({ - customRange: range, - rects: rects.map((rect) => ({ - top: rect.top + documentOffsetConfig.docsTop, - bottom: rect.bottom + documentOffsetConfig.docsTop, - left: rect.left + documentOffsetConfig.docsLeft, - right: rect.right + documentOffsetConfig.docsLeft, - })), - segmentId, - }); }); return layouts; @@ -156,13 +173,13 @@ export class DocEventManagerService extends Disposable implements IRenderModule } this._customRangeDirty = false; - const headerKeys = this._context.unit.headerModelMap.keys(); - const footerKeys = this._context.unit.footerModelMap.keys(); + const headerKeys = Array.from(this._context.unit.headerModelMap.keys()); + const footerKeys = Array.from(this._context.unit.footerModelMap.keys()); this._customRangeBounds = [ ...this._buildCustomRangeBoundsBySegment(), - ...Array.from(headerKeys).flatMap((key) => this._buildCustomRangeBoundsBySegment(key)), - ...Array.from(footerKeys).flatMap((key) => this._buildCustomRangeBoundsBySegment(key)), + ...(headerKeys.map((key) => this._buildCustomRangeBoundsBySegment(key)).flat()), + ...(footerKeys.map((key) => this._buildCustomRangeBoundsBySegment(key)).flat()), ]; } diff --git a/packages/docs-ui/src/services/doc-popup-manager.service.ts b/packages/docs-ui/src/services/doc-popup-manager.service.ts index 255d5c14cb0..40b2709c371 100644 --- a/packages/docs-ui/src/services/doc-popup-manager.service.ts +++ b/packages/docs-ui/src/services/doc-popup-manager.service.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import type { IDisposable, ITextRange } from '@univerjs/core'; +import type { IDisposable, ITextRangeParam } from '@univerjs/core'; import { Disposable, DisposableCollection, ICommandService, Inject, IUniverInstanceService, UniverInstanceType } from '@univerjs/core'; import { getLineBounding, IRenderManagerService, NodePositionConvertToCursor, pxToNum } from '@univerjs/engine-render'; import type { BaseObject, Documents, IBoundRectNoAngle, IRender, Scene } from '@univerjs/engine-render'; @@ -85,11 +85,11 @@ export interface IDocCanvasPopup extends Pick; } -export const calcDocRangePositions = (range: ITextRange, currentRender: IRender): IBoundRectNoAngle[] | undefined => { +export const calcDocRangePositions = (range: ITextRangeParam, currentRender: IRender): IBoundRectNoAngle[] | undefined => { const { scene, mainComponent, engine } = currentRender; const skeleton = currentRender.with(DocSkeletonManagerService).getSkeleton(); - const startPosition = skeleton.findNodePositionByCharIndex(range.startOffset); - const endPosition = skeleton.findNodePositionByCharIndex(range.endOffset); + const startPosition = skeleton.findNodePositionByCharIndex(range.startOffset, false, range.segmentId, range.segmentPage); + const endPosition = skeleton.findNodePositionByCharIndex(range.endOffset, false, range.segmentId, range.segmentPage); const document = mainComponent as Documents; if (!endPosition || !startPosition) { @@ -185,7 +185,7 @@ export class DocCanvasPopManagerService extends Disposable { }; } - private _createRangePositionObserver(range: ITextRange, currentRender: IRender) { + private _createRangePositionObserver(range: ITextRangeParam, currentRender: IRender) { const positions = calcDocRangePositions(range, currentRender) ?? []; const positions$ = new BehaviorSubject(positions); const disposable = new DisposableCollection(); @@ -256,7 +256,7 @@ export class DocCanvasPopManagerService extends Disposable { }; } - attachPopupToRange(range: ITextRange, popup: IDocCanvasPopup): IDisposable { + attachPopupToRange(range: ITextRangeParam, popup: IDocCanvasPopup): IDisposable { const workbook = this._univerInstanceService.getCurrentUnitForType(UniverInstanceType.UNIVER_DOC)!; const unitId = workbook.getUnitId(); const { direction = 'top' } = popup; From f9528c5ed07c0c7abef61e7262f9815a245d4000 Mon Sep 17 00:00:00 2001 From: zw Date: Sat, 17 Aug 2024 18:15:22 +0800 Subject: [PATCH 06/43] feat: update --- .../src/commands/operations/popup.operation.ts | 1 + .../hyper-link-event.render-controller.ts | 1 + packages/docs-ui/src/services/doc-event-manager.service.ts | 7 ++++--- packages/docs-ui/src/services/doc-popup-manager.service.ts | 4 ++-- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/packages/docs-hyper-link-ui/src/commands/operations/popup.operation.ts b/packages/docs-hyper-link-ui/src/commands/operations/popup.operation.ts index 1ebeed73111..b7c495daa2a 100644 --- a/packages/docs-hyper-link-ui/src/commands/operations/popup.operation.ts +++ b/packages/docs-hyper-link-ui/src/commands/operations/popup.operation.ts @@ -88,6 +88,7 @@ export interface IShowDocHyperLinkInfoPopupOperationParams { segmentId?: string; unitId: string; rangeIndex: number; + segmentPage?: number; } export const ToggleDocHyperLinkInfoPopupOperation: ICommand = { diff --git a/packages/docs-hyper-link-ui/src/controllers/render-controllers/hyper-link-event.render-controller.ts b/packages/docs-hyper-link-ui/src/controllers/render-controllers/hyper-link-event.render-controller.ts index 063801cda4d..175e570eb9d 100644 --- a/packages/docs-hyper-link-ui/src/controllers/render-controllers/hyper-link-event.render-controller.ts +++ b/packages/docs-hyper-link-ui/src/controllers/render-controllers/hyper-link-event.render-controller.ts @@ -51,6 +51,7 @@ export class DocHyperLinkEventRenderController extends Disposable implements IRe linkId: link.range.rangeId, segmentId: link.segmentId, rangeIndex: linkIndex, + segmentPage: link.segmentPageIndex, } ); } diff --git a/packages/docs-ui/src/services/doc-event-manager.service.ts b/packages/docs-ui/src/services/doc-event-manager.service.ts index 19fa0c83ee6..a25919b924e 100644 --- a/packages/docs-ui/src/services/doc-event-manager.service.ts +++ b/packages/docs-ui/src/services/doc-event-manager.service.ts @@ -53,10 +53,10 @@ const calcDocRangePositions = (range: ITextRangeParam, documents: Documents, ske }; export class DocEventManagerService extends Disposable implements IRenderModule { - private _hoverCustomRanges$ = new Subject<{ range: ICustomRange; segmentId?: string }[]>(); - readonly hoverCustomRanges$ = this._hoverCustomRanges$.pipe(distinctUntilChanged((pre, aft) => pre.length === aft.length && pre.every((item, i) => aft[i].range.rangeId === item.range.rangeId && aft[i].segmentId === item.segmentId))); + private _hoverCustomRanges$ = new Subject<{ range: ICustomRange; segmentId?: string; segmentPageIndex: number }[]>(); + readonly hoverCustomRanges$ = this._hoverCustomRanges$.pipe(distinctUntilChanged((pre, aft) => pre.length === aft.length && pre.every((item, i) => aft[i].range.rangeId === item.range.rangeId && aft[i].segmentId === item.segmentId && aft[i].segmentPageIndex === item.segmentPageIndex))); - private _clickCustomRanges$ = new Subject<{ range: ICustomRange; segmentId?: string }>(); + private _clickCustomRanges$ = new Subject<{ range: ICustomRange; segmentId?: string; segmentPageIndex: number }>(); readonly clickCustomRanges$ = this._clickCustomRanges$.asObservable(); private _customRangeDirty = true; @@ -202,6 +202,7 @@ export class DocEventManagerService extends Disposable implements IRenderModule (range) => ({ segmentId: range.segmentId, range: range.customRange, + segmentPageIndex: range.segmentPageIndex, }) ); } diff --git a/packages/docs-ui/src/services/doc-popup-manager.service.ts b/packages/docs-ui/src/services/doc-popup-manager.service.ts index 40b2709c371..3c37341fa6b 100644 --- a/packages/docs-ui/src/services/doc-popup-manager.service.ts +++ b/packages/docs-ui/src/services/doc-popup-manager.service.ts @@ -106,8 +106,8 @@ export const calcDocRangePositions = (range: ITextRangeParam, currentRender: IRe const { scaleX, scaleY } = scene.getAncestorScale(); const convertor = new NodePositionConvertToCursor(documentOffsetConfig, skeleton); - const { contentBoxPointGroup } = convertor.getRangePointData(startPosition, endPosition); - const bounds = getLineBounding(contentBoxPointGroup); + const { borderBoxPointGroup } = convertor.getRangePointData(startPosition, endPosition); + const bounds = getLineBounding(borderBoxPointGroup); const res = bounds.map((bound) => transformBound2OffsetBound(bound, scene)).map((i) => ({ left: (i.left + docsLeft * scaleX) * scaleAdjust + left, right: (i.right + docsLeft * scaleX) * scaleAdjust + left, From 35fc4ae1c22e670139d00830debfc73bfa23f852 Mon Sep 17 00:00:00 2001 From: zw Date: Sat, 17 Aug 2024 18:56:07 +0800 Subject: [PATCH 07/43] feat: update --- .../doc-checklist.render-controller.ts | 25 ++-- .../src/services/doc-event-manager.service.ts | 119 +++++++++++++++++- 2 files changed, 129 insertions(+), 15 deletions(-) diff --git a/packages/docs-ui/src/controllers/render-controllers/doc-checklist.render-controller.ts b/packages/docs-ui/src/controllers/render-controllers/doc-checklist.render-controller.ts index 5d8b3dbcedd..e6081ccaa0a 100644 --- a/packages/docs-ui/src/controllers/render-controllers/doc-checklist.render-controller.ts +++ b/packages/docs-ui/src/controllers/render-controllers/doc-checklist.render-controller.ts @@ -15,17 +15,18 @@ */ import type { Documents, IRenderContext, IRenderModule, Viewport } from '@univerjs/engine-render'; -import { getParagraphByGlyph, GlyphType, PageLayoutType, Vector2 } from '@univerjs/engine-render'; +import { CURSOR_TYPE, getParagraphByGlyph, GlyphType, PageLayoutType, Vector2 } from '@univerjs/engine-render'; import type { DocumentDataModel } from '@univerjs/core'; import { Disposable, ICommandService, Inject, PresetListType } from '@univerjs/core'; import { DocSkeletonManagerService, ToggleCheckListCommand, VIEWPORT_KEY } from '@univerjs/docs'; -// import { DocEventManagerService } from '../../services/doc-event-manager.service'; +import { DocEventManagerService } from '../../services/doc-event-manager.service'; export class DocChecklistRenderController extends Disposable implements IRenderModule { constructor( private _context: IRenderContext, @Inject(DocSkeletonManagerService) private readonly _docSkeletonManagerService: DocSkeletonManagerService, - @ICommandService private readonly _commandService: ICommandService + @ICommandService private readonly _commandService: ICommandService, + @Inject(DocEventManagerService) private readonly _docEventManagerService: DocEventManagerService ) { super(); @@ -75,15 +76,15 @@ export class DocChecklistRenderController extends Disposable implements IRenderM } private _initHoverCursor() { - // this.disposeWithMe( - // this._docHoverManagerService.bullet$.subscribe((paragraph) => { - // if (paragraph) { - // this._context.mainComponent!.setCursor(CURSOR_TYPE.POINTER); - // } else { - // this._context.mainComponent!.setCursor(CURSOR_TYPE.TEXT); - // } - // }) - // ); + this.disposeWithMe( + this._docEventManagerService.hoverBullet$.subscribe((paragraph) => { + if (paragraph) { + this._context.mainComponent!.setCursor(CURSOR_TYPE.POINTER); + } else { + this._context.mainComponent!.setCursor(CURSOR_TYPE.TEXT); + } + }) + ); } private _getTransformCoordForDocumentOffset(document: Documents, viewport: Viewport, evtOffsetX: number, evtOffsetY: number) { diff --git a/packages/docs-ui/src/services/doc-event-manager.service.ts b/packages/docs-ui/src/services/doc-event-manager.service.ts index a25919b924e..c11ec1157ba 100644 --- a/packages/docs-ui/src/services/doc-event-manager.service.ts +++ b/packages/docs-ui/src/services/doc-event-manager.service.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import type { DocumentDataModel, ICustomRange, IParagraph, ITextRangeParam } from '@univerjs/core'; +import type { DocumentDataModel, ICustomRange, IParagraph, ITextRangeParam, Nullable } from '@univerjs/core'; import { Disposable, fromEventSubject, Inject } from '@univerjs/core'; import { DocSkeletonManagerService } from '@univerjs/docs'; import type { Documents, DocumentSkeleton, IBoundRectNoAngle, IMouseEvent, IPointerEvent, IRender, IRenderContext, IRenderModule } from '@univerjs/engine-render'; @@ -52,13 +52,27 @@ const calcDocRangePositions = (range: ITextRangeParam, documents: Documents, ske return bounds; }; +interface ICustomRangeActive { + range: ICustomRange; segmentId?: string; segmentPageIndex: number; +} + +interface IBulletActive { + paragraph: IParagraph; segmentId?: string; segmentPageIndex: number; +} + export class DocEventManagerService extends Disposable implements IRenderModule { - private _hoverCustomRanges$ = new Subject<{ range: ICustomRange; segmentId?: string; segmentPageIndex: number }[]>(); + private _hoverCustomRanges$ = new Subject(); readonly hoverCustomRanges$ = this._hoverCustomRanges$.pipe(distinctUntilChanged((pre, aft) => pre.length === aft.length && pre.every((item, i) => aft[i].range.rangeId === item.range.rangeId && aft[i].segmentId === item.segmentId && aft[i].segmentPageIndex === item.segmentPageIndex))); - private _clickCustomRanges$ = new Subject<{ range: ICustomRange; segmentId?: string; segmentPageIndex: number }>(); + private _clickCustomRanges$ = new Subject(); readonly clickCustomRanges$ = this._clickCustomRanges$.asObservable(); + private _hoverBullet$ = new Subject>(); + readonly hoverBullet$ = this._hoverBullet$.pipe(distinctUntilChanged((pre, aft) => pre?.paragraph.startIndex === aft?.paragraph.startIndex && pre?.segmentId === aft?.segmentId && pre?.segmentPageIndex === aft?.segmentPageIndex)); + + private _clickBullet$ = new Subject(); + readonly clickBullets$ = this._clickBullet$.asObservable(); + private _customRangeDirty = true; private _bulletDirty = true; @@ -91,6 +105,12 @@ export class DocEventManagerService extends Disposable implements IRenderModule this._initCustomRanges(); } + override dispose() { + this._hoverCustomRanges$.complete(); + this._clickCustomRanges$.complete(); + super.dispose(); + } + private _initResetDirty() { this.disposeWithMe(this._skeleton.dirty$.subscribe(() => { this._customRangeDirty = true; @@ -112,6 +132,9 @@ export class DocEventManagerService extends Disposable implements IRenderModule this._hoverCustomRanges$.next( this._calcActiveRanges(evt) ); + this._hoverBullet$.next( + this._calcActiveBullet(evt) + ); })); this.disposeWithMe(this._context.scene.onPointerUp$.subscribeEvent((evt) => { @@ -119,6 +142,11 @@ export class DocEventManagerService extends Disposable implements IRenderModule if (ranges.length) { this._clickCustomRanges$.next(ranges.pop()!); } + + const bullet = this._calcActiveBullet(evt); + if (bullet) { + this._clickBullet$.next(bullet); + } })); } @@ -206,4 +234,89 @@ export class DocEventManagerService extends Disposable implements IRenderModule }) ); } + + private _buildBulletBoundsBySegment(segmentId?: string) { + const paragraphs = this._context.unit.getSelfOrHeaderFooterModel(segmentId)?.getBody()?.paragraphs ?? []; + const bounds: IBulletBound[] = []; + const paragraphRanges = paragraphs.map((paragraph, i) => ({ + ...paragraph, + paragraphStart: (paragraphs[i - 1]?.startIndex ?? -1) + 1, + paragraphEnd: paragraph.startIndex, + })); + + paragraphRanges.forEach((paragraph) => { + if (paragraph.bullet && paragraph.bullet.listType === 'CHECK_LIST') { + const node = this._skeleton.findNodeByCharIndex(paragraph.paragraphStart, segmentId); + if (!node) { + return; + } + const bulletNode = node.parent?.glyphGroup[0]; + const { pageMarginLeft, pageMarginTop, docsLeft, docsTop } = this._documents.getOffsetConfig(); + if (!bulletNode) { + return; + } + + const left = node.left + pageMarginLeft + docsLeft; + const right = node.left + node.width + pageMarginLeft; + let top = pageMarginTop + docsTop; + + let p = bulletNode.parent; + while (p) { + //@ts-ignore + top += p.top ?? 0; + // @ts-ignore + p = p.parent; + } + + const height = bulletNode.bBox.aba + bulletNode.bBox.abd; + const bottom = top + height; + + bounds.push({ + rect: { + left, + right, + top, + bottom, + }, + segmentId, + segmentPageIndex: 0, + paragraph, + }); + } + }); + // console.log('===bounds', bounds); + return bounds; + } + + private _buildBulletBounds() { + if (!this._bulletDirty) { + return; + } + this._bulletDirty = false; + + const headerKeys = Array.from(this._context.unit.headerModelMap.keys()); + const footerKeys = Array.from(this._context.unit.footerModelMap.keys()); + + this._bulletBounds = [ + ...this._buildBulletBoundsBySegment(), + ...(headerKeys.map((key) => this._buildBulletBoundsBySegment(key)).flat()), + ...(footerKeys.map((key) => this._buildBulletBoundsBySegment(key)).flat()), + ]; + } + + private _calcActiveBullet(evt: IPointerEvent | IMouseEvent) { + this._buildBulletBounds(); + + const { offsetX, offsetY } = evt; + const { x, y } = transformOffset2Bound(offsetX, offsetY, this._context.scene); + const bullet = this._bulletBounds.find((layout) => { + const { left, right, top, bottom } = layout.rect; + if (x >= left && x <= right && y >= top && y <= bottom) { + return true; + } + return false; + }); + // console.log('===bullet', bullet); + return bullet; + } } From ea68962e15aa108934e8805bb0bf35f732124ab0 Mon Sep 17 00:00:00 2001 From: zw Date: Sat, 17 Aug 2024 19:09:46 +0800 Subject: [PATCH 08/43] feat: update --- .../doc-checklist.render-controller.ts | 10 +++++++--- packages/docs/src/commands/commands/list.command.ts | 10 +++++++--- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/packages/docs-ui/src/controllers/render-controllers/doc-checklist.render-controller.ts b/packages/docs-ui/src/controllers/render-controllers/doc-checklist.render-controller.ts index e6081ccaa0a..68a083e431d 100644 --- a/packages/docs-ui/src/controllers/render-controllers/doc-checklist.render-controller.ts +++ b/packages/docs-ui/src/controllers/render-controllers/doc-checklist.render-controller.ts @@ -18,7 +18,7 @@ import type { Documents, IRenderContext, IRenderModule, Viewport } from '@univer import { CURSOR_TYPE, getParagraphByGlyph, GlyphType, PageLayoutType, Vector2 } from '@univerjs/engine-render'; import type { DocumentDataModel } from '@univerjs/core'; import { Disposable, ICommandService, Inject, PresetListType } from '@univerjs/core'; -import { DocSkeletonManagerService, ToggleCheckListCommand, VIEWPORT_KEY } from '@univerjs/docs'; +import { DocSkeletonManagerService, TextSelectionManagerService, ToggleCheckListCommand, VIEWPORT_KEY } from '@univerjs/docs'; import { DocEventManagerService } from '../../services/doc-event-manager.service'; export class DocChecklistRenderController extends Disposable implements IRenderModule { @@ -26,7 +26,8 @@ export class DocChecklistRenderController extends Disposable implements IRenderM private _context: IRenderContext, @Inject(DocSkeletonManagerService) private readonly _docSkeletonManagerService: DocSkeletonManagerService, @ICommandService private readonly _commandService: ICommandService, - @Inject(DocEventManagerService) private readonly _docEventManagerService: DocEventManagerService + @Inject(DocEventManagerService) private readonly _docEventManagerService: DocEventManagerService, + @Inject(TextSelectionManagerService) private readonly _textSelectionManagerService: TextSelectionManagerService ) { super(); @@ -49,8 +50,10 @@ export class DocChecklistRenderController extends Disposable implements IRenderM if (!coord) { return; } + const { pageLayoutType = PageLayoutType.VERTICAL, pageMarginLeft, pageMarginTop } = documentComponent.getOffsetConfig(); const skeleton = this._docSkeletonManagerService.getSkeleton(); + const segmentId = this._textSelectionManagerService.getActiveTextRange()?.segmentId; const node = skeleton.findNodeByCoord( coord, pageLayoutType, @@ -60,7 +63,7 @@ export class DocChecklistRenderController extends Disposable implements IRenderM if (!node) { return; } - const paragraph = getParagraphByGlyph(node.node, this._context.unit.getBody()); + const paragraph = getParagraphByGlyph(node.node, this._context.unit.getSelfOrHeaderFooterModel(segmentId).getBody()); if (paragraph && paragraph.bullet && node.node.glyphType === GlyphType.LIST) { if ( paragraph.bullet.listType === PresetListType.CHECK_LIST || @@ -68,6 +71,7 @@ export class DocChecklistRenderController extends Disposable implements IRenderM ) { this._commandService.executeCommand(ToggleCheckListCommand.id, { index: paragraph.startIndex, + segmentId, }); } } diff --git a/packages/docs/src/commands/commands/list.command.ts b/packages/docs/src/commands/commands/list.command.ts index 4c8719e5331..b9e5c1ee31f 100644 --- a/packages/docs/src/commands/commands/list.command.ts +++ b/packages/docs/src/commands/commands/list.command.ts @@ -512,6 +512,7 @@ export const CheckListCommand: ICommand = { export interface IToggleCheckListCommandParams { index: number; + segmentId?: string; } export const ToggleCheckListCommand: ICommand = { @@ -524,14 +525,14 @@ export const ToggleCheckListCommand: ICommand = { } const univerInstanceService = accessor.get(IUniverInstanceService); const commandService = accessor.get(ICommandService); - const { index } = params; + const { index, segmentId } = params; const docDataModel = univerInstanceService.getCurrentUniverDocInstance(); if (docDataModel == null) { return false; } - const paragraphs = docDataModel.getBody()?.paragraphs; + const paragraphs = docDataModel.getSelfOrHeaderFooterModel(segmentId).getBody()?.paragraphs; if (paragraphs == null) { return false; } @@ -547,6 +548,7 @@ export const ToggleCheckListCommand: ICommand = { unitId, actions: [], textRanges: [], + segmentId, }, }; @@ -573,6 +575,7 @@ export const ToggleCheckListCommand: ICommand = { textX.push({ t: TextXActionType.RETAIN, len: startIndex - memoryCursor.cursor, + segmentId, }); textX.push({ @@ -599,11 +602,12 @@ export const ToggleCheckListCommand: ICommand = { ], }, coverType: UpdateDocsAttributeType.REPLACE, + segmentId, }); memoryCursor.moveCursorTo(startIndex + 1); - const path = getRichTextEditPath(docDataModel); + const path = getRichTextEditPath(docDataModel, segmentId); doMutation.params.actions = jsonX.editOp(textX.serialize(), path); const result = commandService.syncExecuteCommand< IRichTextEditingMutationParams, From 159b3d626ea648f4b23721c71aa53a7d588ee8d3 Mon Sep 17 00:00:00 2001 From: zw Date: Sat, 17 Aug 2024 21:01:42 +0800 Subject: [PATCH 09/43] feat: update --- .../src/views/hyper-link-edit/index.tsx | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/packages/docs-hyper-link-ui/src/views/hyper-link-edit/index.tsx b/packages/docs-hyper-link-ui/src/views/hyper-link-edit/index.tsx index 47b55acbc62..d465b9d259d 100644 --- a/packages/docs-hyper-link-ui/src/views/hyper-link-edit/index.tsx +++ b/packages/docs-hyper-link-ui/src/views/hyper-link-edit/index.tsx @@ -22,6 +22,7 @@ import { DocHyperLinkModel } from '@univerjs/docs-hyper-link'; import type { DocumentDataModel } from '@univerjs/core'; import { ITextSelectionRenderManager } from '@univerjs/engine-render'; import { TextSelectionManagerService } from '@univerjs/docs'; +import { KeyCode } from '@univerjs/ui'; import { DocHyperLinkPopupService } from '../../services/hyper-link-popup.service'; import { AddDocHyperLinkCommand } from '../../commands/commands/add-link.command'; import { UpdateDocHyperLinkCommand } from '../../commands/commands/update-link.command'; @@ -122,7 +123,16 @@ export const DocHyperLinkEdit = () => { label={localeService.t('docLink.edit.address')} error={showError && !isLegal ? localeService.t('docLink.edit.addressError') : ''} > - + { + if (evt.keyCode === KeyCode.ENTER) { + handleConfirm(); + } + }} + />
From 20242621ef25e73297a2665a52e312139e5e48e7 Mon Sep 17 00:00:00 2001 From: zw Date: Sat, 17 Aug 2024 21:03:50 +0800 Subject: [PATCH 10/43] feat: update --- .../src/views/thread-comment-editor/index.tsx | 29 ++++++++++++------- 1 file changed, 19 insertions(+), 10 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 2336e97b6c1..b67e6c80a84 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 @@ -22,6 +22,7 @@ import { ICommandService, LocaleService, useDependency } from '@univerjs/core'; import type { IDocumentBody } from '@univerjs/core'; import { ITextSelectionRenderManager } from '@univerjs/engine-render'; import { TextSelectionManagerService } from '@univerjs/docs'; +import { KeyCode } from '@univerjs/ui'; import { IThreadCommentMentionDataService } from '../../services/thread-comment-mention-data.service'; import { SetActiveCommentOperation } from '../../commands/operations/comment.operations'; import styles from './index.module.less'; @@ -75,6 +76,17 @@ export const ThreadCommentEditor = forwardRef { + if (localComment.text) { + onSave?.({ + ...localComment, + text: localComment.text, + }); + setEditing(false); + setLocalComment({ text: undefined }); + } + }; + return (
e.preventDefault()}> { + if (e.keyCode === KeyCode.ENTER) { + handleSave(); + } + }} > res.map(transformMention)).then(callback) as any} displayTransform={(id, label) => `@${label} `} renderSuggestion={mentionDataService.renderSuggestion ?? defaultRenderSuggestion} + /> {editing @@ -125,16 +143,7 @@ export const ThreadCommentEditor = forwardRef { - if (localComment.text) { - onSave?.({ - ...localComment, - text: localComment.text, - }); - setEditing(false); - setLocalComment({ text: undefined }); - } - }} + onClick={handleSave} > {localeService.t(id ? 'threadCommentUI.editor.save' : 'threadCommentUI.editor.reply')} From 42655694b5488d345b07abee50220202970c7b6f Mon Sep 17 00:00:00 2001 From: zw Date: Sat, 17 Aug 2024 21:07:24 +0800 Subject: [PATCH 11/43] feat: update --- .../thread-comment-ui/src/views/thread-comment-editor/index.tsx | 1 + 1 file changed, 1 insertion(+) 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 b67e6c80a84..f08747827ae 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 @@ -84,6 +84,7 @@ export const ThreadCommentEditor = forwardRef Date: Mon, 19 Aug 2024 17:07:06 +0800 Subject: [PATCH 12/43] feat: update --- .../commands/commands/update-link.command.ts | 24 ++++++ .../doc-checklist.render-controller.ts | 47 ++---------- .../src/services/doc-event-manager.service.ts | 73 +++++++++---------- .../components/docs/layout/doc-skeleton.ts | 31 ++++++++ 4 files changed, 96 insertions(+), 79 deletions(-) diff --git a/packages/docs-hyper-link-ui/src/commands/commands/update-link.command.ts b/packages/docs-hyper-link-ui/src/commands/commands/update-link.command.ts index b75ce0c9b52..aa541b8d917 100644 --- a/packages/docs-hyper-link-ui/src/commands/commands/update-link.command.ts +++ b/packages/docs-hyper-link-ui/src/commands/commands/update-link.command.ts @@ -15,12 +15,14 @@ */ import { CommandType, type ICommand, ICommandService } from '@univerjs/core'; +import { replaceSelectionFactory, TextSelectionManagerService } from '@univerjs/docs'; import { UpdateDocHyperLinkMutation } from '@univerjs/docs-hyper-link'; export interface IUpdateDocHyperLinkCommandParams { unitId: string; linkId: string; payload: string; + label: string; } export const UpdateDocHyperLinkCommand: ICommand = { @@ -32,6 +34,28 @@ export const UpdateDocHyperLinkCommand: ICommand { - const { offsetX, offsetY } = evt; - - const documentComponent = this._context.mainComponent as Documents; - const coord = this._getTransformCoordForDocumentOffset( - documentComponent, - this._context.scene.getViewport(VIEWPORT_KEY.VIEW_MAIN)!, - offsetX, - offsetY - ); - if (!coord) { - return; - } - - const { pageLayoutType = PageLayoutType.VERTICAL, pageMarginLeft, pageMarginTop } = documentComponent.getOffsetConfig(); - const skeleton = this._docSkeletonManagerService.getSkeleton(); - const segmentId = this._textSelectionManagerService.getActiveTextRange()?.segmentId; - const node = skeleton.findNodeByCoord( - coord, - pageLayoutType, - pageMarginLeft, - pageMarginTop - ); - if (!node) { - return; - } - const paragraph = getParagraphByGlyph(node.node, this._context.unit.getSelfOrHeaderFooterModel(segmentId).getBody()); - if (paragraph && paragraph.bullet && node.node.glyphType === GlyphType.LIST) { - if ( - paragraph.bullet.listType === PresetListType.CHECK_LIST || - paragraph.bullet.listType === PresetListType.CHECK_LIST_CHECKED - ) { - this._commandService.executeCommand(ToggleCheckListCommand.id, { - index: paragraph.startIndex, - segmentId, - }); - } - } - }) - ); + this._docEventManagerService.clickBullets$.subscribe((paragraph) => { + this._commandService.executeCommand(ToggleCheckListCommand.id, { + index: paragraph.paragraph.startIndex, + segmentId: paragraph.segmentId, + }); + }); } private _initHoverCursor() { diff --git a/packages/docs-ui/src/services/doc-event-manager.service.ts b/packages/docs-ui/src/services/doc-event-manager.service.ts index c11ec1157ba..8bdaacb0b0f 100644 --- a/packages/docs-ui/src/services/doc-event-manager.service.ts +++ b/packages/docs-ui/src/services/doc-event-manager.service.ts @@ -245,46 +245,44 @@ export class DocEventManagerService extends Disposable implements IRenderModule })); paragraphRanges.forEach((paragraph) => { - if (paragraph.bullet && paragraph.bullet.listType === 'CHECK_LIST') { - const node = this._skeleton.findNodeByCharIndex(paragraph.paragraphStart, segmentId); - if (!node) { - return; + if (paragraph.bullet && paragraph.bullet.listType.indexOf('CHECK_LIST') === 0) { + const calcRect = (pageIndex: number) => { + const node = this._skeleton.findNodeByCharIndex(paragraph.paragraphStart, segmentId, pageIndex); + + if (!node) { + return; + } + const bulletNode = node.parent?.glyphGroup[0]; + const offsetConfig = this._documents.getOffsetConfig(); + + if (!bulletNode) { + return; + } + const rect = this._skeleton.getGlyphBounding(bulletNode, offsetConfig); + + bounds.push({ + rect: { + ...rect, + left: segmentId ? rect.left + offsetConfig.pageMarginLeft : rect.left, + right: segmentId ? rect.right + offsetConfig.pageMarginLeft : rect.right, + }, + segmentId, + segmentPageIndex: pageIndex, + paragraph, + }); + }; + + if (segmentId) { + const pageSize = (this._skeleton.getSkeletonData()?.pages.length ?? 0); + for (let i = 0; i < pageSize; i++) { + calcRect(i); + } + } else { + calcRect(-1); } - const bulletNode = node.parent?.glyphGroup[0]; - const { pageMarginLeft, pageMarginTop, docsLeft, docsTop } = this._documents.getOffsetConfig(); - if (!bulletNode) { - return; - } - - const left = node.left + pageMarginLeft + docsLeft; - const right = node.left + node.width + pageMarginLeft; - let top = pageMarginTop + docsTop; - - let p = bulletNode.parent; - while (p) { - //@ts-ignore - top += p.top ?? 0; - // @ts-ignore - p = p.parent; - } - - const height = bulletNode.bBox.aba + bulletNode.bBox.abd; - const bottom = top + height; - - bounds.push({ - rect: { - left, - right, - top, - bottom, - }, - segmentId, - segmentPageIndex: 0, - paragraph, - }); } }); - // console.log('===bounds', bounds); + return bounds; } @@ -316,7 +314,6 @@ export class DocEventManagerService extends Disposable implements IRenderModule } return false; }); - // console.log('===bullet', bullet); return bullet; } } diff --git a/packages/engine-render/src/components/docs/layout/doc-skeleton.ts b/packages/engine-render/src/components/docs/layout/doc-skeleton.ts index 583bc5679f3..7ac8f326d9f 100644 --- a/packages/engine-render/src/components/docs/layout/doc-skeleton.ts +++ b/packages/engine-render/src/components/docs/layout/doc-skeleton.ts @@ -31,6 +31,7 @@ import { Liquid } from '../liquid'; import type { DocumentViewModel } from '../view-model/document-view-model'; import { DocumentEditArea } from '../view-model/document-view-model'; import { getPageFromPath } from '../text-selection/convert-text-range'; +import type { IDocumentOffsetConfig } from '../document'; import type { ILayoutContext } from './tools'; import { getLastPage, getNullSkeleton, prepareSectionBreakConfig, resetContext, setPageParent, updateBlockIndex, updateInlineDrawingCoords } from './tools'; import { createSkeletonSection } from './model/section'; @@ -684,6 +685,36 @@ export class DocumentSkeleton extends Skeleton { return this._getNearestNode(cache.nearestNodeList, cache.nearestNodeDistanceList); } + getGlyphBounding(glyph: IDocumentSkeletonGlyph, offset: IDocumentOffsetConfig) { + const { docsLeft, docsTop } = offset; + const divide = glyph.parent!; + const line = divide?.parent!; + const column = line?.parent!; + const section = column?.parent!; + const page = section?.parent!; + const doc = page?.parent as IDocumentSkeletonCached | undefined; + + const pageIndex = doc?.pages.indexOf(page) ?? 0; + const pageTop = (doc?.pages.slice(0, pageIndex).reduce((acc, cur) => acc + cur.pageHeight + docsTop, 0) ?? 0) + docsTop; + const sectionIndex = page.sections.indexOf(section); + const sectionTop = page.sections.slice(0, sectionIndex).reduce((acc, cur) => acc + cur.height, 0); + const lineTop = line.top; + const top = pageTop + sectionTop + lineTop + page.marginTop; + const bottom = top + line.lineHeight; + + const columnIndex = section.columns.indexOf(column); + const columnLeft = section.columns.slice(0, columnIndex).reduce((acc, cur) => acc + cur.width, 0); + const left = docsLeft + (doc?.left ?? 0) + page.marginLeft + columnLeft + glyph.left + divide.left; + const right = left + glyph.width; + + return { + left, + right, + top, + bottom, + }; + } + private _collectNearestNode( segmentPage: IDocumentSkeletonPage, pageType: DocumentSkeletonPageType, From de51171a9a826f8987aebad41c44b336aab420e3 Mon Sep 17 00:00:00 2001 From: zw Date: Mon, 19 Aug 2024 18:06:40 +0800 Subject: [PATCH 13/43] fix: code review --- .../src/commands/operations/popup.operation.ts | 6 +++--- .../docs-ui/src/services/doc-event-manager.service.ts | 9 ++++----- .../docs-ui/src/services/doc-popup-manager.service.ts | 1 - 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/packages/docs-hyper-link-ui/src/commands/operations/popup.operation.ts b/packages/docs-hyper-link-ui/src/commands/operations/popup.operation.ts index b7c495daa2a..d08c7e6123a 100644 --- a/packages/docs-hyper-link-ui/src/commands/operations/popup.operation.ts +++ b/packages/docs-hyper-link-ui/src/commands/operations/popup.operation.ts @@ -71,7 +71,7 @@ export interface IShowDocHyperLinkEditPopupOperationParams { export const ShowDocHyperLinkEditPopupOperation: ICommand = { type: CommandType.OPERATION, - id: 'docs.operation.show-hyper-link-edit-popup', + id: 'doc.operation.show-hyper-link-edit-popup', handler(accessor, params) { const linkInfo = params?.link; if (shouldDisableAddLink(accessor) && !linkInfo) { @@ -93,7 +93,7 @@ export interface IShowDocHyperLinkInfoPopupOperationParams { export const ToggleDocHyperLinkInfoPopupOperation: ICommand = { type: CommandType.OPERATION, - id: 'docs.operation.show-hyper-link-info-popup', + id: 'doc.operation.show-hyper-link-info-popup', handler(accessor, params) { const hyperLinkService = accessor.get(DocHyperLinkPopupService); if (!params) { @@ -108,7 +108,7 @@ export const ToggleDocHyperLinkInfoPopupOperation: ICommand = { type: CommandType.OPERATION, - id: 'docs.operation.click-hyper-link', + id: 'doc.operation.click-hyper-link', handler(accessor, params) { if (!params) { return false; diff --git a/packages/docs-ui/src/services/doc-event-manager.service.ts b/packages/docs-ui/src/services/doc-event-manager.service.ts index 8bdaacb0b0f..8a6611ef80a 100644 --- a/packages/docs-ui/src/services/doc-event-manager.service.ts +++ b/packages/docs-ui/src/services/doc-event-manager.service.ts @@ -39,7 +39,6 @@ interface IBulletBound { const calcDocRangePositions = (range: ITextRangeParam, documents: Documents, skeleton: DocumentSkeleton, pageIndex: number): IBoundRectNoAngle[] | undefined => { const startPosition = skeleton.findNodePositionByCharIndex(range.startOffset, true, range.segmentId, pageIndex); const endPosition = skeleton.findNodePositionByCharIndex(range.endOffset, true, range.segmentId, pageIndex); - // console.log('===startPosition', range, startPosition, endPosition); if (!endPosition || !startPosition) { return; } @@ -61,16 +60,16 @@ interface IBulletActive { } export class DocEventManagerService extends Disposable implements IRenderModule { - private _hoverCustomRanges$ = new Subject(); + private readonly _hoverCustomRanges$ = new Subject(); readonly hoverCustomRanges$ = this._hoverCustomRanges$.pipe(distinctUntilChanged((pre, aft) => pre.length === aft.length && pre.every((item, i) => aft[i].range.rangeId === item.range.rangeId && aft[i].segmentId === item.segmentId && aft[i].segmentPageIndex === item.segmentPageIndex))); - private _clickCustomRanges$ = new Subject(); + private readonly _clickCustomRanges$ = new Subject(); readonly clickCustomRanges$ = this._clickCustomRanges$.asObservable(); - private _hoverBullet$ = new Subject>(); + private readonly _hoverBullet$ = new Subject>(); readonly hoverBullet$ = this._hoverBullet$.pipe(distinctUntilChanged((pre, aft) => pre?.paragraph.startIndex === aft?.paragraph.startIndex && pre?.segmentId === aft?.segmentId && pre?.segmentPageIndex === aft?.segmentPageIndex)); - private _clickBullet$ = new Subject(); + private readonly _clickBullet$ = new Subject(); readonly clickBullets$ = this._clickBullet$.asObservable(); private _customRangeDirty = true; diff --git a/packages/docs-ui/src/services/doc-popup-manager.service.ts b/packages/docs-ui/src/services/doc-popup-manager.service.ts index 3c37341fa6b..cb868aa0908 100644 --- a/packages/docs-ui/src/services/doc-popup-manager.service.ts +++ b/packages/docs-ui/src/services/doc-popup-manager.service.ts @@ -57,7 +57,6 @@ export function transformPosition2Offset(x: number, y: number, scene: Scene) { }; } -// write a revert function for transformPosition2Offset export function transformOffset2Bound(offsetX: number, offsetY: number, scene: Scene) { const { scaleX, scaleY } = scene.getAncestorScale(); const viewMain = scene.getViewport(VIEWPORT_KEY.VIEW_MAIN); From 2770b93d7e13349d405ab1d09096d0a79e363004 Mon Sep 17 00:00:00 2001 From: zw Date: Mon, 19 Aug 2024 18:08:52 +0800 Subject: [PATCH 14/43] fix: code review --- .../hyper-link-event.render-controller.ts | 35 ++++++++----------- 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/packages/docs-hyper-link-ui/src/controllers/render-controllers/hyper-link-event.render-controller.ts b/packages/docs-hyper-link-ui/src/controllers/render-controllers/hyper-link-event.render-controller.ts index 175e570eb9d..21feb8ebe67 100644 --- a/packages/docs-hyper-link-ui/src/controllers/render-controllers/hyper-link-event.render-controller.ts +++ b/packages/docs-hyper-link-ui/src/controllers/render-controllers/hyper-link-event.render-controller.ts @@ -39,29 +39,24 @@ export class DocHyperLinkEventRenderController extends Disposable implements IRe this._docEventManagerService.hoverCustomRanges$.subscribe((ranges) => { const link = ranges.find((range) => range.range.rangeType === CustomRangeType.HYPERLINK); if (link) { - // const linkIndex - const customRanges = this._context.unit.getSelfOrHeaderFooterModel(link.segmentId).getBody()?.customRanges; - if (customRanges) { - const linkIndex = customRanges.findIndex((range) => range.rangeId === link.range.rangeId); - if (linkIndex !== -1) { - this._commandService.executeCommand( - ToggleDocHyperLinkInfoPopupOperation.id, - { - unitId: this._context.unitId, - linkId: link.range.rangeId, - segmentId: link.segmentId, - rangeIndex: linkIndex, - segmentPage: link.segmentPageIndex, - } - ); - } - } - } else { - if (this._hyperLinkPopupService.showing) { + const customRanges = this._context.unit.getSelfOrHeaderFooterModel(link.segmentId).getBody()?.customRanges ?? []; + const linkIndex = customRanges.findIndex((range) => range.rangeId === link.range.rangeId); + if (linkIndex !== -1) { this._commandService.executeCommand( - ToggleDocHyperLinkInfoPopupOperation.id + ToggleDocHyperLinkInfoPopupOperation.id, + { + unitId: this._context.unitId, + linkId: link.range.rangeId, + segmentId: link.segmentId, + rangeIndex: linkIndex, + segmentPage: link.segmentPageIndex, + } ); } + } else if (this._hyperLinkPopupService.showing) { + this._commandService.executeCommand( + ToggleDocHyperLinkInfoPopupOperation.id + ); } }) ); From 929ca3f20a29d6baefad88d7ebefbf038cbfc955 Mon Sep 17 00:00:00 2001 From: zw Date: Mon, 19 Aug 2024 18:12:24 +0800 Subject: [PATCH 15/43] feat: update --- .../src/controllers/doc-formula-input.controller.ts | 2 +- .../render-controllers/doc-checklist.render-controller.ts | 6 +++--- packages/docs-ui/src/services/doc-event-manager.service.ts | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages-experimental/uni-formula-ui/src/controllers/doc-formula-input.controller.ts b/packages-experimental/uni-formula-ui/src/controllers/doc-formula-input.controller.ts index cade1acf530..456fb6a434f 100644 --- a/packages-experimental/uni-formula-ui/src/controllers/doc-formula-input.controller.ts +++ b/packages-experimental/uni-formula-ui/src/controllers/doc-formula-input.controller.ts @@ -43,7 +43,7 @@ export class DocUniFormulaInputController extends Disposable { this._initKeyboardListeners(); this._initCommands(); - // this._initHoverListener(); + this._initHoverListener(); } private _initCommands(): void { diff --git a/packages/docs-ui/src/controllers/render-controllers/doc-checklist.render-controller.ts b/packages/docs-ui/src/controllers/render-controllers/doc-checklist.render-controller.ts index 78f667db8fd..0a1f29d0245 100644 --- a/packages/docs-ui/src/controllers/render-controllers/doc-checklist.render-controller.ts +++ b/packages/docs-ui/src/controllers/render-controllers/doc-checklist.render-controller.ts @@ -15,10 +15,10 @@ */ import type { Documents, IRenderContext, IRenderModule, Viewport } from '@univerjs/engine-render'; -import { CURSOR_TYPE, getParagraphByGlyph, GlyphType, PageLayoutType, Vector2 } from '@univerjs/engine-render'; +import { CURSOR_TYPE, Vector2 } from '@univerjs/engine-render'; import type { DocumentDataModel } from '@univerjs/core'; -import { Disposable, ICommandService, Inject, PresetListType } from '@univerjs/core'; -import { DocSkeletonManagerService, TextSelectionManagerService, ToggleCheckListCommand, VIEWPORT_KEY } from '@univerjs/docs'; +import { Disposable, ICommandService, Inject } from '@univerjs/core'; +import { DocSkeletonManagerService, TextSelectionManagerService, ToggleCheckListCommand } from '@univerjs/docs'; import { DocEventManagerService } from '../../services/doc-event-manager.service'; export class DocChecklistRenderController extends Disposable implements IRenderModule { diff --git a/packages/docs-ui/src/services/doc-event-manager.service.ts b/packages/docs-ui/src/services/doc-event-manager.service.ts index 8a6611ef80a..16533bdc7dc 100644 --- a/packages/docs-ui/src/services/doc-event-manager.service.ts +++ b/packages/docs-ui/src/services/doc-event-manager.service.ts @@ -17,8 +17,8 @@ import type { DocumentDataModel, ICustomRange, IParagraph, ITextRangeParam, Nullable } from '@univerjs/core'; import { Disposable, fromEventSubject, Inject } from '@univerjs/core'; import { DocSkeletonManagerService } from '@univerjs/docs'; -import type { Documents, DocumentSkeleton, IBoundRectNoAngle, IMouseEvent, IPointerEvent, IRender, IRenderContext, IRenderModule } from '@univerjs/engine-render'; -import { getLineBounding, NodePositionConvertToCursor, pxToNum, TRANSFORM_CHANGE_OBSERVABLE_TYPE } from '@univerjs/engine-render'; +import type { Documents, DocumentSkeleton, IBoundRectNoAngle, IMouseEvent, IPointerEvent, IRenderContext, IRenderModule } from '@univerjs/engine-render'; +import { getLineBounding, NodePositionConvertToCursor, TRANSFORM_CHANGE_OBSERVABLE_TYPE } from '@univerjs/engine-render'; import { distinctUntilChanged, filter, Subject, throttleTime } from 'rxjs'; import { transformOffset2Bound } from './doc-popup-manager.service'; From ce5f50f7f054d49c2ffe969252a69f1003e61720 Mon Sep 17 00:00:00 2001 From: zw Date: Mon, 19 Aug 2024 20:14:29 +0800 Subject: [PATCH 16/43] feat: update --- .../src/services/doc-event-manager.service.ts | 51 ++++++++++++------- .../components/docs/layout/doc-skeleton.ts | 30 ----------- 2 files changed, 34 insertions(+), 47 deletions(-) diff --git a/packages/docs-ui/src/services/doc-event-manager.service.ts b/packages/docs-ui/src/services/doc-event-manager.service.ts index 16533bdc7dc..5252d038240 100644 --- a/packages/docs-ui/src/services/doc-event-manager.service.ts +++ b/packages/docs-ui/src/services/doc-event-manager.service.ts @@ -17,7 +17,7 @@ import type { DocumentDataModel, ICustomRange, IParagraph, ITextRangeParam, Nullable } from '@univerjs/core'; import { Disposable, fromEventSubject, Inject } from '@univerjs/core'; import { DocSkeletonManagerService } from '@univerjs/docs'; -import type { Documents, DocumentSkeleton, IBoundRectNoAngle, IMouseEvent, IPointerEvent, IRenderContext, IRenderModule } from '@univerjs/engine-render'; +import type { Documents, DocumentSkeleton, IBoundRectNoAngle, IDocumentSkeletonGlyph, IMouseEvent, IPointerEvent, IRenderContext, IRenderModule } from '@univerjs/engine-render'; import { getLineBounding, NodePositionConvertToCursor, TRANSFORM_CHANGE_OBSERVABLE_TYPE } from '@univerjs/engine-render'; import { distinctUntilChanged, filter, Subject, throttleTime } from 'rxjs'; import { transformOffset2Bound } from './doc-popup-manager.service'; @@ -48,7 +48,32 @@ const calcDocRangePositions = (range: ITextRangeParam, documents: Documents, ske const { borderBoxPointGroup } = convertor.getRangePointData(startPosition, endPosition); const bounds = getLineBounding(borderBoxPointGroup); - return bounds; + return bounds.map((rect) => ({ + top: rect.top + documentOffsetConfig.docsTop, + bottom: rect.bottom + documentOffsetConfig.docsTop, + left: rect.left + documentOffsetConfig.docsLeft, + right: rect.right + documentOffsetConfig.docsLeft, + })); +}; +export const calcDocGlyphPosition = (glyph: IDocumentSkeletonGlyph, documents: Documents, skeleton: DocumentSkeleton, pageIndex = -1): IBoundRectNoAngle | undefined => { + const start = skeleton.findPositionByGlyph(glyph, pageIndex); + if (!start) { + return; + } + + const documentOffsetConfig = documents.getOffsetConfig(); + const startPosition = { ...start, isBack: true }; + const convertor = new NodePositionConvertToCursor(documentOffsetConfig, skeleton); + const { borderBoxPointGroup } = convertor.getRangePointData(startPosition, startPosition); + const bounds = getLineBounding(borderBoxPointGroup); + const rect = bounds[0]; + + return { + top: rect.top + documentOffsetConfig.docsTop, + bottom: rect.bottom + documentOffsetConfig.docsTop, + left: rect.left + documentOffsetConfig.docsLeft, + right: rect.left + documentOffsetConfig.docsLeft + glyph.width, + }; }; interface ICustomRangeActive { @@ -166,16 +191,10 @@ export class DocEventManagerService extends Disposable implements IRenderModule if (!rects) { return null; } - const documentOffsetConfig = this._documents.getOffsetConfig(); layouts.push({ customRange: range, - rects: rects.map((rect) => ({ - top: rect.top + documentOffsetConfig.docsTop, - bottom: rect.bottom + documentOffsetConfig.docsTop, - left: rect.left + documentOffsetConfig.docsLeft, - right: rect.right + documentOffsetConfig.docsLeft, - })), + rects, segmentId, segmentPageIndex: pageIndex, }); @@ -252,19 +271,18 @@ export class DocEventManagerService extends Disposable implements IRenderModule return; } const bulletNode = node.parent?.glyphGroup[0]; - const offsetConfig = this._documents.getOffsetConfig(); if (!bulletNode) { return; } - const rect = this._skeleton.getGlyphBounding(bulletNode, offsetConfig); + const rect = calcDocGlyphPosition(bulletNode, this._documents, this._skeleton, pageIndex); + + if (!rect) { + return; + } bounds.push({ - rect: { - ...rect, - left: segmentId ? rect.left + offsetConfig.pageMarginLeft : rect.left, - right: segmentId ? rect.right + offsetConfig.pageMarginLeft : rect.right, - }, + rect, segmentId, segmentPageIndex: pageIndex, paragraph, @@ -293,7 +311,6 @@ export class DocEventManagerService extends Disposable implements IRenderModule const headerKeys = Array.from(this._context.unit.headerModelMap.keys()); const footerKeys = Array.from(this._context.unit.footerModelMap.keys()); - this._bulletBounds = [ ...this._buildBulletBoundsBySegment(), ...(headerKeys.map((key) => this._buildBulletBoundsBySegment(key)).flat()), diff --git a/packages/engine-render/src/components/docs/layout/doc-skeleton.ts b/packages/engine-render/src/components/docs/layout/doc-skeleton.ts index 7ac8f326d9f..5cccae3e6b3 100644 --- a/packages/engine-render/src/components/docs/layout/doc-skeleton.ts +++ b/packages/engine-render/src/components/docs/layout/doc-skeleton.ts @@ -685,36 +685,6 @@ export class DocumentSkeleton extends Skeleton { return this._getNearestNode(cache.nearestNodeList, cache.nearestNodeDistanceList); } - getGlyphBounding(glyph: IDocumentSkeletonGlyph, offset: IDocumentOffsetConfig) { - const { docsLeft, docsTop } = offset; - const divide = glyph.parent!; - const line = divide?.parent!; - const column = line?.parent!; - const section = column?.parent!; - const page = section?.parent!; - const doc = page?.parent as IDocumentSkeletonCached | undefined; - - const pageIndex = doc?.pages.indexOf(page) ?? 0; - const pageTop = (doc?.pages.slice(0, pageIndex).reduce((acc, cur) => acc + cur.pageHeight + docsTop, 0) ?? 0) + docsTop; - const sectionIndex = page.sections.indexOf(section); - const sectionTop = page.sections.slice(0, sectionIndex).reduce((acc, cur) => acc + cur.height, 0); - const lineTop = line.top; - const top = pageTop + sectionTop + lineTop + page.marginTop; - const bottom = top + line.lineHeight; - - const columnIndex = section.columns.indexOf(column); - const columnLeft = section.columns.slice(0, columnIndex).reduce((acc, cur) => acc + cur.width, 0); - const left = docsLeft + (doc?.left ?? 0) + page.marginLeft + columnLeft + glyph.left + divide.left; - const right = left + glyph.width; - - return { - left, - right, - top, - bottom, - }; - } - private _collectNearestNode( segmentPage: IDocumentSkeletonPage, pageType: DocumentSkeletonPageType, From 4128807d8c4198ff2824cfa8acfe61fc640ef0d9 Mon Sep 17 00:00:00 2001 From: zw Date: Mon, 19 Aug 2024 21:18:42 +0800 Subject: [PATCH 17/43] feat: update --- .../commands/commands/update-link.command.ts | 38 +++++++++++---- .../commands/operations/popup.operation.ts | 4 +- .../doc-hyper-link-selection.controller.ts | 2 +- .../hyper-link-event.render-controller.ts | 24 ++++------ .../src/services/hyper-link-popup.service.ts | 14 +++--- .../src/views/hyper-link-edit/index.tsx | 46 +++++++++++++++---- packages/docs/src/basics/plain-text.ts | 18 ++++++-- .../mutations/core-editing.mutation.ts | 3 +- packages/docs/src/index.ts | 2 +- 9 files changed, 102 insertions(+), 49 deletions(-) diff --git a/packages/docs-hyper-link-ui/src/commands/commands/update-link.command.ts b/packages/docs-hyper-link-ui/src/commands/commands/update-link.command.ts index aa541b8d917..b7c49d515f9 100644 --- a/packages/docs-hyper-link-ui/src/commands/commands/update-link.command.ts +++ b/packages/docs-hyper-link-ui/src/commands/commands/update-link.command.ts @@ -14,9 +14,10 @@ * limitations under the License. */ -import { CommandType, type ICommand, ICommandService } from '@univerjs/core'; +import { CommandType, CustomRangeType, DataStreamTreeTokenType, generateRandomId, type ICommand, ICommandService, sequenceExecute } from '@univerjs/core'; import { replaceSelectionFactory, TextSelectionManagerService } from '@univerjs/docs'; -import { UpdateDocHyperLinkMutation } from '@univerjs/docs-hyper-link'; +import { AddDocHyperLinkMutation, UpdateDocHyperLinkMutation } from '@univerjs/docs-hyper-link'; +import type { IAddDocHyperLinkMutationParams } from '../../../../docs-hyper-link/lib/types'; export interface IUpdateDocHyperLinkCommandParams { unitId: string; @@ -32,30 +33,49 @@ export const UpdateDocHyperLinkCommand: ICommand = { type: CommandType.OPERATION, - id: 'doc.operation.show-hyper-link-info-popup', + id: 'doc.operation.toggle-hyper-link-info-popup', handler(accessor, params) { const hyperLinkService = accessor.get(DocHyperLinkPopupService); if (!params) { diff --git a/packages/docs-hyper-link-ui/src/controllers/doc-hyper-link-selection.controller.ts b/packages/docs-hyper-link-ui/src/controllers/doc-hyper-link-selection.controller.ts index 680ea1999a1..41ba90c90c1 100644 --- a/packages/docs-hyper-link-ui/src/controllers/doc-hyper-link-selection.controller.ts +++ b/packages/docs-hyper-link-ui/src/controllers/doc-hyper-link-selection.controller.ts @@ -58,7 +58,7 @@ export class DocHyperLinkSelectionController extends Disposable { const index = customRanges?.findIndex((value) => (value.startIndex) < startOffset && value.endIndex > endOffset - 1) ?? -1; if (index > -1) { const customRange = customRanges![index]; - this._docHyperLinkService.showInfoPopup({ unitId, linkId: customRange.rangeId, rangeIndex: index }); + this._docHyperLinkService.showInfoPopup({ unitId, linkId: customRange.rangeId }); return; } } else { // range diff --git a/packages/docs-hyper-link-ui/src/controllers/render-controllers/hyper-link-event.render-controller.ts b/packages/docs-hyper-link-ui/src/controllers/render-controllers/hyper-link-event.render-controller.ts index 21feb8ebe67..a7cb3c1a4c4 100644 --- a/packages/docs-hyper-link-ui/src/controllers/render-controllers/hyper-link-event.render-controller.ts +++ b/packages/docs-hyper-link-ui/src/controllers/render-controllers/hyper-link-event.render-controller.ts @@ -39,20 +39,16 @@ export class DocHyperLinkEventRenderController extends Disposable implements IRe this._docEventManagerService.hoverCustomRanges$.subscribe((ranges) => { const link = ranges.find((range) => range.range.rangeType === CustomRangeType.HYPERLINK); if (link) { - const customRanges = this._context.unit.getSelfOrHeaderFooterModel(link.segmentId).getBody()?.customRanges ?? []; - const linkIndex = customRanges.findIndex((range) => range.rangeId === link.range.rangeId); - if (linkIndex !== -1) { - this._commandService.executeCommand( - ToggleDocHyperLinkInfoPopupOperation.id, - { - unitId: this._context.unitId, - linkId: link.range.rangeId, - segmentId: link.segmentId, - rangeIndex: linkIndex, - segmentPage: link.segmentPageIndex, - } - ); - } + this._commandService.executeCommand( + ToggleDocHyperLinkInfoPopupOperation.id, + { + unitId: this._context.unitId, + linkId: link.range.rangeId, + segmentId: link.segmentId, + segmentPage: link.segmentPageIndex, + rangeId: link.range.rangeId, + } + ); } else if (this._hyperLinkPopupService.showing) { this._commandService.executeCommand( ToggleDocHyperLinkInfoPopupOperation.id diff --git a/packages/docs-hyper-link-ui/src/services/hyper-link-popup.service.ts b/packages/docs-hyper-link-ui/src/services/hyper-link-popup.service.ts index ffd5b1a9467..c1c7a1d0f28 100644 --- a/packages/docs-hyper-link-ui/src/services/hyper-link-popup.service.ts +++ b/packages/docs-hyper-link-ui/src/services/hyper-link-popup.service.ts @@ -31,7 +31,6 @@ const SKIT_PLACEHOLDER = 2; export interface ILinkInfo { unitId: string; linkId: string; - rangeIndex: number; segmentId?: string; segmentPage?: number; } @@ -75,9 +74,9 @@ export class DocHyperLinkPopupService extends Disposable { this._editingLink$.next(linkInfo); let activeRange: Nullable = this._textSelectionManagerService.getActiveTextRangeWithStyle(); if (linkInfo) { - const { unitId, rangeIndex } = linkInfo; + const { unitId, linkId } = linkInfo; const doc = this._univerInstanceService.getUnit(unitId, UniverInstanceType.UNIVER_DOC); - const range = doc?.getBody()?.customRanges?.[rangeIndex]; + const range = doc?.getBody()?.customRanges?.find((i) => i.rangeId === linkId); if (range) { activeRange = { collapsed: false, @@ -112,11 +111,10 @@ export class DocHyperLinkPopupService extends Disposable { } showInfoPopup(info: ILinkInfo): Nullable { - const { linkId, unitId, rangeIndex } = info; + const { linkId, unitId } = info; if ( this.showing?.linkId === linkId && - this.showing?.unitId === unitId && - this.showing.rangeIndex === rangeIndex + this.showing?.unitId === unitId ) { return; } @@ -129,8 +127,8 @@ export class DocHyperLinkPopupService extends Disposable { if (!doc || !link) { return; } - const range = doc.getSelfOrHeaderFooterModel(info.segmentId).getBody()?.customRanges?.[rangeIndex]; - this._showingLink$.next({ unitId, linkId, rangeIndex }); + const range = doc.getSelfOrHeaderFooterModel(info.segmentId).getBody()?.customRanges?.find((i) => i.rangeId === linkId); + this._showingLink$.next({ unitId, linkId }); if (!range) { return; } diff --git a/packages/docs-hyper-link-ui/src/views/hyper-link-edit/index.tsx b/packages/docs-hyper-link-ui/src/views/hyper-link-edit/index.tsx index d465b9d259d..8c627536389 100644 --- a/packages/docs-hyper-link-ui/src/views/hyper-link-edit/index.tsx +++ b/packages/docs-hyper-link-ui/src/views/hyper-link-edit/index.tsx @@ -17,11 +17,11 @@ import { Button, FormLayout, Input } from '@univerjs/design'; import React, { useEffect, useState } from 'react'; import { CloseSingle } from '@univerjs/icons'; -import { ICommandService, IUniverInstanceService, LocaleService, Tools, UniverInstanceType, useDependency, useObservable } from '@univerjs/core'; +import { getBodySlice, ICommandService, IUniverInstanceService, LocaleService, Tools, UniverInstanceType, useDependency, useObservable } from '@univerjs/core'; import { DocHyperLinkModel } from '@univerjs/docs-hyper-link'; import type { DocumentDataModel } from '@univerjs/core'; import { ITextSelectionRenderManager } from '@univerjs/engine-render'; -import { TextSelectionManagerService } from '@univerjs/docs'; +import { getPlainTextFormBody, TextSelectionManagerService } from '@univerjs/docs'; import { KeyCode } from '@univerjs/ui'; import { DocHyperLinkPopupService } from '../../services/hyper-link-popup.service'; import { AddDocHyperLinkCommand } from '../../commands/commands/add-link.command'; @@ -52,6 +52,7 @@ export const DocHyperLinkEdit = () => { const textSelectionRenderManager = useDependency(ITextSelectionRenderManager); const textSelectionManagerService = useDependency(TextSelectionManagerService); const [link, setLink] = useState(''); + const [label, setLabel] = useState(''); const [showError, setShowError] = useState(false); const isLegal = Tools.isLegalUrl(link); const doc = editingId @@ -59,22 +60,27 @@ export const DocHyperLinkEdit = () => { univerInstanceService.getCurrentUnitForType(UniverInstanceType.UNIVER_DOC); useEffect(() => { + const activeRange = textSelectionManagerService.getActiveTextRangeWithStyle(); + if (!activeRange) { + return; + } + if (editingId) { const linkDetail = editingId ? hyperLinkModel.getLink(editingId.unitId, editingId.linkId) : null; setLink(linkDetail?.payload ?? ''); + const matchedRange = doc?.getBody()?.customRanges?.find((i) => linkDetail?.id === i.rangeId); + if (doc && matchedRange) { + setLabel(getPlainTextFormBody(getBodySlice(doc.getBody()!, matchedRange.startIndex, matchedRange.endIndex))); + } return; } - const activeRange = textSelectionManagerService.getActiveTextRangeWithStyle(); - if (!activeRange) { - return; - } - const doc = univerInstanceService.getCurrentUnitForType(UniverInstanceType.UNIVER_DOC); + const matchedRange = doc?.getBody()?.customRanges?.find((i) => Math.max(activeRange.startOffset, i.startIndex) <= Math.min(activeRange.endOffset - 1, i.endIndex)); if (doc && matchedRange) { const linkDetail = hyperLinkModel.getLink(doc.getUnitId(), matchedRange.rangeId); setLink(linkDetail?.payload ?? ''); } - }, [editingId, hyperLinkModel, textSelectionManagerService, univerInstanceService]); + }, [doc, editingId, hyperLinkModel, textSelectionManagerService, univerInstanceService]); useEffect(() => { textSelectionRenderManager.blurEditor(); @@ -99,10 +105,15 @@ export const DocHyperLinkEdit = () => { payload: linkFinal, }); } else { + if (!label) { + return; + } + commandService.executeCommand(UpdateDocHyperLinkCommand.id, { unitId: doc.getUnitId(), payload: linkFinal, linkId: editingId.linkId, + label, }); } hyperLinkService.hideEditPopup(); @@ -119,6 +130,25 @@ export const DocHyperLinkEdit = () => {
+ {editingId + ? ( + + { + if (evt.keyCode === KeyCode.ENTER) { + handleConfirm(); + } + }} + /> + + ) + : null} { + let str = body.dataStream; + if (body.dataStream.endsWith('\r\n')) { + str = body.dataStream.slice(0, -2); + } + + return str.replaceAll(DataStreamTreeTokenType.CUSTOM_RANGE_START, '').replaceAll(DataStreamTreeTokenType.CUSTOM_RANGE_END, ''); +}; /** * get plain text from rich-text @@ -23,6 +36,5 @@ export const getPlainTextFormDocument = (data: IDocumentData) => { if (!data.body) { return ''; } - const str = data.body.dataStream.slice(0, -2); - return str.replaceAll(DataStreamTreeTokenType.CUSTOM_RANGE_START, '').replaceAll(DataStreamTreeTokenType.CUSTOM_RANGE_END, ''); + return getPlainTextFormBody(data.body); }; diff --git a/packages/docs/src/commands/mutations/core-editing.mutation.ts b/packages/docs/src/commands/mutations/core-editing.mutation.ts index 80a7f3bbc9f..56f042fb1dc 100644 --- a/packages/docs/src/commands/mutations/core-editing.mutation.ts +++ b/packages/docs/src/commands/mutations/core-editing.mutation.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { CommandType, IUniverInstanceService, JSONX } from '@univerjs/core'; +import { CommandType, IUniverInstanceService, JSONX, Tools } from '@univerjs/core'; import type { IMutation, IMutationCommonParams, JSONXActions, Nullable } from '@univerjs/core'; import { IRenderManagerService, type ITextRangeWithStyle } from '@univerjs/engine-render'; import { serializeDocRange, TextSelectionManagerService } from '../../services/text-selection-manager.service'; @@ -96,7 +96,6 @@ export const RichTextEditingMutation: IMutation Date: Mon, 19 Aug 2024 21:25:22 +0800 Subject: [PATCH 18/43] feat: update --- packages/docs-ui/src/services/doc-event-manager.service.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/docs-ui/src/services/doc-event-manager.service.ts b/packages/docs-ui/src/services/doc-event-manager.service.ts index 5252d038240..d058f49715c 100644 --- a/packages/docs-ui/src/services/doc-event-manager.service.ts +++ b/packages/docs-ui/src/services/doc-event-manager.service.ts @@ -126,7 +126,7 @@ export class DocEventManagerService extends Disposable implements IRenderModule super(); this._initResetDirty(); - this._initCustomRanges(); + this._initEvents(); } override dispose() { @@ -151,7 +151,7 @@ export class DocEventManagerService extends Disposable implements IRenderModule ); } - private _initCustomRanges() { + private _initEvents() { this.disposeWithMe(fromEventSubject(this._context.scene.onPointerMove$).pipe(throttleTime(16)).subscribe((evt) => { this._hoverCustomRanges$.next( this._calcActiveRanges(evt) @@ -161,7 +161,7 @@ export class DocEventManagerService extends Disposable implements IRenderModule ); })); - this.disposeWithMe(this._context.scene.onPointerUp$.subscribeEvent((evt) => { + this.disposeWithMe(this._context.mainComponent!.onPointerDown$.subscribeEvent((evt) => { const ranges = this._calcActiveRanges(evt); if (ranges.length) { this._clickCustomRanges$.next(ranges.pop()!); From 04217af498c69a69b468870d53f2f821c0d10643 Mon Sep 17 00:00:00 2001 From: zw Date: Mon, 19 Aug 2024 21:38:33 +0800 Subject: [PATCH 19/43] feat: update --- packages/docs/src/basics/selection.ts | 4 +++- packages/docs/src/commands/commands/ime-input.command.ts | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/docs/src/basics/selection.ts b/packages/docs/src/basics/selection.ts index 818f9bcd725..71081f1ef49 100644 --- a/packages/docs/src/basics/selection.ts +++ b/packages/docs/src/basics/selection.ts @@ -123,7 +123,7 @@ export function getDeleteSelection(selection: T, body: IDo }; } -export function getInsertSelection(selection: ITextRange, body: IDocumentBody): ITextRange { +export function getInsertSelection(selection: T, body: IDocumentBody): T { let { startOffset, endOffset, collapsed } = normalizeSelection(selection); if (collapsed) { @@ -138,12 +138,14 @@ export function getInsertSelection(selection: ITextRange, body: IDocumentBody): } return { + ...selection, startOffset, endOffset, collapsed, }; } else { return { + ...selection, ...getSelectionWithSymbolMax(selection, body), collapsed: false, }; diff --git a/packages/docs/src/commands/commands/ime-input.command.ts b/packages/docs/src/commands/commands/ime-input.command.ts index 7316e502dbd..c1a0f5cf164 100644 --- a/packages/docs/src/commands/commands/ime-input.command.ts +++ b/packages/docs/src/commands/commands/ime-input.command.ts @@ -54,7 +54,7 @@ export const IMEInputCommand: ICommand = { if (!previousActiveRange) { return false; } - const { startOffset, style, segmentId } = previousActiveRange; + const { style, segmentId } = previousActiveRange; const body = docDataModel.getSelfOrHeaderFooterModel(segmentId).getBody(); if (body == null) { @@ -63,6 +63,7 @@ export const IMEInputCommand: ICommand = { const insertRange = getInsertSelection(previousActiveRange, body); Object.assign(previousActiveRange, insertRange); + const { startOffset } = previousActiveRange; const len = newText.length; From 3c0fc2a61f30228da8ff4ba9b943384399f3fee0 Mon Sep 17 00:00:00 2001 From: zw Date: Mon, 19 Aug 2024 21:40:52 +0800 Subject: [PATCH 20/43] feat: update --- .../src/commands/commands/update-link.command.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/docs-hyper-link-ui/src/commands/commands/update-link.command.ts b/packages/docs-hyper-link-ui/src/commands/commands/update-link.command.ts index b7c49d515f9..ee219815518 100644 --- a/packages/docs-hyper-link-ui/src/commands/commands/update-link.command.ts +++ b/packages/docs-hyper-link-ui/src/commands/commands/update-link.command.ts @@ -63,6 +63,9 @@ export const UpdateDocHyperLinkCommand: ICommand Date: Mon, 19 Aug 2024 21:43:03 +0800 Subject: [PATCH 21/43] feat: update --- .../src/commands/commands/update-link.command.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/docs-hyper-link-ui/src/commands/commands/update-link.command.ts b/packages/docs-hyper-link-ui/src/commands/commands/update-link.command.ts index ee219815518..d5b648542d3 100644 --- a/packages/docs-hyper-link-ui/src/commands/commands/update-link.command.ts +++ b/packages/docs-hyper-link-ui/src/commands/commands/update-link.command.ts @@ -16,7 +16,7 @@ import { CommandType, CustomRangeType, DataStreamTreeTokenType, generateRandomId, type ICommand, ICommandService, sequenceExecute } from '@univerjs/core'; import { replaceSelectionFactory, TextSelectionManagerService } from '@univerjs/docs'; -import { AddDocHyperLinkMutation, UpdateDocHyperLinkMutation } from '@univerjs/docs-hyper-link'; +import { AddDocHyperLinkMutation } from '@univerjs/docs-hyper-link'; import type { IAddDocHyperLinkMutationParams } from '../../../../docs-hyper-link/lib/types'; export interface IUpdateDocHyperLinkCommandParams { From 8624c37951526f3d15221adfa1a677b93344d184 Mon Sep 17 00:00:00 2001 From: zw Date: Mon, 19 Aug 2024 21:50:47 +0800 Subject: [PATCH 22/43] feat: update --- .../src/commands/operations/popup.operation.ts | 11 +++++++---- packages/docs/src/basics/custom-range.ts | 2 +- packages/docs/src/index.ts | 1 + packages/docs/src/services/doc-auto-format.service.ts | 4 ++-- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/packages/docs-hyper-link-ui/src/commands/operations/popup.operation.ts b/packages/docs-hyper-link-ui/src/commands/operations/popup.operation.ts index 4ca63c3ae7a..6353ce3c562 100644 --- a/packages/docs-hyper-link-ui/src/commands/operations/popup.operation.ts +++ b/packages/docs-hyper-link-ui/src/commands/operations/popup.operation.ts @@ -15,8 +15,8 @@ */ import type { DocumentDataModel, IAccessor, ICommand } from '@univerjs/core'; -import { CommandType, IUniverInstanceService, UniverInstanceType } from '@univerjs/core'; -import { DocSkeletonManagerService, serializeDocRange, TextSelectionManagerService } from '@univerjs/docs'; +import { CommandType, CustomRangeType, IUniverInstanceService, UniverInstanceType } from '@univerjs/core'; +import { DocSkeletonManagerService, getCustomRangesInterestsWithRange, serializeDocRange, TextSelectionManagerService } from '@univerjs/docs'; import { DocumentEditArea, IRenderManagerService } from '@univerjs/engine-render'; import { DocHyperLinkModel } from '@univerjs/docs-hyper-link'; import { DocHyperLinkPopupService } from '../../services/hyper-link-popup.service'; @@ -42,7 +42,8 @@ export const shouldDisableAddLink = (accessor: IAccessor) => { return true; } - const paragraphs = doc.getBody()?.paragraphs; + const body = doc.getBody(); + const paragraphs = body?.paragraphs; if (!paragraphs) { return true; } @@ -50,7 +51,9 @@ export const shouldDisableAddLink = (accessor: IAccessor) => { for (let i = 0, len = paragraphs.length; i < len; i++) { const p = paragraphs[i]; if (activeRange.startOffset <= p.startIndex && activeRange.endOffset > p.startIndex) { - return true; + const insertCustomRanges = getCustomRangesInterestsWithRange(activeRange, body.customRanges ?? []); + // can't insert hyperlink in range contains other custom ranges + return insertCustomRanges.every((range) => range.rangeType === CustomRangeType.HYPERLINK); } if (p.startIndex > activeRange.endOffset!) { diff --git a/packages/docs/src/basics/custom-range.ts b/packages/docs/src/basics/custom-range.ts index 4d62c24ce97..74d29329888 100644 --- a/packages/docs/src/basics/custom-range.ts +++ b/packages/docs/src/basics/custom-range.ts @@ -54,7 +54,7 @@ export function shouldDeleteCustomRange(deleteStart: number, deleteLen: number, return true; } -export function getCustomRangesIntesetsWithRange(range: ITextRange, customRanges: ICustomRange[]) { +export function getCustomRangesInterestsWithRange(range: ITextRange, customRanges: ICustomRange[]) { const result: ICustomRange[] = []; for (let i = 0, len = customRanges.length; i < len; i++) { const customRange = customRanges[i]; diff --git a/packages/docs/src/index.ts b/packages/docs/src/index.ts index d75edf31851..f70bf091505 100644 --- a/packages/docs/src/index.ts +++ b/packages/docs/src/index.ts @@ -14,6 +14,7 @@ * limitations under the License. */ +export { getCustomRangesInterestsWithRange } from './basics/custom-range'; export { getParagraphsInRange, getParagraphsInRanges } from './commands/commands/list.command'; export { replaceSelectionFactory } from './basics/replace'; export { makeSelection, getSelectionText, getDeleteSelection, getInsertSelection, isSegmentIntersects } from './basics/selection'; diff --git a/packages/docs/src/services/doc-auto-format.service.ts b/packages/docs/src/services/doc-auto-format.service.ts index 51368cc9a47..b71aec492a9 100644 --- a/packages/docs/src/services/doc-auto-format.service.ts +++ b/packages/docs/src/services/doc-auto-format.service.ts @@ -16,7 +16,7 @@ import type { DocumentDataModel, ICommandInfo, ICustomRange, IDisposable, IParagraph, IParagraphRange, ITextRange, Nullable } from '@univerjs/core'; import { Disposable, Inject, IUniverInstanceService, toDisposable, UniverInstanceType } from '@univerjs/core'; -import { getCustomRangesIntesetsWithRange } from '../basics/custom-range'; +import { getCustomRangesInterestsWithRange } from '../basics/custom-range'; import type { ITextActiveRange } from './text-selection-manager.service'; import { TextSelectionManagerService } from './text-selection-manager.service'; @@ -122,7 +122,7 @@ export class DocAutoFormatService extends Disposable { selection, isBody: !selection.segmentId, paragraphs: getParagraphsInRange(selection, doc.getBody()?.paragraphs ?? []), - customRanges: getCustomRangesIntesetsWithRange(selection, doc.getBody()?.customRanges ?? []), + customRanges: getCustomRangesInterestsWithRange(selection, doc.getBody()?.customRanges ?? []), commandId: id, commandParams: params, }; From 651be6897682c8b3fa4e8d20dd17dbe7eaa05748 Mon Sep 17 00:00:00 2001 From: zw Date: Mon, 19 Aug 2024 21:53:20 +0800 Subject: [PATCH 23/43] feat: updtae --- .../src/commands/operations/popup.operation.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/docs-hyper-link-ui/src/commands/operations/popup.operation.ts b/packages/docs-hyper-link-ui/src/commands/operations/popup.operation.ts index 6353ce3c562..1da21f0c95c 100644 --- a/packages/docs-hyper-link-ui/src/commands/operations/popup.operation.ts +++ b/packages/docs-hyper-link-ui/src/commands/operations/popup.operation.ts @@ -51,9 +51,7 @@ export const shouldDisableAddLink = (accessor: IAccessor) => { for (let i = 0, len = paragraphs.length; i < len; i++) { const p = paragraphs[i]; if (activeRange.startOffset <= p.startIndex && activeRange.endOffset > p.startIndex) { - const insertCustomRanges = getCustomRangesInterestsWithRange(activeRange, body.customRanges ?? []); - // can't insert hyperlink in range contains other custom ranges - return insertCustomRanges.every((range) => range.rangeType === CustomRangeType.HYPERLINK); + return true; } if (p.startIndex > activeRange.endOffset!) { @@ -61,7 +59,9 @@ export const shouldDisableAddLink = (accessor: IAccessor) => { } } - return false; + const insertCustomRanges = getCustomRangesInterestsWithRange(activeRange, body.customRanges ?? []); + // can't insert hyperlink in range contains other custom ranges + return !insertCustomRanges.every((range) => range.rangeType === CustomRangeType.HYPERLINK); }; export interface IShowDocHyperLinkEditPopupOperationParams { From 8f994d048026de8612a60432b306d93f54044ec1 Mon Sep 17 00:00:00 2001 From: zw Date: Mon, 19 Aug 2024 22:00:01 +0800 Subject: [PATCH 24/43] feat: i18n --- packages/docs-hyper-link-ui/src/locale/en-US.ts | 2 ++ packages/docs-hyper-link-ui/src/locale/ru-RU.ts | 2 ++ packages/docs-hyper-link-ui/src/locale/vi-VN.ts | 2 ++ packages/docs-hyper-link-ui/src/locale/zh-CN.ts | 3 +++ packages/docs-hyper-link-ui/src/locale/zh-TW.ts | 2 ++ 5 files changed, 11 insertions(+) diff --git a/packages/docs-hyper-link-ui/src/locale/en-US.ts b/packages/docs-hyper-link-ui/src/locale/en-US.ts index 4ce664e6e4b..6e12a6e3283 100644 --- a/packages/docs-hyper-link-ui/src/locale/en-US.ts +++ b/packages/docs-hyper-link-ui/src/locale/en-US.ts @@ -25,6 +25,8 @@ const enUS: typeof zhCN = { address: 'Link address', placeholder: 'Please input a link url', addressError: 'Url is illegal!', + label: 'Label', + LabelError: 'Please input label of link', }, info: { copy: 'Copy', diff --git a/packages/docs-hyper-link-ui/src/locale/ru-RU.ts b/packages/docs-hyper-link-ui/src/locale/ru-RU.ts index 02858dc3501..7af27211a51 100644 --- a/packages/docs-hyper-link-ui/src/locale/ru-RU.ts +++ b/packages/docs-hyper-link-ui/src/locale/ru-RU.ts @@ -23,6 +23,8 @@ const ruRU: typeof zhCN = { cancel: 'Отменить', title: 'Ссылка', address: 'Адрес ссылки', + label: 'Текст', + LabelError: 'Введите текст ссылки', placeholder: 'Пожалуйста, введите URL ссылки', addressError: 'URL некорректен!', }, diff --git a/packages/docs-hyper-link-ui/src/locale/vi-VN.ts b/packages/docs-hyper-link-ui/src/locale/vi-VN.ts index 95e5d72dc1b..727f10b466a 100644 --- a/packages/docs-hyper-link-ui/src/locale/vi-VN.ts +++ b/packages/docs-hyper-link-ui/src/locale/vi-VN.ts @@ -23,6 +23,8 @@ const locale: typeof zhCN = { cancel: 'Hủy', title: 'Liên kết', address: 'Địa chỉ liên kết', + label: 'Nhãn', + LabelError: 'Vui lòng nhập nhãn liên kết', placeholder: 'Vui lòng nhập liên kết hợp lệ', addressError: 'Vui lòng nhập liên kết hợp lệ', }, diff --git a/packages/docs-hyper-link-ui/src/locale/zh-CN.ts b/packages/docs-hyper-link-ui/src/locale/zh-CN.ts index 8c3742c7f2e..d45c4cc5aca 100644 --- a/packages/docs-hyper-link-ui/src/locale/zh-CN.ts +++ b/packages/docs-hyper-link-ui/src/locale/zh-CN.ts @@ -21,8 +21,11 @@ const zhCN = { cancel: '取消', title: '链接', address: '链接地址', + label: '文本', + LabelError: '请输入链接文本', placeholder: '请输入合法的链接', addressError: '请输入合法的链接', + }, info: { copy: '复制', diff --git a/packages/docs-hyper-link-ui/src/locale/zh-TW.ts b/packages/docs-hyper-link-ui/src/locale/zh-TW.ts index 85d825943e3..14c1c864392 100644 --- a/packages/docs-hyper-link-ui/src/locale/zh-TW.ts +++ b/packages/docs-hyper-link-ui/src/locale/zh-TW.ts @@ -23,6 +23,8 @@ const locale: typeof zhCN = { cancel: '取消', title: '連結', address: '連結地址', + label: '文本', + LabelError: '请输入链接文本', placeholder: '請輸入合法的連結', addressError: '請輸入合法的連結', }, From 494264bd3f1566a1fb8aab48f621006d2574055a Mon Sep 17 00:00:00 2001 From: zw Date: Mon, 19 Aug 2024 22:09:03 +0800 Subject: [PATCH 25/43] feat: update --- .../render-controllers/doc-resize.render-controller.ts | 4 ++-- packages/docs-ui/src/services/doc-event-manager.service.ts | 4 ++-- packages/ui/src/views/components/popup/CanvasPopup.tsx | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/docs-ui/src/controllers/render-controllers/doc-resize.render-controller.ts b/packages/docs-ui/src/controllers/render-controllers/doc-resize.render-controller.ts index 97d8fb7eb2d..ea684b1dc5d 100644 --- a/packages/docs-ui/src/controllers/render-controllers/doc-resize.render-controller.ts +++ b/packages/docs-ui/src/controllers/render-controllers/doc-resize.render-controller.ts @@ -17,7 +17,7 @@ import { Disposable, DOCS_ZEN_EDITOR_UNIT_ID_KEY, fromEventSubject, Inject, isInternalEditorID } from '@univerjs/core'; import type { IRenderContext, IRenderModule } from '@univerjs/engine-render'; import { TRANSFORM_CHANGE_OBSERVABLE_TYPE } from '@univerjs/engine-render'; -import { filter, throttleTime } from 'rxjs'; +import { animationFrameScheduler, filter, throttleTime } from 'rxjs'; import { TextSelectionManagerService } from '@univerjs/docs'; import { DocPageLayoutService } from '../../services/doc-page-layout.service'; @@ -39,7 +39,7 @@ export class DocResizeRenderController extends Disposable implements IRenderModu this.disposeWithMe( fromEventSubject(this._context.engine.onTransformChange$).pipe( filter((evt) => evt.type === TRANSFORM_CHANGE_OBSERVABLE_TYPE.resize), - throttleTime(16) + throttleTime(0, animationFrameScheduler) ).subscribe(() => { this._docPageLayoutService.calculatePagePosition(); this._textSelectionManagerService.refreshSelection(); diff --git a/packages/docs-ui/src/services/doc-event-manager.service.ts b/packages/docs-ui/src/services/doc-event-manager.service.ts index d058f49715c..ece96b5efa4 100644 --- a/packages/docs-ui/src/services/doc-event-manager.service.ts +++ b/packages/docs-ui/src/services/doc-event-manager.service.ts @@ -19,7 +19,7 @@ import { Disposable, fromEventSubject, Inject } from '@univerjs/core'; import { DocSkeletonManagerService } from '@univerjs/docs'; import type { Documents, DocumentSkeleton, IBoundRectNoAngle, IDocumentSkeletonGlyph, IMouseEvent, IPointerEvent, IRenderContext, IRenderModule } from '@univerjs/engine-render'; import { getLineBounding, NodePositionConvertToCursor, TRANSFORM_CHANGE_OBSERVABLE_TYPE } from '@univerjs/engine-render'; -import { distinctUntilChanged, filter, Subject, throttleTime } from 'rxjs'; +import { animationFrameScheduler, distinctUntilChanged, filter, Subject, throttleTime } from 'rxjs'; import { transformOffset2Bound } from './doc-popup-manager.service'; interface ICustomRangeBound { @@ -152,7 +152,7 @@ export class DocEventManagerService extends Disposable implements IRenderModule } private _initEvents() { - this.disposeWithMe(fromEventSubject(this._context.scene.onPointerMove$).pipe(throttleTime(16)).subscribe((evt) => { + this.disposeWithMe(fromEventSubject(this._context.scene.onPointerMove$).pipe(throttleTime(0, animationFrameScheduler)).subscribe((evt) => { this._hoverCustomRanges$.next( this._calcActiveRanges(evt) ); diff --git a/packages/ui/src/views/components/popup/CanvasPopup.tsx b/packages/ui/src/views/components/popup/CanvasPopup.tsx index 3d5b90ec701..ef9b86f6bb3 100644 --- a/packages/ui/src/views/components/popup/CanvasPopup.tsx +++ b/packages/ui/src/views/components/popup/CanvasPopup.tsx @@ -18,7 +18,7 @@ import { useDependency } from '@univerjs/core'; import React, { useEffect, useMemo, useState } from 'react'; import { RectPopup } from '@univerjs/design'; import type { IBoundRectNoAngle } from '@univerjs/engine-render'; -import { throttleTime } from 'rxjs'; +import { animationFrameScheduler, throttleTime } from 'rxjs'; import { ICanvasPopupService } from '../../../services/popup/canvas-popup.service'; import { useObservable } from '../../../components/hooks/observable'; import { ComponentManager } from '../../../common'; @@ -31,8 +31,8 @@ interface ISingleCanvasPopupProps { const SingleCanvasPopup = ({ popup, children }: ISingleCanvasPopupProps) => { const [hidden, setHidden] = useState(false); - const anchorRect$ = useMemo(() => popup.anchorRect$.pipe(throttleTime(16)), [popup.anchorRect$]); - const excludeRects$ = useMemo(() => popup.excludeRects$?.pipe(throttleTime(16)), [popup.excludeRects$]); + const anchorRect$ = useMemo(() => popup.anchorRect$.pipe(throttleTime(0, animationFrameScheduler)), [popup.anchorRect$]); + const excludeRects$ = useMemo(() => popup.excludeRects$?.pipe(throttleTime(0, animationFrameScheduler)), [popup.excludeRects$]); const anchorRect = useObservable(anchorRect$, popup.anchorRect); const excludeRects = useObservable(excludeRects$, popup.excludeRects); const { bottom, left, right, top } = anchorRect; From fe026625a218ad3132ee1aac434cc5f03d2b81fc Mon Sep 17 00:00:00 2001 From: zw Date: Mon, 19 Aug 2024 22:15:40 +0800 Subject: [PATCH 26/43] feat: update --- .../src/commands/commands/update-link.command.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/docs-hyper-link-ui/src/commands/commands/update-link.command.ts b/packages/docs-hyper-link-ui/src/commands/commands/update-link.command.ts index d5b648542d3..b523290b81a 100644 --- a/packages/docs-hyper-link-ui/src/commands/commands/update-link.command.ts +++ b/packages/docs-hyper-link-ui/src/commands/commands/update-link.command.ts @@ -16,8 +16,8 @@ import { CommandType, CustomRangeType, DataStreamTreeTokenType, generateRandomId, type ICommand, ICommandService, sequenceExecute } from '@univerjs/core'; import { replaceSelectionFactory, TextSelectionManagerService } from '@univerjs/docs'; +import type { IAddDocHyperLinkMutationParams } from '@univerjs/docs-hyper-link'; import { AddDocHyperLinkMutation } from '@univerjs/docs-hyper-link'; -import type { IAddDocHyperLinkMutationParams } from '../../../../docs-hyper-link/lib/types'; export interface IUpdateDocHyperLinkCommandParams { unitId: string; From 14c171812079d34e1498cd8fc080bc32b9023807 Mon Sep 17 00:00:00 2001 From: zw Date: Mon, 19 Aug 2024 22:24:33 +0800 Subject: [PATCH 27/43] feat: update --- packages/docs-hyper-link-ui/src/locale/en-US.ts | 2 +- packages/docs-hyper-link-ui/src/locale/ru-RU.ts | 2 +- packages/docs-hyper-link-ui/src/locale/vi-VN.ts | 2 +- packages/docs-hyper-link-ui/src/locale/zh-CN.ts | 2 +- packages/docs-hyper-link-ui/src/locale/zh-TW.ts | 2 +- .../src/views/hyper-link-edit/index.module.less | 1 + .../docs-hyper-link-ui/src/views/hyper-link-edit/index.tsx | 4 ---- 7 files changed, 6 insertions(+), 9 deletions(-) diff --git a/packages/docs-hyper-link-ui/src/locale/en-US.ts b/packages/docs-hyper-link-ui/src/locale/en-US.ts index 6e12a6e3283..c0e484b9071 100644 --- a/packages/docs-hyper-link-ui/src/locale/en-US.ts +++ b/packages/docs-hyper-link-ui/src/locale/en-US.ts @@ -22,7 +22,7 @@ const enUS: typeof zhCN = { confirm: 'Confirm', cancel: 'Cancel', title: 'Link', - address: 'Link address', + address: 'Link', placeholder: 'Please input a link url', addressError: 'Url is illegal!', label: 'Label', diff --git a/packages/docs-hyper-link-ui/src/locale/ru-RU.ts b/packages/docs-hyper-link-ui/src/locale/ru-RU.ts index 7af27211a51..ea26fd77f98 100644 --- a/packages/docs-hyper-link-ui/src/locale/ru-RU.ts +++ b/packages/docs-hyper-link-ui/src/locale/ru-RU.ts @@ -22,7 +22,7 @@ const ruRU: typeof zhCN = { confirm: 'Подтвердить', cancel: 'Отменить', title: 'Ссылка', - address: 'Адрес ссылки', + address: 'Cсылки', label: 'Текст', LabelError: 'Введите текст ссылки', placeholder: 'Пожалуйста, введите URL ссылки', diff --git a/packages/docs-hyper-link-ui/src/locale/vi-VN.ts b/packages/docs-hyper-link-ui/src/locale/vi-VN.ts index 727f10b466a..03a5e7ca869 100644 --- a/packages/docs-hyper-link-ui/src/locale/vi-VN.ts +++ b/packages/docs-hyper-link-ui/src/locale/vi-VN.ts @@ -22,7 +22,7 @@ const locale: typeof zhCN = { confirm: 'Xác nhận', cancel: 'Hủy', title: 'Liên kết', - address: 'Địa chỉ liên kết', + address: 'Liên kết', label: 'Nhãn', LabelError: 'Vui lòng nhập nhãn liên kết', placeholder: 'Vui lòng nhập liên kết hợp lệ', diff --git a/packages/docs-hyper-link-ui/src/locale/zh-CN.ts b/packages/docs-hyper-link-ui/src/locale/zh-CN.ts index d45c4cc5aca..c71262ed97a 100644 --- a/packages/docs-hyper-link-ui/src/locale/zh-CN.ts +++ b/packages/docs-hyper-link-ui/src/locale/zh-CN.ts @@ -20,7 +20,7 @@ const zhCN = { confirm: '确认', cancel: '取消', title: '链接', - address: '链接地址', + address: '链接', label: '文本', LabelError: '请输入链接文本', placeholder: '请输入合法的链接', diff --git a/packages/docs-hyper-link-ui/src/locale/zh-TW.ts b/packages/docs-hyper-link-ui/src/locale/zh-TW.ts index 14c1c864392..30599512ce4 100644 --- a/packages/docs-hyper-link-ui/src/locale/zh-TW.ts +++ b/packages/docs-hyper-link-ui/src/locale/zh-TW.ts @@ -22,7 +22,7 @@ const locale: typeof zhCN = { confirm: '確認', cancel: '取消', title: '連結', - address: '連結地址', + address: '連結', label: '文本', LabelError: '请输入链接文本', placeholder: '請輸入合法的連結', diff --git a/packages/docs-hyper-link-ui/src/views/hyper-link-edit/index.module.less b/packages/docs-hyper-link-ui/src/views/hyper-link-edit/index.module.less index 73a72186005..80a32d65ffc 100644 --- a/packages/docs-hyper-link-ui/src/views/hyper-link-edit/index.module.less +++ b/packages/docs-hyper-link-ui/src/views/hyper-link-edit/index.module.less @@ -5,6 +5,7 @@ box-shadow: var(--box-shadow-base); padding: 20px 24px; width: 328px; + box-sizing: border-box; &-title { display: flex; diff --git a/packages/docs-hyper-link-ui/src/views/hyper-link-edit/index.tsx b/packages/docs-hyper-link-ui/src/views/hyper-link-edit/index.tsx index 8c627536389..c74dfd3a653 100644 --- a/packages/docs-hyper-link-ui/src/views/hyper-link-edit/index.tsx +++ b/packages/docs-hyper-link-ui/src/views/hyper-link-edit/index.tsx @@ -125,10 +125,6 @@ export const DocHyperLinkEdit = () => { return (
-
- {localeService.t('docLink.edit.title')} - -
{editingId ? ( From a83ca4dac554362678fb7806a58fd05950980517 Mon Sep 17 00:00:00 2001 From: zw Date: Mon, 19 Aug 2024 22:30:32 +0800 Subject: [PATCH 28/43] feat: lint --- packages/docs-hyper-link-ui/src/views/hyper-link-edit/index.tsx | 1 - packages/docs/src/commands/mutations/core-editing.mutation.ts | 2 +- .../engine-render/src/components/docs/layout/doc-skeleton.ts | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/docs-hyper-link-ui/src/views/hyper-link-edit/index.tsx b/packages/docs-hyper-link-ui/src/views/hyper-link-edit/index.tsx index c74dfd3a653..b447daf4146 100644 --- a/packages/docs-hyper-link-ui/src/views/hyper-link-edit/index.tsx +++ b/packages/docs-hyper-link-ui/src/views/hyper-link-edit/index.tsx @@ -16,7 +16,6 @@ import { Button, FormLayout, Input } from '@univerjs/design'; import React, { useEffect, useState } from 'react'; -import { CloseSingle } from '@univerjs/icons'; import { getBodySlice, ICommandService, IUniverInstanceService, LocaleService, Tools, UniverInstanceType, useDependency, useObservable } from '@univerjs/core'; import { DocHyperLinkModel } from '@univerjs/docs-hyper-link'; import type { DocumentDataModel } from '@univerjs/core'; diff --git a/packages/docs/src/commands/mutations/core-editing.mutation.ts b/packages/docs/src/commands/mutations/core-editing.mutation.ts index 56f042fb1dc..b73a42976a8 100644 --- a/packages/docs/src/commands/mutations/core-editing.mutation.ts +++ b/packages/docs/src/commands/mutations/core-editing.mutation.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { CommandType, IUniverInstanceService, JSONX, Tools } from '@univerjs/core'; +import { CommandType, IUniverInstanceService, JSONX } from '@univerjs/core'; import type { IMutation, IMutationCommonParams, JSONXActions, Nullable } from '@univerjs/core'; import { IRenderManagerService, type ITextRangeWithStyle } from '@univerjs/engine-render'; import { serializeDocRange, TextSelectionManagerService } from '../../services/text-selection-manager.service'; diff --git a/packages/engine-render/src/components/docs/layout/doc-skeleton.ts b/packages/engine-render/src/components/docs/layout/doc-skeleton.ts index 5cccae3e6b3..583bc5679f3 100644 --- a/packages/engine-render/src/components/docs/layout/doc-skeleton.ts +++ b/packages/engine-render/src/components/docs/layout/doc-skeleton.ts @@ -31,7 +31,6 @@ import { Liquid } from '../liquid'; import type { DocumentViewModel } from '../view-model/document-view-model'; import { DocumentEditArea } from '../view-model/document-view-model'; import { getPageFromPath } from '../text-selection/convert-text-range'; -import type { IDocumentOffsetConfig } from '../document'; import type { ILayoutContext } from './tools'; import { getLastPage, getNullSkeleton, prepareSectionBreakConfig, resetContext, setPageParent, updateBlockIndex, updateInlineDrawingCoords } from './tools'; import { createSkeletonSection } from './model/section'; From a4fd33b412977a6f8e31362fb61c68c0a9352bb1 Mon Sep 17 00:00:00 2001 From: zw Date: Mon, 19 Aug 2024 22:45:55 +0800 Subject: [PATCH 29/43] feat: update --- .../commands/operations/show-comment-panel.operation.ts | 9 +++------ 1 file changed, 3 insertions(+), 6 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 0ca501b2949..ac9cb2bfdc4 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 @@ -36,7 +36,7 @@ export const ShowCommentPanelOperation: ICommand Date: Tue, 20 Aug 2024 14:19:46 +0800 Subject: [PATCH 30/43] feat: update --- .../commands/operations/popup.operation.ts | 2 ++ .../doc-hyper-link-selection.controller.ts | 25 ++++++++----------- .../src/services/hyper-link-popup.service.ts | 22 +++++++++------- .../src/views/hyper-link-edit/index.tsx | 20 +++++++-------- .../src/views/hyper-link-popup/index.tsx | 2 +- .../src/services/doc-popup-manager.service.ts | 1 - 6 files changed, 36 insertions(+), 36 deletions(-) diff --git a/packages/docs-hyper-link-ui/src/commands/operations/popup.operation.ts b/packages/docs-hyper-link-ui/src/commands/operations/popup.operation.ts index 1da21f0c95c..2da2b09fe01 100644 --- a/packages/docs-hyper-link-ui/src/commands/operations/popup.operation.ts +++ b/packages/docs-hyper-link-ui/src/commands/operations/popup.operation.ts @@ -68,6 +68,8 @@ export interface IShowDocHyperLinkEditPopupOperationParams { link?: { unitId: string; linkId: string; + segmentId?: string; + segmentPage?: number; }; } diff --git a/packages/docs-hyper-link-ui/src/controllers/doc-hyper-link-selection.controller.ts b/packages/docs-hyper-link-ui/src/controllers/doc-hyper-link-selection.controller.ts index 41ba90c90c1..eefd4b15eae 100644 --- a/packages/docs-hyper-link-ui/src/controllers/doc-hyper-link-selection.controller.ts +++ b/packages/docs-hyper-link-ui/src/controllers/doc-hyper-link-selection.controller.ts @@ -39,30 +39,25 @@ export class DocHyperLinkSelectionController extends Disposable { this._commandService.onCommandExecuted((commandInfo) => { if (commandInfo.id === SetTextSelectionsOperation.id) { const params = commandInfo.params as ISetTextSelectionsOperationParams; - const { unitId, ranges } = params; - const render = this._renderMangerService.getRenderById(unitId); - const skeleton = render?.with(DocSkeletonManagerService).getSkeleton(); - const editArea = skeleton?.getViewModel().getEditArea(); - if (editArea !== DocumentEditArea.BODY) { - this._docHyperLinkService.hideInfoPopup(); - this._docHyperLinkService.hideEditPopup(); - return; - } + const { unitId, ranges, segmentId } = params; const doc = this._univerInstanceService.getUnit(unitId, UniverInstanceType.UNIVER_DOC); const primary = ranges[0]; if (primary && doc) { - const { startOffset, endOffset, collapsed } = primary; - const customRanges = doc.getBody()?.customRanges; - if (collapsed) { // cursor + const { startOffset, endOffset, collapsed, segmentPage } = primary; + const customRanges = doc.getSelfOrHeaderFooterModel(segmentId)?.getBody()?.customRanges; + if (collapsed) { + // cursor const index = customRanges?.findIndex((value) => (value.startIndex) < startOffset && value.endIndex > endOffset - 1) ?? -1; if (index > -1) { const customRange = customRanges![index]; - this._docHyperLinkService.showInfoPopup({ unitId, linkId: customRange.rangeId }); + this._docHyperLinkService.showInfoPopup({ unitId, linkId: customRange.rangeId, segmentId, segmentPage }); return; } - } else { // range - if (customRanges?.find((value) => value.startIndex <= startOffset && value.endIndex >= (endOffset - 1))) { + } else { + // range + const range = customRanges?.find((value) => value.startIndex <= startOffset && value.endIndex >= (endOffset - 1)); + if (range) { return; } } diff --git a/packages/docs-hyper-link-ui/src/services/hyper-link-popup.service.ts b/packages/docs-hyper-link-ui/src/services/hyper-link-popup.service.ts index c1c7a1d0f28..4140738e113 100644 --- a/packages/docs-hyper-link-ui/src/services/hyper-link-popup.service.ts +++ b/packages/docs-hyper-link-ui/src/services/hyper-link-popup.service.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import type { DocumentDataModel, IDisposable, ITextRange, Nullable } from '@univerjs/core'; +import type { DocumentDataModel, IDisposable, ITextRange, ITextRangeParam, Nullable } from '@univerjs/core'; import { Disposable, ICommandService, Inject, IUniverInstanceService, UniverInstanceType } from '@univerjs/core'; import { TextSelectionManagerService } from '@univerjs/docs'; import { DocCanvasPopManagerService } from '@univerjs/docs-ui'; @@ -48,8 +48,7 @@ export class DocHyperLinkPopupService extends Disposable { @Inject(DocCanvasPopManagerService) private readonly _docCanvasPopupManagerService: DocCanvasPopManagerService, @Inject(TextSelectionManagerService) private readonly _textSelectionManagerService: TextSelectionManagerService, @Inject(DocHyperLinkModel) private readonly _docHyperLinkModel: DocHyperLinkModel, - @IUniverInstanceService private readonly _univerInstanceService: IUniverInstanceService, - @ICommandService private readonly _commandService: ICommandService + @IUniverInstanceService private readonly _univerInstanceService: IUniverInstanceService ) { super(); @@ -72,17 +71,20 @@ export class DocHyperLinkPopupService extends Disposable { this._editPopup.dispose(); } this._editingLink$.next(linkInfo); - let activeRange: Nullable = this._textSelectionManagerService.getActiveTextRangeWithStyle(); + let activeRange: Nullable = this._textSelectionManagerService.getActiveTextRangeWithStyle(); if (linkInfo) { - const { unitId, linkId } = linkInfo; + const { unitId, linkId, segmentId, segmentPage } = linkInfo; const doc = this._univerInstanceService.getUnit(unitId, UniverInstanceType.UNIVER_DOC); - const range = doc?.getBody()?.customRanges?.find((i) => i.rangeId === linkId); + const range = doc?.getSelfOrHeaderFooterModel(segmentId)?.getBody()?.customRanges?.find((i) => i.rangeId === linkId); if (range) { activeRange = { collapsed: false, startOffset: range.startIndex, endOffset: range.endIndex + 1, + segmentId, + segmentPage, }; + this._textSelectionManagerService.replaceTextRanges([{ startOffset: range.startIndex, endOffset: range.endIndex + 1, @@ -111,10 +113,12 @@ export class DocHyperLinkPopupService extends Disposable { } showInfoPopup(info: ILinkInfo): Nullable { - const { linkId, unitId } = info; + const { linkId, unitId, segmentId, segmentPage } = info; if ( this.showing?.linkId === linkId && - this.showing?.unitId === unitId + this.showing?.unitId === unitId && + this.showing?.segmentId === segmentId && + this.showing?.segmentPage === segmentPage ) { return; } @@ -128,7 +132,7 @@ export class DocHyperLinkPopupService extends Disposable { return; } const range = doc.getSelfOrHeaderFooterModel(info.segmentId).getBody()?.customRanges?.find((i) => i.rangeId === linkId); - this._showingLink$.next({ unitId, linkId }); + this._showingLink$.next({ unitId, linkId, segmentId, segmentPage }); if (!range) { return; } diff --git a/packages/docs-hyper-link-ui/src/views/hyper-link-edit/index.tsx b/packages/docs-hyper-link-ui/src/views/hyper-link-edit/index.tsx index b447daf4146..b52caa45e5b 100644 --- a/packages/docs-hyper-link-ui/src/views/hyper-link-edit/index.tsx +++ b/packages/docs-hyper-link-ui/src/views/hyper-link-edit/index.tsx @@ -45,7 +45,7 @@ export const DocHyperLinkEdit = () => { const hyperLinkService = useDependency(DocHyperLinkPopupService); const localeService = useDependency(LocaleService); const hyperLinkModel = useDependency(DocHyperLinkModel); - const editingId = useObservable(hyperLinkService.editingLink$); + const editing = useObservable(hyperLinkService.editingLink$); const commandService = useDependency(ICommandService); const univerInstanceService = useDependency(IUniverInstanceService); const textSelectionRenderManager = useDependency(ITextSelectionRenderManager); @@ -54,8 +54,8 @@ export const DocHyperLinkEdit = () => { const [label, setLabel] = useState(''); const [showError, setShowError] = useState(false); const isLegal = Tools.isLegalUrl(link); - const doc = editingId - ? univerInstanceService.getUnit(editingId.unitId, UniverInstanceType.UNIVER_DOC) : + const doc = editing + ? univerInstanceService.getUnit(editing.unitId, UniverInstanceType.UNIVER_DOC) : univerInstanceService.getCurrentUnitForType(UniverInstanceType.UNIVER_DOC); useEffect(() => { @@ -64,10 +64,10 @@ export const DocHyperLinkEdit = () => { return; } - if (editingId) { - const linkDetail = editingId ? hyperLinkModel.getLink(editingId.unitId, editingId.linkId) : null; + if (editing) { + const linkDetail = editing ? hyperLinkModel.getLink(editing.unitId, editing.linkId) : null; setLink(linkDetail?.payload ?? ''); - const matchedRange = doc?.getBody()?.customRanges?.find((i) => linkDetail?.id === i.rangeId); + const matchedRange = doc?.getSelfOrHeaderFooterModel(editing.segmentId)?.getBody()?.customRanges?.find((i) => linkDetail?.id === i.rangeId); if (doc && matchedRange) { setLabel(getPlainTextFormBody(getBodySlice(doc.getBody()!, matchedRange.startIndex, matchedRange.endIndex))); } @@ -79,7 +79,7 @@ export const DocHyperLinkEdit = () => { const linkDetail = hyperLinkModel.getLink(doc.getUnitId(), matchedRange.rangeId); setLink(linkDetail?.payload ?? ''); } - }, [doc, editingId, hyperLinkModel, textSelectionManagerService, univerInstanceService]); + }, [doc, editing, hyperLinkModel, textSelectionManagerService, univerInstanceService]); useEffect(() => { textSelectionRenderManager.blurEditor(); @@ -98,7 +98,7 @@ export const DocHyperLinkEdit = () => { } const linkFinal = transformUrl(link); - if (!editingId) { + if (!editing) { commandService.executeCommand(AddDocHyperLinkCommand.id, { unitId: doc.getUnitId(), payload: linkFinal, @@ -111,7 +111,7 @@ export const DocHyperLinkEdit = () => { commandService.executeCommand(UpdateDocHyperLinkCommand.id, { unitId: doc.getUnitId(), payload: linkFinal, - linkId: editingId.linkId, + linkId: editing.linkId, label, }); } @@ -125,7 +125,7 @@ export const DocHyperLinkEdit = () => { return (
- {editingId + {editing ? ( { return null; } - const { unitId, linkId } = currentPopup; + const { unitId, linkId, segmentId, segmentPage } = currentPopup; const link = hyperLinkModel.getLink(unitId, linkId); if (!link) { return null; diff --git a/packages/docs-ui/src/services/doc-popup-manager.service.ts b/packages/docs-ui/src/services/doc-popup-manager.service.ts index cb868aa0908..da7849ebd96 100644 --- a/packages/docs-ui/src/services/doc-popup-manager.service.ts +++ b/packages/docs-ui/src/services/doc-popup-manager.service.ts @@ -259,7 +259,6 @@ export class DocCanvasPopManagerService extends Disposable { const workbook = this._univerInstanceService.getCurrentUnitForType(UniverInstanceType.UNIVER_DOC)!; const unitId = workbook.getUnitId(); const { direction = 'top' } = popup; - const currentRender = this._renderManagerService.getRenderById(unitId); if (!currentRender) { return { From 101514229811b423d9d598b50f7cba82ab528b71 Mon Sep 17 00:00:00 2001 From: zw Date: Tue, 20 Aug 2024 14:36:03 +0800 Subject: [PATCH 31/43] feat: update --- .../hyper-link-event.render-controller.ts | 30 +++++++++++++++---- .../src/services/doc-event-manager.service.ts | 15 +++++++++- 2 files changed, 38 insertions(+), 7 deletions(-) diff --git a/packages/docs-hyper-link-ui/src/controllers/render-controllers/hyper-link-event.render-controller.ts b/packages/docs-hyper-link-ui/src/controllers/render-controllers/hyper-link-event.render-controller.ts index a7cb3c1a4c4..38383ca4026 100644 --- a/packages/docs-hyper-link-ui/src/controllers/render-controllers/hyper-link-event.render-controller.ts +++ b/packages/docs-hyper-link-ui/src/controllers/render-controllers/hyper-link-event.render-controller.ts @@ -17,16 +17,22 @@ import type { DocumentDataModel } from '@univerjs/core'; import { CustomRangeType, Disposable, ICommandService, Inject } from '@univerjs/core'; import { DocEventManagerService } from '@univerjs/docs-ui'; -import type { IRenderContext, IRenderModule } from '@univerjs/engine-render'; +import { DocumentEditArea, type IRenderContext, type IRenderModule } from '@univerjs/engine-render'; +import { DocSkeletonManagerService } from '@univerjs/docs'; import { ClickDocHyperLinkOperation, ToggleDocHyperLinkInfoPopupOperation } from '../../commands/operations/popup.operation'; import { DocHyperLinkPopupService } from '../../services/hyper-link-popup.service'; export class DocHyperLinkEventRenderController extends Disposable implements IRenderModule { + get _skeleton() { + return this._docSkeletonManagerService.getSkeleton(); + } + constructor( private readonly _context: IRenderContext, @Inject(DocEventManagerService) private readonly _docEventManagerService: DocEventManagerService, @ICommandService private readonly _commandService: ICommandService, - @Inject(DocHyperLinkPopupService) private readonly _hyperLinkPopupService: DocHyperLinkPopupService + @Inject(DocHyperLinkPopupService) private readonly _hyperLinkPopupService: DocHyperLinkPopupService, + @Inject(DocSkeletonManagerService) private readonly _docSkeletonManagerService: DocSkeletonManagerService ) { super(); @@ -34,10 +40,24 @@ export class DocHyperLinkEventRenderController extends Disposable implements IRe this._initClick(); } + private _hideInfoPopup() { + if (this._hyperLinkPopupService.showing) { + this._commandService.executeCommand( + ToggleDocHyperLinkInfoPopupOperation.id + ); + } + } + private _initHover() { this.disposeWithMe( this._docEventManagerService.hoverCustomRanges$.subscribe((ranges) => { const link = ranges.find((range) => range.range.rangeType === CustomRangeType.HYPERLINK); + const editArea = this._skeleton.getViewModel().getEditArea(); + if ((link?.segmentId && editArea === DocumentEditArea.BODY) || (!link?.segmentId && editArea !== DocumentEditArea.BODY)) { + this._hideInfoPopup(); + return; + } + if (link) { this._commandService.executeCommand( ToggleDocHyperLinkInfoPopupOperation.id, @@ -49,10 +69,8 @@ export class DocHyperLinkEventRenderController extends Disposable implements IRe rangeId: link.range.rangeId, } ); - } else if (this._hyperLinkPopupService.showing) { - this._commandService.executeCommand( - ToggleDocHyperLinkInfoPopupOperation.id - ); + } else { + this._hideInfoPopup(); } }) ); diff --git a/packages/docs-ui/src/services/doc-event-manager.service.ts b/packages/docs-ui/src/services/doc-event-manager.service.ts index ece96b5efa4..cf460cf0c3f 100644 --- a/packages/docs-ui/src/services/doc-event-manager.service.ts +++ b/packages/docs-ui/src/services/doc-event-manager.service.ts @@ -18,7 +18,7 @@ import type { DocumentDataModel, ICustomRange, IParagraph, ITextRangeParam, Null import { Disposable, fromEventSubject, Inject } from '@univerjs/core'; import { DocSkeletonManagerService } from '@univerjs/docs'; import type { Documents, DocumentSkeleton, IBoundRectNoAngle, IDocumentSkeletonGlyph, IMouseEvent, IPointerEvent, IRenderContext, IRenderModule } from '@univerjs/engine-render'; -import { getLineBounding, NodePositionConvertToCursor, TRANSFORM_CHANGE_OBSERVABLE_TYPE } from '@univerjs/engine-render'; +import { CURSOR_TYPE, getLineBounding, NodePositionConvertToCursor, TRANSFORM_CHANGE_OBSERVABLE_TYPE } from '@univerjs/engine-render'; import { animationFrameScheduler, distinctUntilChanged, filter, Subject, throttleTime } from 'rxjs'; import { transformOffset2Bound } from './doc-popup-manager.service'; @@ -127,6 +127,7 @@ export class DocEventManagerService extends Disposable implements IRenderModule this._initResetDirty(); this._initEvents(); + this._initPointer(); } override dispose() { @@ -135,6 +136,18 @@ export class DocEventManagerService extends Disposable implements IRenderModule super.dispose(); } + private _initPointer() { + let preCursor = CURSOR_TYPE.TEXT; + this.disposeWithMe(this.hoverCustomRanges$.subscribe((ranges) => { + if (ranges.length) { + preCursor = this._context.scene.getCursor(); + this._context.scene.setCursor(CURSOR_TYPE.POINTER); + } else { + this._context.scene.setCursor(preCursor); + } + })); + } + private _initResetDirty() { this.disposeWithMe(this._skeleton.dirty$.subscribe(() => { this._customRangeDirty = true; From d7900905a615304c840a8bbe045c6976710e333a Mon Sep 17 00:00:00 2001 From: zw Date: Tue, 20 Aug 2024 14:58:53 +0800 Subject: [PATCH 32/43] fix: table checklist --- .../docs-ui/src/services/doc-event-manager.service.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/docs-ui/src/services/doc-event-manager.service.ts b/packages/docs-ui/src/services/doc-event-manager.service.ts index cf460cf0c3f..da68c8016b9 100644 --- a/packages/docs-ui/src/services/doc-event-manager.service.ts +++ b/packages/docs-ui/src/services/doc-event-manager.service.ts @@ -278,16 +278,20 @@ export class DocEventManagerService extends Disposable implements IRenderModule paragraphRanges.forEach((paragraph) => { if (paragraph.bullet && paragraph.bullet.listType.indexOf('CHECK_LIST') === 0) { const calcRect = (pageIndex: number) => { - const node = this._skeleton.findNodeByCharIndex(paragraph.paragraphStart, segmentId, pageIndex); + const node = this._skeleton.findNodeByCharIndex(paragraph.paragraphEnd, segmentId, pageIndex); + const divide = node?.parent; + const line = divide?.parent; - if (!node) { + const bulletNode = line?.divides?.[0]?.glyphGroup?.[0]; + + if (!bulletNode) { return; } - const bulletNode = node.parent?.glyphGroup[0]; if (!bulletNode) { return; } + const rect = calcDocGlyphPosition(bulletNode, this._documents, this._skeleton, pageIndex); if (!rect) { From 1ad16285bad33985bae77dece82dddfe2ac6a245 Mon Sep 17 00:00:00 2001 From: zw Date: Tue, 20 Aug 2024 15:29:46 +0800 Subject: [PATCH 33/43] feat: update --- .../src/services/doc-event-manager.service.ts | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/packages/docs-ui/src/services/doc-event-manager.service.ts b/packages/docs-ui/src/services/doc-event-manager.service.ts index da68c8016b9..d0c9a73ed09 100644 --- a/packages/docs-ui/src/services/doc-event-manager.service.ts +++ b/packages/docs-ui/src/services/doc-event-manager.service.ts @@ -267,22 +267,19 @@ export class DocEventManagerService extends Disposable implements IRenderModule } private _buildBulletBoundsBySegment(segmentId?: string) { - const paragraphs = this._context.unit.getSelfOrHeaderFooterModel(segmentId)?.getBody()?.paragraphs ?? []; + const body = this._context.unit.getSelfOrHeaderFooterModel(segmentId)?.getBody(); + const paragraphs = body?.paragraphs ?? []; const bounds: IBulletBound[] = []; - const paragraphRanges = paragraphs.map((paragraph, i) => ({ - ...paragraph, - paragraphStart: (paragraphs[i - 1]?.startIndex ?? -1) + 1, - paragraphEnd: paragraph.startIndex, - })); - paragraphRanges.forEach((paragraph) => { + paragraphs.forEach((paragraph) => { if (paragraph.bullet && paragraph.bullet.listType.indexOf('CHECK_LIST') === 0) { const calcRect = (pageIndex: number) => { - const node = this._skeleton.findNodeByCharIndex(paragraph.paragraphEnd, segmentId, pageIndex); + const node = this._skeleton.findNodeByCharIndex(paragraph.startIndex, segmentId, pageIndex); const divide = node?.parent; const line = divide?.parent; - - const bulletNode = line?.divides?.[0]?.glyphGroup?.[0]; + const column = line?.parent; + const targetLine = column?.lines.find((l) => l.paragraphStart && l.paragraphIndex === paragraph.startIndex); + const bulletNode = targetLine?.divides?.[0]?.glyphGroup?.[0]; if (!bulletNode) { return; From 907d3536755738dee173080319a4abe49d6bdadc Mon Sep 17 00:00:00 2001 From: zw Date: Tue, 20 Aug 2024 15:58:03 +0800 Subject: [PATCH 34/43] feat: update --- .../src/services/doc-event-manager.service.ts | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/packages/docs-ui/src/services/doc-event-manager.service.ts b/packages/docs-ui/src/services/doc-event-manager.service.ts index d0c9a73ed09..4db2fb73cd9 100644 --- a/packages/docs-ui/src/services/doc-event-manager.service.ts +++ b/packages/docs-ui/src/services/doc-event-manager.service.ts @@ -19,7 +19,7 @@ import { Disposable, fromEventSubject, Inject } from '@univerjs/core'; import { DocSkeletonManagerService } from '@univerjs/docs'; import type { Documents, DocumentSkeleton, IBoundRectNoAngle, IDocumentSkeletonGlyph, IMouseEvent, IPointerEvent, IRenderContext, IRenderModule } from '@univerjs/engine-render'; import { CURSOR_TYPE, getLineBounding, NodePositionConvertToCursor, TRANSFORM_CHANGE_OBSERVABLE_TYPE } from '@univerjs/engine-render'; -import { animationFrameScheduler, distinctUntilChanged, filter, Subject, throttleTime } from 'rxjs'; +import { animationFrameScheduler, distinctUntilChanged, filter, map, mergeMap, Subject, take, throttleTime } from 'rxjs'; import { transformOffset2Bound } from './doc-popup-manager.service'; interface ICustomRangeBound { @@ -174,13 +174,22 @@ export class DocEventManagerService extends Disposable implements IRenderModule ); })); - this.disposeWithMe(this._context.mainComponent!.onPointerDown$.subscribeEvent((evt) => { - const ranges = this._calcActiveRanges(evt); + const onPointerDown$ = fromEventSubject(this._context.mainComponent!.onPointerDown$); + const onPointerUp$ = fromEventSubject(this._context.scene!.onPointerUp$); + this.disposeWithMe(onPointerDown$.pipe( + mergeMap((down) => onPointerUp$.pipe(take(1), map((up) => ({ down, up })))), + filter(({ down, up }) => down.target === up.target && up.timeStamp - down.timeStamp < 300) + // filter(({ down, up }) => down.offsetX === up.offsetX && down.offsetY === up.offsetY) + ).subscribe(({ down }) => { + if (down.button === 2) { + return; + } + const ranges = this._calcActiveRanges(down); if (ranges.length) { this._clickCustomRanges$.next(ranges.pop()!); } - const bullet = this._calcActiveBullet(evt); + const bullet = this._calcActiveBullet(down); if (bullet) { this._clickBullet$.next(bullet); } From ec0c68828da3508529cd0657b5a41c6c52fb8699 Mon Sep 17 00:00:00 2001 From: zw Date: Tue, 20 Aug 2024 16:23:28 +0800 Subject: [PATCH 35/43] feat: update --- .../commands/operations/popup.operation.ts | 22 ++++++------------- .../doc-hyper-link-selection.controller.ts | 4 ++-- .../src/services/hyper-link-popup.service.ts | 4 ++-- .../docs/src/basics/custom-range-factory.ts | 10 +++++---- 4 files changed, 17 insertions(+), 23 deletions(-) diff --git a/packages/docs-hyper-link-ui/src/commands/operations/popup.operation.ts b/packages/docs-hyper-link-ui/src/commands/operations/popup.operation.ts index 2da2b09fe01..6839b61006b 100644 --- a/packages/docs-hyper-link-ui/src/commands/operations/popup.operation.ts +++ b/packages/docs-hyper-link-ui/src/commands/operations/popup.operation.ts @@ -14,25 +14,17 @@ * limitations under the License. */ -import type { DocumentDataModel, IAccessor, ICommand } from '@univerjs/core'; +import type { DocumentDataModel, IAccessor, ICommand, ITextRange } from '@univerjs/core'; import { CommandType, CustomRangeType, IUniverInstanceService, UniverInstanceType } from '@univerjs/core'; -import { DocSkeletonManagerService, getCustomRangesInterestsWithRange, serializeDocRange, TextSelectionManagerService } from '@univerjs/docs'; -import { DocumentEditArea, IRenderManagerService } from '@univerjs/engine-render'; +import { getCustomRangesInterestsWithRange, TextSelectionManagerService } from '@univerjs/docs'; import { DocHyperLinkModel } from '@univerjs/docs-hyper-link'; import { DocHyperLinkPopupService } from '../../services/hyper-link-popup.service'; export const shouldDisableAddLink = (accessor: IAccessor) => { const textSelectionService = accessor.get(TextSelectionManagerService); const univerInstanceService = accessor.get(IUniverInstanceService); - const textRanges = textSelectionService.getCurrentTextRanges()?.map(serializeDocRange); - const renderManagerService = accessor.get(IRenderManagerService); - const render = renderManagerService.getCurrent(); - const skeleton = render?.with(DocSkeletonManagerService).getSkeleton(); - const editArea = skeleton?.getViewModel().getEditArea(); - if (editArea === DocumentEditArea.FOOTER || editArea === DocumentEditArea.HEADER) { - return true; - } - if (!textRanges || textRanges.length > 1) { + const textRanges = textSelectionService.getDocRanges(); + if (!textRanges.length || textRanges.length > 1) { return true; } @@ -42,7 +34,7 @@ export const shouldDisableAddLink = (accessor: IAccessor) => { return true; } - const body = doc.getBody(); + const body = doc.getSelfOrHeaderFooterModel(activeRange.segmentId).getBody(); const paragraphs = body?.paragraphs; if (!paragraphs) { return true; @@ -50,7 +42,7 @@ export const shouldDisableAddLink = (accessor: IAccessor) => { for (let i = 0, len = paragraphs.length; i < len; i++) { const p = paragraphs[i]; - if (activeRange.startOffset <= p.startIndex && activeRange.endOffset > p.startIndex) { + if (activeRange.startOffset! <= p.startIndex && activeRange.endOffset! > p.startIndex) { return true; } @@ -59,7 +51,7 @@ export const shouldDisableAddLink = (accessor: IAccessor) => { } } - const insertCustomRanges = getCustomRangesInterestsWithRange(activeRange, body.customRanges ?? []); + const insertCustomRanges = getCustomRangesInterestsWithRange(activeRange as ITextRange, body.customRanges ?? []); // can't insert hyperlink in range contains other custom ranges return !insertCustomRanges.every((range) => range.rangeType === CustomRangeType.HYPERLINK); }; diff --git a/packages/docs-hyper-link-ui/src/controllers/doc-hyper-link-selection.controller.ts b/packages/docs-hyper-link-ui/src/controllers/doc-hyper-link-selection.controller.ts index eefd4b15eae..db3f6fdb69a 100644 --- a/packages/docs-hyper-link-ui/src/controllers/doc-hyper-link-selection.controller.ts +++ b/packages/docs-hyper-link-ui/src/controllers/doc-hyper-link-selection.controller.ts @@ -17,8 +17,8 @@ import type { DocumentDataModel } from '@univerjs/core'; import { Disposable, ICommandService, Inject, IUniverInstanceService, LifecycleStages, OnLifecycle, UniverInstanceType } from '@univerjs/core'; import type { ISetTextSelectionsOperationParams } from '@univerjs/docs'; -import { DocSkeletonManagerService, SetTextSelectionsOperation } from '@univerjs/docs'; -import { DocumentEditArea, IRenderManagerService } from '@univerjs/engine-render'; +import { SetTextSelectionsOperation } from '@univerjs/docs'; +import { IRenderManagerService } from '@univerjs/engine-render'; import { DocHyperLinkPopupService } from '../services/hyper-link-popup.service'; @OnLifecycle(LifecycleStages.Ready, DocHyperLinkSelectionController) diff --git a/packages/docs-hyper-link-ui/src/services/hyper-link-popup.service.ts b/packages/docs-hyper-link-ui/src/services/hyper-link-popup.service.ts index 4140738e113..ab428ec5eaa 100644 --- a/packages/docs-hyper-link-ui/src/services/hyper-link-popup.service.ts +++ b/packages/docs-hyper-link-ui/src/services/hyper-link-popup.service.ts @@ -14,8 +14,8 @@ * limitations under the License. */ -import type { DocumentDataModel, IDisposable, ITextRange, ITextRangeParam, Nullable } from '@univerjs/core'; -import { Disposable, ICommandService, Inject, IUniverInstanceService, UniverInstanceType } from '@univerjs/core'; +import type { DocumentDataModel, IDisposable, ITextRangeParam, Nullable } from '@univerjs/core'; +import { Disposable, Inject, IUniverInstanceService, UniverInstanceType } from '@univerjs/core'; import { TextSelectionManagerService } from '@univerjs/docs'; import { DocCanvasPopManagerService } from '@univerjs/docs-ui'; import { BehaviorSubject } from 'rxjs'; diff --git a/packages/docs/src/basics/custom-range-factory.ts b/packages/docs/src/basics/custom-range-factory.ts index 568c294c0cd..924dd40cb59 100644 --- a/packages/docs/src/basics/custom-range-factory.ts +++ b/packages/docs/src/basics/custom-range-factory.ts @@ -19,6 +19,7 @@ import { DataStreamTreeTokenType, IUniverInstanceService, JSONX, TextX, TextXAct import type { IRichTextEditingMutationParams } from '../commands/mutations/core-editing.mutation'; import { RichTextEditingMutation } from '../commands/mutations/core-editing.mutation'; import { TextSelectionManagerService } from '../services/text-selection-manager.service'; +import { getRichTextEditPath } from '../commands/util'; import { getSelectionForAddCustomRange, normalizeSelection } from './selection'; interface IAddCustomRangeParam { @@ -105,7 +106,6 @@ export function addCustomRangeFactory(param: IAddCustomRangeParam, body: IDocume } interface IAddCustomRangeFactoryParam { - segmentId?: string; rangeId: string; rangeType: CustomRangeType; wholeEntity?: boolean; @@ -113,11 +113,12 @@ interface IAddCustomRangeFactoryParam { // eslint-disable-next-line max-lines-per-function export function addCustomRangeBySelectionFactory(accessor: IAccessor, param: IAddCustomRangeFactoryParam) { - const { segmentId, rangeId, rangeType, wholeEntity } = param; + const { rangeId, rangeType, wholeEntity } = param; const textSelectionManagerService = accessor.get(TextSelectionManagerService); const univerInstanceService = accessor.get(IUniverInstanceService); const selection = textSelectionManagerService.getActiveTextRangeWithStyle(); + const segmentId = selection?.segmentId; if (!selection) { return false; } @@ -126,7 +127,7 @@ export function addCustomRangeBySelectionFactory(accessor: IAccessor, param: IAd if (!documentDataModel) { return false; } - const body = documentDataModel.getBody(); + const body = documentDataModel.getSelfOrHeaderFooterModel(selection.segmentId).getBody(); const unitId = documentDataModel.getUnitId(); if (!body) { return false; @@ -232,7 +233,8 @@ export function addCustomRangeBySelectionFactory(accessor: IAccessor, param: IAd textRanges: undefined, }, }; - doMutation.params.actions = jsonX.editOp(textX.serialize()); + const path = getRichTextEditPath(documentDataModel, segmentId); + doMutation.params.actions = jsonX.editOp(textX.serialize(), path); return doMutation; } From a73ed30e4a3babfdef504045f643a99463408fdb Mon Sep 17 00:00:00 2001 From: zw Date: Tue, 20 Aug 2024 18:40:40 +0800 Subject: [PATCH 36/43] feat: update --- .../commands/commands/update-link.command.ts | 5 ++++- .../src/services/hyper-link-popup.service.ts | 4 +--- .../src/views/hyper-link-edit/index.tsx | 6 +++-- packages/docs/src/basics/replace.ts | 22 +++++++++++++------ .../commands/clipboard.inner.command.ts | 1 + .../services/popup/canvas-popup.service.ts | 3 ++- .../views/components/popup/CanvasPopup.tsx | 15 ++++++++----- 7 files changed, 36 insertions(+), 20 deletions(-) diff --git a/packages/docs-hyper-link-ui/src/commands/commands/update-link.command.ts b/packages/docs-hyper-link-ui/src/commands/commands/update-link.command.ts index b523290b81a..9957f1d44c2 100644 --- a/packages/docs-hyper-link-ui/src/commands/commands/update-link.command.ts +++ b/packages/docs-hyper-link-ui/src/commands/commands/update-link.command.ts @@ -24,6 +24,7 @@ export interface IUpdateDocHyperLinkCommandParams { linkId: string; payload: string; label: string; + segmentId: string; } export const UpdateDocHyperLinkCommand: ICommand = { @@ -33,13 +34,14 @@ export const UpdateDocHyperLinkCommand: ICommand { if (editing) { const linkDetail = editing ? hyperLinkModel.getLink(editing.unitId, editing.linkId) : null; setLink(linkDetail?.payload ?? ''); - const matchedRange = doc?.getSelfOrHeaderFooterModel(editing.segmentId)?.getBody()?.customRanges?.find((i) => linkDetail?.id === i.rangeId); + const body = doc?.getSelfOrHeaderFooterModel(editing.segmentId)?.getBody(); + const matchedRange = body?.customRanges?.find((i) => linkDetail?.id === i.rangeId); if (doc && matchedRange) { - setLabel(getPlainTextFormBody(getBodySlice(doc.getBody()!, matchedRange.startIndex, matchedRange.endIndex))); + setLabel(getPlainTextFormBody(getBodySlice(body!, matchedRange.startIndex, matchedRange.endIndex))); } return; } @@ -113,6 +114,7 @@ export const DocHyperLinkEdit = () => { payload: linkFinal, linkId: editing.linkId, label, + segmentId: editing.segmentId, }); } hyperLinkService.hideEditPopup(); diff --git a/packages/docs/src/basics/replace.ts b/packages/docs/src/basics/replace.ts index e130c68ad0b..2f60fc5eaa2 100644 --- a/packages/docs/src/basics/replace.ts +++ b/packages/docs/src/basics/replace.ts @@ -14,12 +14,13 @@ * limitations under the License. */ -import type { DocumentDataModel, IAccessor, IDeleteAction, IDocumentBody, IMutationInfo, IRetainAction, ITextRange } from '@univerjs/core'; +import type { DocumentDataModel, IAccessor, IDeleteAction, IDocumentBody, IMutationInfo, IRetainAction, ITextRange, ITextRangeParam } from '@univerjs/core'; import { IUniverInstanceService, JSONX, TextX, TextXActionType } from '@univerjs/core'; import type { ITextRangeWithStyle } from '@univerjs/engine-render'; import type { IRichTextEditingMutationParams } from '../commands/mutations/core-editing.mutation'; import { RichTextEditingMutation } from '../commands/mutations/core-editing.mutation'; import { TextSelectionManagerService } from '../services/text-selection-manager.service'; +import { getRichTextEditPath } from '../commands/util'; import { isIntersecting, shouldDeleteCustomRange } from './custom-range'; import { getDeleteSelection } from './selection'; @@ -48,7 +49,6 @@ export function getRetainAndDeleteAndExcludeLineBreak( const relativeCustomRanges = body.customRanges?.filter((customRange) => isIntersecting(customRange.startIndex, customRange.endIndex, startOffset, endOffset)); const toDeleteRanges = new Set(relativeCustomRanges?.filter((customRange) => shouldDeleteCustomRange(startOffset, endOffset - startOffset, customRange, dataStream))); const retainPoints = new Set(); - relativeCustomRanges?.forEach((range) => { if (toDeleteRanges.has(range)) { return; @@ -115,7 +115,7 @@ export function getRetainAndDeleteAndExcludeLineBreak( export interface IReplaceSelectionFactoryParams { unitId: string; - selection?: ITextRange; + selection?: ITextRangeParam; originBody?: IDocumentBody; @@ -128,11 +128,15 @@ export interface IReplaceSelectionFactoryParams { export function replaceSelectionFactory(accessor: IAccessor, params: IReplaceSelectionFactoryParams) { const { unitId, originBody, body: insertBody } = params; const univerInstanceService = accessor.get(IUniverInstanceService); + const docDataModel = univerInstanceService.getUnit(unitId); + if (!docDataModel) { + return false; + } + const segmentId = params.selection?.segmentId; let body: IDocumentBody | undefined; if (!params.originBody) { - const docDataModel = univerInstanceService.getUnit(unitId); - body = docDataModel?.getBody(); + body = docDataModel.getSelfOrHeaderFooterModel(segmentId)?.getBody(); } else { body = originBody; } @@ -148,6 +152,7 @@ export function replaceSelectionFactory(accessor: IAccessor, params: IReplaceSel startOffset: selection.startOffset + insertBody.dataStream.length, endOffset: selection.startOffset + insertBody.dataStream.length, collapsed: true, + segmentId, }]; const doMutation: IMutationInfo = { @@ -157,20 +162,23 @@ export function replaceSelectionFactory(accessor: IAccessor, params: IReplaceSel actions: [], textRanges, debounce: true, + segmentId, }, }; const textX = new TextX(); const jsonX = JSONX.getInstance(); // delete - textX.push(...getRetainAndDeleteAndExcludeLineBreak(selection, body)); + textX.push(...getRetainAndDeleteAndExcludeLineBreak(selection, body, segmentId)); // insert textX.push({ t: TextXActionType.INSERT, body: insertBody, len: insertBody.dataStream.length, line: 0, + segmentId, }); - doMutation.params.actions = jsonX.editOp(textX.serialize()); + const path = getRichTextEditPath(docDataModel, segmentId); + doMutation.params.actions = jsonX.editOp(textX.serialize(), path); return doMutation; } diff --git a/packages/docs/src/commands/commands/clipboard.inner.command.ts b/packages/docs/src/commands/commands/clipboard.inner.command.ts index 4b7f8730347..0079523cb5c 100644 --- a/packages/docs/src/commands/commands/clipboard.inner.command.ts +++ b/packages/docs/src/commands/commands/clipboard.inner.command.ts @@ -88,6 +88,7 @@ export const InnerPasteCommand: ICommand = { unitId, actions: [], textRanges, + segmentId, }, }; diff --git a/packages/ui/src/services/popup/canvas-popup.service.ts b/packages/ui/src/services/popup/canvas-popup.service.ts index 943a6d61675..b794f2888b7 100644 --- a/packages/ui/src/services/popup/canvas-popup.service.ts +++ b/packages/ui/src/services/popup/canvas-popup.service.ts @@ -14,6 +14,7 @@ * limitations under the License. */ +import type { Nullable } from '@univerjs/core'; import { createIdentifier, Disposable, Tools } from '@univerjs/core'; import type { IRectPopupProps } from '@univerjs/design'; import type { IBoundRectNoAngle } from '@univerjs/engine-render'; @@ -21,7 +22,7 @@ import type { Observable } from 'rxjs'; import { BehaviorSubject } from 'rxjs'; export interface IPopup extends Pick { - anchorRect: IBoundRectNoAngle; + anchorRect: Nullable; anchorRect$: Observable; excludeRects?: IBoundRectNoAngle[]; excludeRects$?: Observable; diff --git a/packages/ui/src/views/components/popup/CanvasPopup.tsx b/packages/ui/src/views/components/popup/CanvasPopup.tsx index ef9b86f6bb3..fdcee4991df 100644 --- a/packages/ui/src/views/components/popup/CanvasPopup.tsx +++ b/packages/ui/src/views/components/popup/CanvasPopup.tsx @@ -17,7 +17,6 @@ import { useDependency } from '@univerjs/core'; import React, { useEffect, useMemo, useState } from 'react'; import { RectPopup } from '@univerjs/design'; -import type { IBoundRectNoAngle } from '@univerjs/engine-render'; import { animationFrameScheduler, throttleTime } from 'rxjs'; import { ICanvasPopupService } from '../../../services/popup/canvas-popup.service'; import { useObservable } from '../../../components/hooks/observable'; @@ -35,11 +34,15 @@ const SingleCanvasPopup = ({ popup, children }: ISingleCanvasPopupProps) => { const excludeRects$ = useMemo(() => popup.excludeRects$?.pipe(throttleTime(0, animationFrameScheduler)), [popup.excludeRects$]); const anchorRect = useObservable(anchorRect$, popup.anchorRect); const excludeRects = useObservable(excludeRects$, popup.excludeRects); - const { bottom, left, right, top } = anchorRect; const { offset, canvasElement, hideOnInvisible = true } = popup; // We add an offset to the anchor rect to make the popup offset with the anchor. - const rectWithOffset: IBoundRectNoAngle = useMemo(() => { + const rectWithOffset = useMemo(() => { + if (!anchorRect) { + return null; + } + + const { bottom, left, right, top } = anchorRect; const [x = 0, y = 0] = offset ?? []; return { left: left - x, @@ -47,10 +50,10 @@ const SingleCanvasPopup = ({ popup, children }: ISingleCanvasPopupProps) => { top: top - y, bottom: bottom + y, }; - }, [bottom, left, right, top, offset]); + }, [anchorRect, offset]); useEffect(() => { - if (!hideOnInvisible) { + if (!hideOnInvisible || !rectWithOffset) { return; } const rect = canvasElement.getBoundingClientRect(); @@ -62,7 +65,7 @@ const SingleCanvasPopup = ({ popup, children }: ISingleCanvasPopupProps) => { } }, [rectWithOffset, canvasElement, hideOnInvisible]); - if (hidden) { + if (hidden || !rectWithOffset) { return null; } From 4e565e87f14f4a2b79a8305261ee56bd7fa81e28 Mon Sep 17 00:00:00 2001 From: zw Date: Wed, 21 Aug 2024 11:14:17 +0800 Subject: [PATCH 37/43] fix: delete link on header --- .../src/commands/commands/delete-link.command.ts | 5 +++-- .../src/views/hyper-link-popup/index.tsx | 3 ++- packages/docs/src/basics/custom-range-factory.ts | 13 +++++++++++-- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/packages/docs-hyper-link-ui/src/commands/commands/delete-link.command.ts b/packages/docs-hyper-link-ui/src/commands/commands/delete-link.command.ts index e114ce02be3..e3659ab8f0a 100644 --- a/packages/docs-hyper-link-ui/src/commands/commands/delete-link.command.ts +++ b/packages/docs-hyper-link-ui/src/commands/commands/delete-link.command.ts @@ -22,6 +22,7 @@ import { DocHyperLinkModel } from '@univerjs/docs-hyper-link'; export interface IDeleteDocHyperLinkMutationParams { unitId: string; linkId: string; + segmentId?: string; } export const DeleteDocHyperLinkCommand: ICommand = { @@ -31,7 +32,7 @@ export const DeleteDocHyperLinkCommand: ICommand { return null; } - const { unitId, linkId, segmentId, segmentPage } = currentPopup; + const { unitId, linkId, segmentId } = currentPopup; const link = hyperLinkModel.getLink(unitId, linkId); if (!link) { return null; @@ -92,6 +92,7 @@ export const DocLinkPopup = () => { commandService.executeCommand(DeleteDocHyperLinkCommand.id, { unitId, linkId: link.id, + segmentId, }); }} > diff --git a/packages/docs/src/basics/custom-range-factory.ts b/packages/docs/src/basics/custom-range-factory.ts index 924dd40cb59..673c8e58f4b 100644 --- a/packages/docs/src/basics/custom-range-factory.ts +++ b/packages/docs/src/basics/custom-range-factory.ts @@ -253,7 +253,7 @@ function deleteCustomRangeTextX(accessor: IAccessor, params: IDeleteCustomRangeP return false; } - const range = documentDataModel.getBody()?.customRanges?.find((r) => r.rangeId === rangeId); + const range = documentDataModel.getSelfOrHeaderFooterModel(segmentId).getBody()?.customRanges?.find((r) => r.rangeId === rangeId); if (!range) { return false; } @@ -297,12 +297,20 @@ function deleteCustomRangeTextX(accessor: IAccessor, params: IDeleteCustomRangeP } export function deleteCustomRangeFactory(accessor: IAccessor, params: IDeleteCustomRangeParam) { + const { unitId, segmentId } = params; + const univerInstanceService = accessor.get(IUniverInstanceService); + + const documentDataModel = univerInstanceService.getUnit(unitId); + if (!documentDataModel) { + return false; + } const doMutation: IMutationInfo = { id: RichTextEditingMutation.id, params: { unitId: params.unitId, actions: [], textRanges: undefined, + segmentId, }, }; @@ -312,6 +320,7 @@ export function deleteCustomRangeFactory(accessor: IAccessor, params: IDeleteCus return false; } - doMutation.params.actions = jsonX.editOp(textX.serialize()); + const path = getRichTextEditPath(documentDataModel, segmentId); + doMutation.params.actions = jsonX.editOp(textX.serialize(), path); return doMutation; } From 5b7cbaf705c4e9e39feb11ef89f964c894478b08 Mon Sep 17 00:00:00 2001 From: zw Date: Wed, 21 Aug 2024 11:23:58 +0800 Subject: [PATCH 38/43] fix: editor --- packages/docs-hyper-link-ui/src/views/hyper-link-edit/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/docs-hyper-link-ui/src/views/hyper-link-edit/index.tsx b/packages/docs-hyper-link-ui/src/views/hyper-link-edit/index.tsx index fe63446445b..79f6f38adf1 100644 --- a/packages/docs-hyper-link-ui/src/views/hyper-link-edit/index.tsx +++ b/packages/docs-hyper-link-ui/src/views/hyper-link-edit/index.tsx @@ -75,7 +75,7 @@ export const DocHyperLinkEdit = () => { return; } - const matchedRange = doc?.getBody()?.customRanges?.find((i) => Math.max(activeRange.startOffset, i.startIndex) <= Math.min(activeRange.endOffset - 1, i.endIndex)); + const matchedRange = doc?.getSelfOrHeaderFooterModel(activeRange.segmentId)?.getBody()?.customRanges?.find((i) => Math.max(activeRange.startOffset, i.startIndex) <= Math.min(activeRange.endOffset - 1, i.endIndex)); if (doc && matchedRange) { const linkDetail = hyperLinkModel.getLink(doc.getUnitId(), matchedRange.rangeId); setLink(linkDetail?.payload ?? ''); From 29604bcd3962b551abe6c10858c032d2a4b34da2 Mon Sep 17 00:00:00 2001 From: zw Date: Wed, 21 Aug 2024 11:45:17 +0800 Subject: [PATCH 39/43] feat: footer header not equal --- .../src/services/doc-event-manager.service.ts | 47 +++++++------------ 1 file changed, 18 insertions(+), 29 deletions(-) diff --git a/packages/docs-ui/src/services/doc-event-manager.service.ts b/packages/docs-ui/src/services/doc-event-manager.service.ts index 4db2fb73cd9..7c127eabc24 100644 --- a/packages/docs-ui/src/services/doc-event-manager.service.ts +++ b/packages/docs-ui/src/services/doc-event-manager.service.ts @@ -196,7 +196,7 @@ export class DocEventManagerService extends Disposable implements IRenderModule })); } - private _buildCustomRangeBoundsBySegment(segmentId?: string) { + private _buildCustomRangeBoundsBySegment(segmentId?: string, segmentPage = -1) { const customRanges = this._context.unit.getSelfOrHeaderFooterModel(segmentId)?.getBody()?.customRanges ?? []; const layouts: ICustomRangeBound[] = []; @@ -208,28 +208,17 @@ export class DocEventManagerService extends Disposable implements IRenderModule segmentId, }; - const calcRect = (pageIndex: number) => { - const rects = calcDocRangePositions(textRange, this._documents, this._skeleton, pageIndex); - if (!rects) { - return null; - } - - layouts.push({ - customRange: range, - rects, - segmentId, - segmentPageIndex: pageIndex, - }); - }; - - if (segmentId) { - const pageSize = (this._skeleton.getSkeletonData()?.pages.length ?? 0); - for (let i = 0; i < pageSize; i++) { - calcRect(i); - } - } else { - calcRect(-1); + const rects = calcDocRangePositions(textRange, this._documents, this._skeleton, segmentPage); + if (!rects) { + return null; } + + layouts.push({ + customRange: range, + rects, + segmentId, + segmentPageIndex: segmentPage, + }); }); return layouts; @@ -240,15 +229,15 @@ export class DocEventManagerService extends Disposable implements IRenderModule return; } this._customRangeDirty = false; + const customRangeBounds: ICustomRangeBound[] = []; - const headerKeys = Array.from(this._context.unit.headerModelMap.keys()); - const footerKeys = Array.from(this._context.unit.footerModelMap.keys()); + customRangeBounds.concat(this._buildCustomRangeBoundsBySegment()); + this._skeleton.getSkeletonData()?.pages.forEach((page, pageIndex) => { + customRangeBounds.concat(this._buildCustomRangeBoundsBySegment(page.headerId, pageIndex)); + customRangeBounds.concat(this._buildCustomRangeBoundsBySegment(page.footerId, pageIndex)); + }); - this._customRangeBounds = [ - ...this._buildCustomRangeBoundsBySegment(), - ...(headerKeys.map((key) => this._buildCustomRangeBoundsBySegment(key)).flat()), - ...(footerKeys.map((key) => this._buildCustomRangeBoundsBySegment(key)).flat()), - ]; + this._customRangeBounds = customRangeBounds; } private _calcActiveRanges(evt: IPointerEvent | IMouseEvent) { From 2599e4f16a11cbd04944d3c5fed5699717eaad18 Mon Sep 17 00:00:00 2001 From: zw Date: Wed, 21 Aug 2024 13:57:27 +0800 Subject: [PATCH 40/43] feat: update --- .../src/services/doc-event-manager.service.ts | 41 ++++++++++--------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/packages/docs-ui/src/services/doc-event-manager.service.ts b/packages/docs-ui/src/services/doc-event-manager.service.ts index 7c127eabc24..f9a508cd938 100644 --- a/packages/docs-ui/src/services/doc-event-manager.service.ts +++ b/packages/docs-ui/src/services/doc-event-manager.service.ts @@ -231,10 +231,15 @@ export class DocEventManagerService extends Disposable implements IRenderModule this._customRangeDirty = false; const customRangeBounds: ICustomRangeBound[] = []; - customRangeBounds.concat(this._buildCustomRangeBoundsBySegment()); + customRangeBounds.push(...this._buildCustomRangeBoundsBySegment()); this._skeleton.getSkeletonData()?.pages.forEach((page, pageIndex) => { - customRangeBounds.concat(this._buildCustomRangeBoundsBySegment(page.headerId, pageIndex)); - customRangeBounds.concat(this._buildCustomRangeBoundsBySegment(page.footerId, pageIndex)); + if (page.headerId) { + customRangeBounds.push(...this._buildCustomRangeBoundsBySegment(page.headerId, pageIndex)); + } + + if (page.footerId) { + customRangeBounds.push(...this._buildCustomRangeBoundsBySegment(page.footerId, pageIndex)); + } }); this._customRangeBounds = customRangeBounds; @@ -264,7 +269,7 @@ export class DocEventManagerService extends Disposable implements IRenderModule ); } - private _buildBulletBoundsBySegment(segmentId?: string) { + private _buildBulletBoundsBySegment(segmentId?: string, segmentPage = -1) { const body = this._context.unit.getSelfOrHeaderFooterModel(segmentId)?.getBody(); const paragraphs = body?.paragraphs ?? []; const bounds: IBulletBound[] = []; @@ -301,14 +306,7 @@ export class DocEventManagerService extends Disposable implements IRenderModule }); }; - if (segmentId) { - const pageSize = (this._skeleton.getSkeletonData()?.pages.length ?? 0); - for (let i = 0; i < pageSize; i++) { - calcRect(i); - } - } else { - calcRect(-1); - } + calcRect(segmentPage); } }); @@ -321,13 +319,18 @@ export class DocEventManagerService extends Disposable implements IRenderModule } this._bulletDirty = false; - const headerKeys = Array.from(this._context.unit.headerModelMap.keys()); - const footerKeys = Array.from(this._context.unit.footerModelMap.keys()); - this._bulletBounds = [ - ...this._buildBulletBoundsBySegment(), - ...(headerKeys.map((key) => this._buildBulletBoundsBySegment(key)).flat()), - ...(footerKeys.map((key) => this._buildBulletBoundsBySegment(key)).flat()), - ]; + this._bulletBounds = []; + this._bulletBounds.push(...this._buildBulletBoundsBySegment()); + + this._skeleton.getSkeletonData()?.pages.forEach((page, pageIndex) => { + if (page.headerId) { + this._bulletBounds.push(...this._buildBulletBoundsBySegment(page.headerId, pageIndex)); + } + + if (page.footerId) { + this._bulletBounds.push(...this._buildBulletBoundsBySegment(page.footerId, pageIndex)); + } + }); } private _calcActiveBullet(evt: IPointerEvent | IMouseEvent) { From ea05586c498438264fdb005c08e41a138f314467 Mon Sep 17 00:00:00 2001 From: zw Date: Wed, 21 Aug 2024 19:52:57 +0800 Subject: [PATCH 41/43] feat: update --- .../src/services/hyper-link-popup.service.ts | 2 +- .../src/services/doc-event-manager.service.ts | 12 ++++++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/packages/docs-hyper-link-ui/src/services/hyper-link-popup.service.ts b/packages/docs-hyper-link-ui/src/services/hyper-link-popup.service.ts index 2d5c226c12c..8545ce17185 100644 --- a/packages/docs-hyper-link-ui/src/services/hyper-link-popup.service.ts +++ b/packages/docs-hyper-link-ui/src/services/hyper-link-popup.service.ts @@ -26,7 +26,7 @@ import { DocLinkPopup } from '../views/hyper-link-popup'; /** * A link will have a placeholder, and when it is a link at the beginning, the placeholder does not have a width or height, causing an evaluation error */ -const SKIT_PLACEHOLDER = 2; +const SKIT_PLACEHOLDER = 0; export interface ILinkInfo { unitId: string; diff --git a/packages/docs-ui/src/services/doc-event-manager.service.ts b/packages/docs-ui/src/services/doc-event-manager.service.ts index f9a508cd938..f57de41bdba 100644 --- a/packages/docs-ui/src/services/doc-event-manager.service.ts +++ b/packages/docs-ui/src/services/doc-event-manager.service.ts @@ -37,8 +37,16 @@ interface IBulletBound { } const calcDocRangePositions = (range: ITextRangeParam, documents: Documents, skeleton: DocumentSkeleton, pageIndex: number): IBoundRectNoAngle[] | undefined => { - const startPosition = skeleton.findNodePositionByCharIndex(range.startOffset, true, range.segmentId, pageIndex); - const endPosition = skeleton.findNodePositionByCharIndex(range.endOffset, true, range.segmentId, pageIndex); + const startPosition = skeleton.findNodePositionByCharIndex(range.startOffset, false, range.segmentId, pageIndex); + const skeletonData = skeleton.getSkeletonData(); + let end = range.endOffset; + if (range.segmentId) { + const root = Array.from(skeletonData?.skeFooters.get(range.segmentId)?.values() ?? [])[0] ?? Array.from(skeletonData?.skeHeaders.get(range.segmentId)?.values() ?? [])[0]; + if (root) { + end = Math.min(root.ed, end); + } + } + const endPosition = skeleton.findNodePositionByCharIndex(end, false, range.segmentId, pageIndex); if (!endPosition || !startPosition) { return; } From 73e370c2f870c27c5a069f31dd72eebfaaa1f757 Mon Sep 17 00:00:00 2001 From: zw Date: Wed, 21 Aug 2024 19:58:47 +0800 Subject: [PATCH 42/43] feat: update --- .../src/views/hyper-link-popup/index.module.less | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/docs-hyper-link-ui/src/views/hyper-link-popup/index.module.less b/packages/docs-hyper-link-ui/src/views/hyper-link-popup/index.module.less index eeda3405e5a..9bb9f1857fc 100644 --- a/packages/docs-hyper-link-ui/src/views/hyper-link-popup/index.module.less +++ b/packages/docs-hyper-link-ui/src/views/hyper-link-popup/index.module.less @@ -9,6 +9,7 @@ border: 1px solid rgb(var(--grey-200)); background: rgb(var(--color-white)); max-width: 328px; + box-sizing: border-box; overflow: hidden; } From 47c7a499a27078779cab9b1eaa303c9d640f0d81 Mon Sep 17 00:00:00 2001 From: zw Date: Wed, 21 Aug 2024 20:41:23 +0800 Subject: [PATCH 43/43] feat: update --- .../docs/data-model/document-data-model.ts | 2 ++ .../doc-interceptor.service.ts | 27 ++++++++++++------- .../docs/view-model/document-view-model.ts | 9 ++++++- 3 files changed, 28 insertions(+), 10 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 a82f1e642b2..4d73dea3135 100644 --- a/packages/core/src/docs/data-model/document-data-model.ts +++ b/packages/core/src/docs/data-model/document-data-model.ts @@ -329,6 +329,7 @@ export class DocumentDataModel extends DocumentDataModelSimple { for (const headerId in headers) { const header = headers[headerId]; this.headerModelMap.set(headerId, new DocumentDataModel(header)); + this.headerModelMap.get(headerId)!.updateDocumentId(this.getUnitId()); } } @@ -336,6 +337,7 @@ export class DocumentDataModel extends DocumentDataModelSimple { for (const footerId in footers) { const footer = footers[footerId]; this.footerModelMap.set(footerId, new DocumentDataModel(footer)); + this.footerModelMap.get(footerId)!.updateDocumentId(this.getUnitId()); } } } diff --git a/packages/docs/src/services/doc-interceptor/doc-interceptor.service.ts b/packages/docs/src/services/doc-interceptor/doc-interceptor.service.ts index 724f049263f..fb75289e3dd 100644 --- a/packages/docs/src/services/doc-interceptor/doc-interceptor.service.ts +++ b/packages/docs/src/services/doc-interceptor/doc-interceptor.service.ts @@ -30,16 +30,14 @@ export class DocInterceptorService extends Disposable implements IRenderModule { ) { super(); - this.disposeWithMe(this._docSkeletonManagerService.currentViewModel$.subscribe((viewModel) => { - if (viewModel) { - const unitId = viewModel.getDataModel().getUnitId(); - if (unitId === DOCS_NORMAL_EDITOR_UNIT_ID_KEY || unitId === DOCS_FORMULA_BAR_EDITOR_UNIT_ID_KEY) { - return; - } + const viewModel = this._docSkeletonManagerService.getViewModel(); - this.interceptDocumentViewModel(viewModel); - } - })); + const unitId = viewModel.getDataModel().getUnitId(); + if (unitId === DOCS_NORMAL_EDITOR_UNIT_ID_KEY || unitId === DOCS_FORMULA_BAR_EDITOR_UNIT_ID_KEY) { + return; + } + + this.disposeWithMe(this.interceptDocumentViewModel(viewModel)); this.disposeWithMe(this.intercept(DOC_INTERCEPTOR_POINT.CUSTOM_RANGE, { priority: -1, @@ -47,6 +45,17 @@ export class DocInterceptorService extends Disposable implements IRenderModule { return next(data); }, })); + + let disposableCollection = new DisposableCollection(); + viewModel.segmentViewModels$.subscribe((segmentViewModels) => { + disposableCollection.dispose(); + disposableCollection = new DisposableCollection(); + segmentViewModels.forEach((segmentViewModel) => { + disposableCollection.add(this.interceptDocumentViewModel(segmentViewModel)); + }); + }); + + this.disposeWithMe(disposableCollection); } intercept>(name: T, interceptor: T) { diff --git a/packages/engine-render/src/components/docs/view-model/document-view-model.ts b/packages/engine-render/src/components/docs/view-model/document-view-model.ts index bb2b4a4e91a..b16d6c2ed9e 100644 --- a/packages/engine-render/src/components/docs/view-model/document-view-model.ts +++ b/packages/engine-render/src/components/docs/view-model/document-view-model.ts @@ -175,6 +175,9 @@ export class DocumentViewModel implements IDisposable { headerTreeMap: Map = new Map(); footerTreeMap: Map = new Map(); + private readonly _segmentViewModels$ = new BehaviorSubject([]); + readonly segmentViewModels$ = this._segmentViewModels$.asObservable(); + constructor(private _documentDataModel: DocumentDataModel) { if (_documentDataModel.getBody() == null) { return; @@ -607,14 +610,18 @@ export class DocumentViewModel implements IDisposable { private _buildHeaderFooterViewModel() { const { headerModelMap, footerModelMap } = this._documentDataModel; - + const viewModels = []; for (const [headerId, headerModel] of headerModelMap) { this.headerTreeMap.set(headerId, new DocumentViewModel(headerModel)); + viewModels.push(this.headerTreeMap.get(headerId)!); } for (const [footerId, footerModel] of footerModelMap) { this.footerTreeMap.set(footerId, new DocumentViewModel(footerModel)); + viewModels.push(this.footerTreeMap.get(footerId)!); } + + this._segmentViewModels$.next(viewModels); } private _getParagraphByIndex(nodes: DataStreamTreeNode[], insertIndex: number): Nullable {