From a4e177a1407c674277e7d95ccccf823795502000 Mon Sep 17 00:00:00 2001 From: WEI ZHANG Date: Thu, 10 Jul 2025 22:13:03 +0800 Subject: [PATCH] feat: add float-dom update & delete & event api (#5515) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 白熱 Co-authored-by: Wpxp123456 <2677556700@qq.com> --- .../src/basics/transform-position.ts | 1 + .../sheets-drawing-ui/src/facade/f-event.ts | 209 +++++++++- .../sheets-drawing-ui/src/facade/f-univer.ts | 204 +++++++++- .../src/facade/f-worksheet.ts | 373 +++++++++++++++++- packages/sheets-drawing-ui/src/index.ts | 4 +- .../canvas-float-dom-manager.service.ts | 29 +- .../services/dom/canvas-dom-layer.service.ts | 13 + 7 files changed, 802 insertions(+), 31 deletions(-) diff --git a/packages/sheets-drawing-ui/src/basics/transform-position.ts b/packages/sheets-drawing-ui/src/basics/transform-position.ts index 4163f963a8..a92d496bed 100644 --- a/packages/sheets-drawing-ui/src/basics/transform-position.ts +++ b/packages/sheets-drawing-ui/src/basics/transform-position.ts @@ -20,6 +20,7 @@ import type { ISheetSelectionRenderService, SheetSkeletonManagerService } from ' import { precisionTo } from '@univerjs/engine-render'; import { convertPositionSheetOverGridToAbsolute } from '@univerjs/sheets-ui'; +// eslint-disable-next-line max-lines-per-function export function drawingPositionToTransform( position: ISheetDrawingPosition, selectionRenderService: ISheetSelectionRenderService, diff --git a/packages/sheets-drawing-ui/src/facade/f-event.ts b/packages/sheets-drawing-ui/src/facade/f-event.ts index 3c397f21c8..9e8d8012ec 100644 --- a/packages/sheets-drawing-ui/src/facade/f-event.ts +++ b/packages/sheets-drawing-ui/src/facade/f-event.ts @@ -16,7 +16,7 @@ import type { IDrawingSearch } from '@univerjs/core'; import type { IEventBase } from '@univerjs/core/facade'; -import type { ISheetImage } from '@univerjs/sheets-drawing'; +import type { ISheetFloatDom, ISheetImage } from '@univerjs/sheets-drawing'; import type { FWorkbook } from '@univerjs/sheets/facade'; import type { FOverGridImage } from './f-over-grid-image'; import { FEventName } from '@univerjs/core/facade'; @@ -25,6 +25,108 @@ import { FEventName } from '@univerjs/core/facade'; * @ignore */ interface IFDrawingEventNameMixin { + /** + * Triggered before float dom insertion. + * @see {@link IBeforeFloatDomAddParam} + * @example + * ```ts + * const disposable = univerAPI.addEvent(univerAPI.Event.BeforeFloatDomAdd, (params) => { + * console.log(params); + * // do something + * const { workbook, drawings } = params; + * // Cancel the insertion operation + * params.cancel = true; + * }) + * + * // Remove the event listener, use `disposable.dispose()` + * ``` + */ + readonly BeforeFloatDomAdd: 'BeforeFloatDomAdd'; + + /** + * Triggered after float dom insertion. + * @see {@link IFloatDomAddedParam} + * @example + * ```ts + * const disposable = univerAPI.addEvent(univerAPI.Event.FloatDomAdded, (params) => { + * console.log(params); + * // do something + * const { workbook, drawings } = params; + * }) + * + * // Remove the event listener, use `disposable.dispose()` + * ``` + */ + readonly FloatDomAdded: 'FloatDomAdded'; + + /** + * Triggered before float dom update. + * @see {@link IBeforeFloatDomUpdateParam} + * @example + * ```ts + * const disposable = univerAPI.addEvent(univerAPI.Event.BeforeFloatDomUpdate, (params) => { + * console.log(params); + * // do something + * const { workbook, drawings } = params; + * // Cancel the update operation + * params.cancel = true; + * }) + * + * // Remove the event listener, use `disposable.dispose()` + * ``` + */ + readonly BeforeFloatDomUpdate: 'BeforeFloatDomUpdate'; + + /** + * Triggered after float dom update. + * @see {@link IFloatDomUpdatedParam} + * @example + * ```ts + * const disposable = univerAPI.addEvent(univerAPI.Event.FloatDomUpdated, (params) => { + * console.log(params); + * // do something + * const { workbook, drawings } = params; + * }) + * + * // Remove the event listener, use `disposable.dispose()` + * ``` + */ + readonly FloatDomUpdated: 'FloatDomUpdated'; + + /** + * Triggered before float dom deletion. + * @see {@link IBeforeFloatDomDeleteParam} + * @example + * ```ts + * const disposable = univerAPI.addEvent(univerAPI.Event.BeforeFloatDomDelete, (params) => { + * console.log(params); + * // do something + * const { workbook, drawings } = params; + * // Cancel the deletion operation + * params.cancel = true; + * }) + * + * // Remove the event listener, use `disposable.dispose()` + * ``` + */ + readonly BeforeFloatDomDelete: 'BeforeFloatDomDelete'; + + /** + * Triggered after float dom deletion. + * @see {@link IFloatDomDeletedParam} + * @example + * ```ts + * const disposable = univerAPI.addEvent(univerAPI.Event.FloatDomDeleted, (params) => { + * console.log(params); + * // do something + * const { workbook, drawings } = params; + * }) + * + * // Remove the event listener, use `disposable.dispose()` + * ``` + */ + readonly FloatDomDeleted: 'FloatDomDeleted'; + /** * Triggered before floating image insertion. * @see {@link IBeforeOverGridImageInsertParam} @@ -163,6 +265,30 @@ interface IFDrawingEventNameMixin { } export class FDrawingEventNameMixin extends FEventName implements IFDrawingEventNameMixin { + override get BeforeFloatDomAdd(): 'BeforeFloatDomAdd' { + return 'BeforeFloatDomAdd' as const; + } + + override get FloatDomAdded(): 'FloatDomAdded' { + return 'FloatDomAdded' as const; + } + + override get BeforeFloatDomUpdate(): 'BeforeFloatDomUpdate' { + return 'BeforeFloatDomUpdate' as const; + } + + override get FloatDomUpdated(): 'FloatDomUpdated' { + return 'FloatDomUpdated' as const; + } + + override get BeforeFloatDomDelete(): 'BeforeFloatDomDelete' { + return 'BeforeFloatDomDelete' as const; + } + + override get FloatDomDeleted(): 'FloatDomDeleted' { + return 'FloatDomDeleted' as const; + } + override get BeforeOverGridImageChange(): 'BeforeOverGridImageChange' { return 'BeforeOverGridImageChange' as const; } @@ -307,6 +433,78 @@ export interface IOverGridImageSelectedParam extends IEventBase { /** * @ignore */ +export interface IBeforeFloatDomAddParam extends IEventBase { + /** + * The workbook instance currently being operated on. {@link FWorkbook} + */ + workbook: FWorkbook; + + /** + * Collection of float dom drawings to be added. + */ + drawings: ISheetFloatDom[]; +} + +export interface IFloatDomAddedParam extends IEventBase { + /** + * The workbook instance currently being operated on. {@link FWorkbook} + */ + workbook: FWorkbook; + + /** + * Collection of float dom drawings that were added. + */ + drawings: ISheetFloatDom[]; +} + +export interface IBeforeFloatDomUpdateParam extends IEventBase { + /** + * The workbook instance currently being operated on. {@link FWorkbook} + */ + workbook: FWorkbook; + + /** + * Collection of float dom drawings to be updated. + */ + drawings: ISheetFloatDom[]; +} + +export interface IFloatDomUpdatedParam extends IEventBase { + /** + * The workbook instance currently being operated on. {@link FWorkbook} + */ + workbook: FWorkbook; + + /** + * Collection of float dom drawings that were updated. + */ + drawings: ISheetFloatDom[]; +} + +export interface IBeforeFloatDomDeleteParam extends IEventBase { + /** + * The workbook instance currently being operated on. {@link FWorkbook} + */ + workbook: FWorkbook; + + /** + * Collection of float dom drawings to be deleted. + */ + drawings: ISheetFloatDom[]; +} + +export interface IFloatDomDeletedParam extends IEventBase { + /** + * The workbook instance currently being operated on. {@link FWorkbook} + */ + workbook: FWorkbook; + + /** + * Collection of float dom drawing ids that were deleted. + */ + drawings: string[]; +} + interface IFSheetsUIEventParamConfig { BeforeOverGridImageChange: IBeforeOverGridImageChangeParam; OverGridImageChanged: IOverGridImageChangedParam; @@ -319,6 +517,15 @@ interface IFSheetsUIEventParamConfig { BeforeOverGridImageSelect: IBeforeOverGridImageSelectParam; OverGridImageSelected: IOverGridImageSelectedParam; + + BeforeFloatDomAdd: IBeforeFloatDomAddParam; + FloatDomAdded: IFloatDomAddedParam; + + BeforeFloatDomUpdate: IBeforeFloatDomUpdateParam; + FloatDomUpdated: IFloatDomUpdatedParam; + + BeforeFloatDomDelete: IBeforeFloatDomDeleteParam; + FloatDomDeleted: IFloatDomDeletedParam; } FEventName.extend(FDrawingEventNameMixin); diff --git a/packages/sheets-drawing-ui/src/facade/f-univer.ts b/packages/sheets-drawing-ui/src/facade/f-univer.ts index ae763a7d41..16207e384b 100644 --- a/packages/sheets-drawing-ui/src/facade/f-univer.ts +++ b/packages/sheets-drawing-ui/src/facade/f-univer.ts @@ -16,11 +16,16 @@ import type { IDrawingSearch, Injector } from '@univerjs/core'; import type { IEventBase } from '@univerjs/core/facade'; -import type { ISheetImage } from '@univerjs/sheets-drawing'; +import type { ISheetFloatDom, ISheetImage } from '@univerjs/sheets-drawing'; import type { IDeleteDrawingCommandParams, IInsertDrawingCommandParams, ISetDrawingCommandParams } from '@univerjs/sheets-drawing-ui'; import type { FWorkbook } from '@univerjs/sheets/facade'; -import type { IBeforeOverGridImageChangeParamObject } from './f-event'; -import { CanceledError, ICommandService } from '@univerjs/core'; +import type { + IBeforeFloatDomAddParam, + IBeforeFloatDomDeleteParam, + IBeforeFloatDomUpdateParam, + IBeforeOverGridImageChangeParamObject, +} from './f-event'; +import { CanceledError, DrawingTypeEnum, ICommandService } from '@univerjs/core'; import { FUniver } from '@univerjs/core/facade'; import { IDrawingManagerService, SetDrawingSelectedOperation } from '@univerjs/drawing'; import { InsertSheetDrawingCommand, RemoveSheetDrawingCommand, SetSheetDrawingCommand } from '@univerjs/sheets-drawing-ui'; @@ -58,6 +63,67 @@ export class FUniverDrawingMixin extends FUniver { override _initialize(injector: Injector): void { const commandService = injector.get(ICommandService); + // Float DOM Add Events + this.registerEventHandler( + this.Event.BeforeFloatDomAdd, + () => commandService.beforeCommandExecuted((commandInfo) => { + if (commandInfo.id !== InsertSheetDrawingCommand.id) return; + + const params = commandInfo.params as IInsertDrawingCommandParams; + const workbook = this.getActiveWorkbook(); + if (workbook == null || params == null) { + return; + } + + const { drawings } = params; + const floatDomDrawings = drawings.filter( + (drawing) => drawing.drawingType === DrawingTypeEnum.DRAWING_DOM + ) as ISheetFloatDom[]; + + if (floatDomDrawings.length === 0) { + return; + } + + const eventParams: IBeforeFloatDomAddParam = { + workbook, + drawings: floatDomDrawings, + }; + + this.fireEvent(this.Event.BeforeFloatDomAdd, eventParams); + + if (eventParams.cancel) { + throw new CanceledError(); + } + }) + ); + + this.registerEventHandler( + this.Event.FloatDomAdded, + () => commandService.onCommandExecuted((commandInfo) => { + if (commandInfo.id !== InsertSheetDrawingCommand.id) return; + + const params = commandInfo.params as IInsertDrawingCommandParams; + const workbook = this.getActiveWorkbook(); + if (workbook == null || params == null) { + return; + } + + const { drawings } = params; + const floatDomDrawings = drawings.filter( + (drawing) => drawing.drawingType === DrawingTypeEnum.DRAWING_DOM + ) as ISheetFloatDom[]; + + if (floatDomDrawings.length === 0) { + return; + } + + this.fireEvent(this.Event.FloatDomAdded, { + workbook, + drawings: floatDomDrawings, + }); + }) + ); + this.registerEventHandler( this.Event.BeforeOverGridImageInsert, () => commandService.beforeCommandExecuted((commandInfo) => { @@ -155,6 +221,138 @@ export class FUniverDrawingMixin extends FUniver { }) ); + // Float DOM Update Events + this.registerEventHandler( + this.Event.BeforeFloatDomUpdate, + () => commandService.beforeCommandExecuted((commandInfo) => { + if (commandInfo.id !== SetSheetDrawingCommand.id) return; + + const params = commandInfo.params as ISetDrawingCommandParams; + const workbook = this.getActiveWorkbook(); + if (workbook == null || params == null) { + return; + } + + const { drawings } = params; + const drawingManagerService = injector.get(IDrawingManagerService); + + const floatDomDrawings: ISheetFloatDom[] = []; + drawings.forEach((drawing) => { + const dom = drawingManagerService.getDrawingByParam(drawing as IDrawingSearch) as ISheetFloatDom; + if (dom?.drawingType === DrawingTypeEnum.DRAWING_DOM) { + floatDomDrawings.push(dom); + } + }); + + if (floatDomDrawings.length === 0) { + return; + } + + const eventParams: IBeforeFloatDomUpdateParam = { + workbook, + drawings: floatDomDrawings, + }; + + this.fireEvent(this.Event.BeforeFloatDomUpdate, eventParams); + + if (eventParams.cancel) { + drawingManagerService.updateNotification(drawings as IDrawingSearch[]); + throw new CanceledError(); + } + }) + ); + + this.registerEventHandler( + this.Event.FloatDomUpdated, + () => commandService.onCommandExecuted((commandInfo) => { + if (commandInfo.id !== SetSheetDrawingCommand.id) return; + + const params = commandInfo.params as ISetDrawingCommandParams; + const workbook = this.getActiveWorkbook(); + if (workbook == null || params == null) { + return; + } + + const { drawings } = params; + const drawingManagerService = injector.get(IDrawingManagerService); + + const floatDomDrawings: ISheetFloatDom[] = []; + drawings.forEach((drawing) => { + const dom = drawingManagerService.getDrawingByParam(drawing as IDrawingSearch) as ISheetFloatDom; + if (dom?.drawingType === DrawingTypeEnum.DRAWING_DOM) { + floatDomDrawings.push(dom); + } + }); + + if (floatDomDrawings.length === 0) { + return; + } + + this.fireEvent(this.Event.FloatDomUpdated, { + workbook, + drawings: floatDomDrawings, + }); + }) + ); + + // Float DOM Delete Events + this.registerEventHandler( + this.Event.BeforeFloatDomDelete, + () => commandService.beforeCommandExecuted((commandInfo) => { + if (commandInfo.id !== RemoveSheetDrawingCommand.id) return; + + const params = commandInfo.params as IDeleteDrawingCommandParams; + const workbook = this.getActiveWorkbook(); + if (workbook == null || params == null) { + return; + } + + const drawingManagerService = injector.get(IDrawingManagerService); + + const { drawings } = params; + const floatDomDrawings = drawings + .map((drawing) => drawingManagerService.getDrawingByParam(drawing)) + .filter((drawing): drawing is ISheetFloatDom => + drawing?.drawingType === DrawingTypeEnum.DRAWING_DOM + ); + + if (floatDomDrawings.length === 0) { + return; + } + + const eventParams: IBeforeFloatDomDeleteParam = { + workbook, + drawings: floatDomDrawings, + }; + + this.fireEvent(this.Event.BeforeFloatDomDelete, eventParams); + + if (eventParams.cancel) { + throw new CanceledError(); + } + }) + ); + + this.registerEventHandler( + this.Event.FloatDomDeleted, + () => commandService.onCommandExecuted((commandInfo) => { + if (commandInfo.id !== RemoveSheetDrawingCommand.id) return; + + const params = commandInfo.params as IDeleteDrawingCommandParams; + const workbook = this.getActiveWorkbook(); + if (workbook == null || params == null) { + return; + } + + const { drawings } = params; + + this.fireEvent(this.Event.FloatDomDeleted, { + workbook, + drawings: drawings.filter((i) => i.drawingType === DrawingTypeEnum.DRAWING_DOM).map((i) => i.drawingId), + }); + }) + ); + this.registerEventHandler( this.Event.BeforeOverGridImageSelect, () => commandService.beforeCommandExecuted((commandInfo) => { diff --git a/packages/sheets-drawing-ui/src/facade/f-worksheet.ts b/packages/sheets-drawing-ui/src/facade/f-worksheet.ts index 6821240554..7c1c479025 100644 --- a/packages/sheets-drawing-ui/src/facade/f-worksheet.ts +++ b/packages/sheets-drawing-ui/src/facade/f-worksheet.ts @@ -14,27 +14,164 @@ * limitations under the License. */ -import type { IDisposable, Nullable } from '@univerjs/core'; +import type { IDisposable, ITransformState, Nullable } from '@univerjs/core'; import type { IFBlobSource } from '@univerjs/core/facade'; -import type { ISheetImage } from '@univerjs/sheets-drawing'; -import type { ICanvasFloatDom, IDOMAnchor } from '@univerjs/sheets-drawing-ui'; +import type { ISheetFloatDom, ISheetImage } from '@univerjs/sheets-drawing'; +import type { ICanvasFloatDom, ICanvasFloatDomInfo, IDOMAnchor } from '@univerjs/sheets-drawing-ui'; import type { IFComponentKey } from '@univerjs/sheets-ui/facade'; import type { FRange } from '@univerjs/sheets/facade'; import { DrawingTypeEnum, ImageSourceType, toDisposable } from '@univerjs/core'; +import { IRenderManagerService } from '@univerjs/engine-render'; import { ISheetDrawingService } from '@univerjs/sheets-drawing'; -import { InsertSheetDrawingCommand, RemoveSheetDrawingCommand, SetSheetDrawingCommand, SheetCanvasFloatDomManagerService } from '@univerjs/sheets-drawing-ui'; +import { InsertSheetDrawingCommand, RemoveSheetDrawingCommand, SetSheetDrawingCommand, SheetCanvasFloatDomManagerService, transformToDrawingPosition } from '@univerjs/sheets-drawing-ui'; +import { ISheetSelectionRenderService } from '@univerjs/sheets-ui'; import { transformComponentKey } from '@univerjs/sheets-ui/facade'; import { FWorksheet } from '@univerjs/sheets/facade'; import { ComponentManager } from '@univerjs/ui'; import { FOverGridImage, FOverGridImageBuilder } from './f-over-grid-image'; // why omit this key? if componentKey is missing, then which component should be used? -export interface IFICanvasFloatDom extends Omit, IFComponentKey {} +export interface IFICanvasFloatDom extends Omit, IFComponentKey { } + +export interface IFCanvasFloatDomResult extends Omit, + IFComponentKey, + Pick { + position: ITransformState; + id: string; +} /** * @ignore */ export interface IFWorksheetLegacy { + /** + * Get float dom by id + * @param {string} id - float dom id + * @returns {IFCanvasFloatDomResult | null} float dom info or null if not found + * @example + * ```ts + * const fWorksheet = univerAPI.getActiveWorkbook().getActiveSheet(); + * const floatDom = fWorksheet.getFloatDomById('myFloatDomId'); + * if (floatDom) { + * console.log('Float dom position:', floatDom.position); + * console.log('Component key:', floatDom.componentKey); + * console.log('Custom data:', floatDom.data); + * } + * ``` + */ + getFloatDomById(id: string): Nullable; + + /** + * Get all float doms in current worksheet + * @returns {IFCanvasFloatDomResult[]} array of float dom info + * @example + * ```ts + * const fWorksheet = univerAPI.getActiveWorkbook().getActiveSheet(); + * const allFloatDoms = fWorksheet.getAllFloatDoms(); + * allFloatDoms.forEach(floatDom => { + * console.log('Float dom ID:', floatDom.id); + * console.log('Position:', floatDom.position); + * }); + * ``` + */ + getAllFloatDoms(): IFCanvasFloatDomResult[]; + + /** + * Update float dom position and properties + * @param {string} id - float dom id + * @param {Partial} config - new float dom config + * @returns {FWorksheet} The worksheet instance for chaining + * @example + * ```ts + * const fWorksheet = univerAPI.getActiveWorkbook().getActiveSheet(); + * const firstFloatDom = fWorksheet.getAllFloatDoms()[0]; + * + * if (!firstFloatDom) return; + * + * // Update first float dom position and size + * fWorksheet.updateFloatDom(firstFloatDom.id, { + * position: { + * left: 100, + * top: 100, + * width: 200, + * height: 150, + * angle: 45, // rotate 45 degrees + * } + * }); + * + * // Update first float dom data + * fWorksheet.updateFloatDom(firstFloatDom.id, { + * data: { + * label: 'Updated Label', + * color: '#ff0000' + * } + * }); + * + * // Disable the first float dom from transform + * fWorksheet.updateFloatDom(firstFloatDom.id, { + * allowTransform: false + * }); + * ``` + */ + updateFloatDom(id: string, config: Partial): this; + + /** + * Batch update float doms + * @param {Array<{id: string, config: Partial}>} updates - array of update configs + * @returns {FWorksheet} The worksheet instance for chaining + * @example + * ```ts + * const fWorksheet = univerAPI.getActiveWorkbook().getActiveSheet(); + * + * // Update multiple float doms at once + * const allFloatDoms = fWorksheet.getAllFloatDoms(); + * fWorksheet.batchUpdateFloatDoms(allFloatDoms.map((floatDom, index) => { + * if (floatDom.id === 'MyFloatDomId') { + * return { + * id: floatDom.id, + * config: { + * position: { + * left: 100, + * top: 100 + * }, + * data: { + * label: 'Updated' + * } + * } + * } + * } + * + * return { + * id: floatDom.id, + * config: { + * position: { + * left: 300, + * top: 100 + * } + * } + * } + * })); + * ``` + */ + batchUpdateFloatDoms(updates: Array<{ id: string; config: Partial }>): this; + + /** + * Remove float dom by id + * @param {string} id - float dom id + * @returns {FWorksheet} The worksheet instance for chaining + * @example + * ```ts + * const fWorksheet = univerAPI.getActiveWorkbook().getActiveSheet(); + * const firstFloatDom = fWorksheet.getAllFloatDoms()[0]; + * + * if (!firstFloatDom) return; + * + * // Remove the first float dom + * fWorksheet.removeFloatDom(firstFloatDom.id); + * ``` + */ + removeFloatDom(id: string): this; + /** * Add a float dom to position. * @param {IFICanvasFloatDom} layer - The float dom layer configuration. @@ -59,7 +196,7 @@ export interface IFWorksheetLegacy { * * // Add a floating DOM * // If disposable is null, floating DOM addition failed - * const disposeable = fWorksheet.addFloatDomToPosition({ + * const disposable = fWorksheet.addFloatDomToPosition({ * componentKey: 'myFloatDom', * initPosition: { * startX: 100, @@ -73,10 +210,11 @@ export interface IFWorksheetLegacy { * label: 'hahah', * }, * }); + * console.log(disposable?.id); // The id of the floating DOM * * // Remove the floating DOM after 2 seconds * setTimeout(() => { - * disposeable?.dispose(); + * disposable?.dispose(); * }, 2000); * ``` */ @@ -121,11 +259,12 @@ export interface IFWorksheetLegacy { * * // Add the range loading component covering the range A1:C3 * const fRange = fWorksheet.getRange('A1:C3'); - * const disposeable = fWorksheet.addFloatDomToRange(fRange, { componentKey: 'RangeLoading' }, {}, 'myRangeLoading'); + * const disposable = fWorksheet.addFloatDomToRange(fRange, { componentKey: 'RangeLoading' }, {}, 'myRangeLoading'); + * console.log(disposable?.id); // The id of the floating DOM * * // Remove the floating DOM after 2 seconds * setTimeout(() => { - * disposeable?.dispose(); + * disposable?.dispose(); * }, 2000); * * // another example------------------- @@ -158,7 +297,7 @@ export interface IFWorksheetLegacy { * * // Add the float button to the range A5:C7, position is start from A5 cell, and width is 100px, height is 30px, margin is 100% of range width and height * const fRange2 = fWorksheet.getRange('A5:C7'); - * const disposeable2 = fWorksheet.addFloatDomToRange( + * const disposable2 = fWorksheet.addFloatDomToRange( * fRange2, * { * componentKey: 'FloatButton', @@ -171,6 +310,7 @@ export interface IFWorksheetLegacy { * }, * 'myFloatButton' * ); + * console.log(disposable2?.id); // The id of the floating DOM * ``` */ addFloatDomToRange(range: FRange, layer: Partial, domLayout: Partial, id?: string): Nullable<{ @@ -217,7 +357,7 @@ export interface IFWorksheetLegacy { * univerAPI.registerComponent('FloatButton', FloatButton); * * // Add the float button to the column D header, position is right align, width is 100px, height is 30px, margin is 0 - * const disposeable = fWorksheet.addFloatDomToColumnHeader( + * const disposable = fWorksheet.addFloatDomToColumnHeader( * 3, * { * componentKey: 'FloatButton', @@ -232,10 +372,11 @@ export interface IFWorksheetLegacy { * }, * 'myFloatButton' * ); + * console.log(disposable?.id); // The id of the floating DOM * * // Remove the float button after 2 seconds * setTimeout(() => { - * disposeable?.dispose(); + * disposable?.dispose(); * }, 2000); * ``` */ @@ -450,6 +591,212 @@ export interface IFWorksheetLegacy { } export class FWorksheetLegacy extends FWorksheet implements IFWorksheetLegacy { + override getFloatDomById(id: string): Nullable { + const floatDomService = this._injector.get(SheetCanvasFloatDomManagerService); + const info = floatDomService.getFloatDomInfo(id); + if (!info) return null; + + const { unitId, subUnitId } = info; + const { rect } = info; + const state = rect.getState(); + const { left = 0, top = 0, width = 0, height = 0, flipX = false, flipY = false, angle = 0, skewX = 0, skewY = 0 } = state; + const drawingParm = this._injector.get(ISheetDrawingService).getDrawingByParam({ + drawingId: info.id, + unitId, + subUnitId, + })! as ISheetFloatDom; + + if (!drawingParm) return null; + + return { + position: { + left, + top, + width, + height, + flipX, + flipY, + angle, + skewX, + skewY, + }, + componentKey: drawingParm.componentKey, + allowTransform: drawingParm.allowTransform, + data: drawingParm.data, + id: info.id, + }; + } + + override getAllFloatDoms(): IFCanvasFloatDomResult[] { + const floatDomService = this._injector.get(SheetCanvasFloatDomManagerService); + const unitId = this._workbook.getUnitId(); + const subUnitId = this._worksheet.getSheetId(); + + return Array.from(floatDomService.getFloatDomsBySubUnitId(unitId, subUnitId).values()) + .map((info) => { + const { rect } = info; + const drawingParm = this._injector.get(ISheetDrawingService).getDrawingByParam({ + drawingId: info.id, + unitId, + subUnitId, + })! as ISheetFloatDom; + + const { left, top, width, height, flipX, flipY, angle, skewX, skewY } = rect.getState(); + + return { + position: { + left, + top, + width, + height, + flipX, + flipY, + angle, + skewX, + skewY, + }, + componentKey: drawingParm.componentKey, + allowTransform: drawingParm.allowTransform, + data: drawingParm.data, + id: info.id, + }; + }); + } + + override updateFloatDom(id: string, config: Partial>): this { + const floatDomService = this._injector.get(SheetCanvasFloatDomManagerService); + const info = floatDomService.getFloatDomInfo(id); + if (!info) return this; + + const { unitId, subUnitId } = info; + + const drawingParm = this._injector.get(ISheetDrawingService).getDrawingByParam({ + unitId, + subUnitId, + drawingId: id, + })! as ISheetFloatDom; + + const renderManagerService = this._injector.get(IRenderManagerService); + const render = renderManagerService.getRenderById(unitId); + if (!render) return this; + + const skeleton = this.getSkeleton(); + if (!skeleton) return this; + + const selectionRenderService = renderManagerService.getRenderById(this.getWorkbook().getUnitId())?.with(ISheetSelectionRenderService); + if (!selectionRenderService) return this; + + const newParam: ISheetFloatDom = { + ...drawingParm, + componentKey: config.componentKey || drawingParm.componentKey, + allowTransform: config.allowTransform !== undefined ? config.allowTransform : drawingParm.allowTransform, + data: config.data || drawingParm.data, + sheetTransform: config.position + ? transformToDrawingPosition( + config.position, + selectionRenderService + ) ?? drawingParm.sheetTransform + : drawingParm.sheetTransform, + transform: { + ...drawingParm.transform, + ...config.position, // Merge with existing transform + }, + }; + const res = this._commandService.syncExecuteCommand(SetSheetDrawingCommand.id, { unitId, subUnitId, drawings: [newParam] }); + if (!res) { + throw new Error('updateFloatDom failed'); + } + + return this; + } + + override batchUpdateFloatDoms(updates: Array<{ id: string; config: Partial> }>): this { + const floatDomService = this._injector.get(SheetCanvasFloatDomManagerService); + const drawingService = this._injector.get(ISheetDrawingService); + const renderManagerService = this._injector.get(IRenderManagerService); + + const drawings: ISheetFloatDom[] = []; + + for (const update of updates) { + const info = floatDomService.getFloatDomInfo(update.id); + if (!info) continue; + + const { unitId, subUnitId } = info; + const drawingParm = drawingService.getDrawingByParam({ + unitId, + subUnitId, + drawingId: update.id, + }) as ISheetFloatDom; + if (!drawingParm) continue; + + const render = renderManagerService.getRenderById(unitId); + if (!render) continue; + + const skeleton = this.getSkeleton(); + if (!skeleton) continue; + + const selectionRenderService = render.with(ISheetSelectionRenderService); + if (!selectionRenderService) return this; + + const newParam: ISheetFloatDom = { + ...drawingParm, + componentKey: update.config.componentKey || drawingParm.componentKey, + allowTransform: update.config.allowTransform !== undefined ? update.config.allowTransform : drawingParm.allowTransform, + data: update.config.data || drawingParm.data, + sheetTransform: update.config.position + ? transformToDrawingPosition( + update.config.position, + selectionRenderService + ) ?? drawingParm.sheetTransform + : drawingParm.sheetTransform, + transform: { + ...drawingParm.transform, + ...update.config.position, // Merge with existing transform + }, + }; + + drawings.push(newParam); + } + + if (drawings.length > 0) { + const unitId = this._workbook.getUnitId(); + const subUnitId = this._worksheet.getSheetId(); + const res = this._commandService.syncExecuteCommand(SetSheetDrawingCommand.id, { unitId, subUnitId, drawings }); + if (!res) { + throw new Error('batchUpdateFloatDoms failed'); + } + } + + return this; + } + + override removeFloatDom(id: string): this { + const floatDomService = this._injector.get(SheetCanvasFloatDomManagerService); + const info = floatDomService.getFloatDomInfo(id); + if (!info) return this; + + const { unitId, subUnitId } = info; + const drawingService = this._injector.get(ISheetDrawingService); + const drawing = drawingService.getDrawingByParam({ + unitId, + subUnitId, + drawingId: id, + }); + + if (!drawing) return this; + + // Then delete it + const res = this._commandService.syncExecuteCommand(RemoveSheetDrawingCommand.id, { + unitId, + drawings: [drawing], + }); + + if (!res) { + throw new Error('removeFloatDom failed'); + } + return this; + } + override addFloatDomToPosition(layer: IFICanvasFloatDom, id?: string): Nullable<{ id: string; dispose: () => void; @@ -669,5 +1016,5 @@ export class FWorksheetLegacy extends FWorksheet implements IFWorksheetLegacy { FWorksheet.extend(FWorksheetLegacy); declare module '@univerjs/sheets/facade' { // eslint-disable-next-line ts/naming-convention - interface FWorksheet extends IFWorksheetLegacy {} + interface FWorksheet extends IFWorksheetLegacy { } } diff --git a/packages/sheets-drawing-ui/src/index.ts b/packages/sheets-drawing-ui/src/index.ts index 7d6ee7c08b..398e788519 100644 --- a/packages/sheets-drawing-ui/src/index.ts +++ b/packages/sheets-drawing-ui/src/index.ts @@ -16,6 +16,7 @@ import './global.css'; +export { drawingPositionToTransform, transformToDrawingPosition } from './basics/transform-position'; export { DeleteDrawingsCommand } from './commands/commands/delete-drawings.command'; export { GroupSheetDrawingCommand } from './commands/commands/group-sheet-drawing.command'; export { type IInsertImageCommandParams, InsertFloatImageCommand } from './commands/commands/insert-image.command'; @@ -32,5 +33,6 @@ export { SidebarSheetDrawingOperation } from './commands/operations/open-drawing export type { IUniverSheetsDrawingUIConfig } from './controllers/config.schema'; export { SheetDrawingUpdateController } from './controllers/sheet-drawing-update.controller'; export { UniverSheetsDrawingUIPlugin } from './plugin'; -export { type ICanvasFloatDom, type IDOMAnchor, SheetCanvasFloatDomManagerService } from './services/canvas-float-dom-manager.service'; +export { calcSheetFloatDomPosition, type ICanvasFloatDom, type ICanvasFloatDomInfo, type IDOMAnchor, SheetCanvasFloatDomManagerService } from './services/canvas-float-dom-manager.service'; + export { SHEETS_IMAGE_MENU_ID } from './views/menu/image.menu'; diff --git a/packages/sheets-drawing-ui/src/services/canvas-float-dom-manager.service.ts b/packages/sheets-drawing-ui/src/services/canvas-float-dom-manager.service.ts index a66a4c1de2..70fbcff927 100644 --- a/packages/sheets-drawing-ui/src/services/canvas-float-dom-manager.service.ts +++ b/packages/sheets-drawing-ui/src/services/canvas-float-dom-manager.service.ts @@ -613,6 +613,22 @@ export class SheetCanvasFloatDomManagerService extends Disposable { ...sheetDrawing.transform, }; this._transformChange$.next({ id: data.drawingId, value: newValue }); + this._canvasFloatDomService.updateFloatDom(data.drawingId, { + ...sheetDrawing, + }); + + const renderObject = this._getSceneAndTransformerByDrawingSearch(data.unitId); + if (renderObject) { + const { scene } = renderObject; + const floatDomInfo = this._domLayerInfoMap.get(data.drawingId); + if (floatDomInfo?.rect) { + if (sheetDrawing.allowTransform === false) { + scene.detachTransformerFrom(floatDomInfo.rect); + } else { + scene.attachTransformerTo(floatDomInfo.rect); + } + } + } }); }) ); @@ -628,19 +644,6 @@ export class SheetCanvasFloatDomManagerService extends Disposable { ); } - updateFloatDomProps(unitId: string, subUnitId: string, id: string, props: Record) { - const info = this._domLayerInfoMap.get(id); - const renderObject = this._getSceneAndTransformerByDrawingSearch(unitId); - if (info && renderObject) { - const { scene } = renderObject; - const rectShapeKey = getDrawingShapeKeyByDrawingSearch({ unitId, subUnitId, drawingId: id }); - const rectShape = scene.getObject(rectShapeKey); - if (rectShape && rectShape instanceof Rect) { - rectShape.setProps(props); - } - } - } - // CreateFloatDomCommand --> floatDomService.addFloatDomToPosition addFloatDomToPosition(layer: ICanvasFloatDom, propId?: string) { const target = getSheetCommandTarget(this._univerInstanceService, { diff --git a/packages/ui/src/services/dom/canvas-dom-layer.service.ts b/packages/ui/src/services/dom/canvas-dom-layer.service.ts index 7f0b565c5d..189105a77d 100644 --- a/packages/ui/src/services/dom/canvas-dom-layer.service.ts +++ b/packages/ui/src/services/dom/canvas-dom-layer.service.ts @@ -56,6 +56,19 @@ export class CanvasFloatDomService { this._domLayers$.next(Array.from(this._domLayerMap.entries())); } + updateFloatDom(id: string, item: Partial) { + const current = this._domLayerMap.get(id); + if (!current) { + return; + } + this._domLayerMap.set(id, { + ...current, + ...item, + }); + + this._notice(); + } + addFloatDom(item: IFloatDom) { this._domLayerMap.set(item.id, item); this._notice();