diff --git a/packages/sheets-drawing/src/facade/__tests__/f-worksheet.spec.ts b/packages/sheets-drawing/src/facade/__tests__/f-worksheet.spec.ts new file mode 100644 index 0000000000..65e3f59df9 --- /dev/null +++ b/packages/sheets-drawing/src/facade/__tests__/f-worksheet.spec.ts @@ -0,0 +1,198 @@ +/** + * Copyright 2023-present DreamNum Co., Ltd. + * + * 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 { Injector, Univer } from '@univerjs/core'; +import type { ISheetDrawing } from '../../services/sheet-drawing.service'; +import { DrawingTypeEnum, ICommandService, RedoCommand, UndoCommand } from '@univerjs/core'; +import { InsertSheetDrawingCommand } from '../../commands/commands/insert-sheet-drawing.command'; +import { ISheetDrawingService } from '../../services/sheet-drawing.service'; +import { afterEach, beforeEach, describe, expect, it } from 'vitest'; +import { createSheetsDrawingTestBed } from '../../__tests__/create-sheets-drawing-test-bed'; +import { FWorksheetDrawingMixin } from '../f-worksheet'; + +describe('FWorksheetDrawingMixin group drawings', () => { + let univer: Univer; + let injector: Injector; + + beforeEach(() => { + const testBed = createSheetsDrawingTestBed(); + univer = testBed.univer; + injector = testBed.injector; + }); + + afterEach(() => { + univer.dispose(); + }); + + it('groups drawings and exposes parent and child relationships', () => { + const drawings = [ + createDrawing('drawing-1', 10), + createDrawing('drawing-2', 80), + ]; + const commandService = injector.get(ICommandService); + const sheetDrawingService = injector.get(ISheetDrawingService); + const fWorksheet = createFacade(injector); + + expect(commandService.syncExecuteCommand(InsertSheetDrawingCommand.id, { unitId: 'test', drawings })).toBe(true); + + const groupId = fWorksheet.groupDrawings(['drawing-1', 'drawing-2'], 'group-1'); + + expect(groupId).toBe('group-1'); + expect(sheetDrawingService.getDrawingByParam({ unitId: 'test', subUnitId: 'sheet1', drawingId: 'group-1' })?.drawingType) + .toBe(DrawingTypeEnum.DRAWING_GROUP); + expect(fWorksheet.isDrawingGrouped('drawing-1')).toBe(true); + expect(fWorksheet.getDrawingParentGroup('drawing-1')?.drawingId).toBe('group-1'); + expect(fWorksheet.getDrawingGroupChildren('group-1').map((drawing: ISheetDrawing) => drawing.drawingId)) + .toEqual(['drawing-1', 'drawing-2']); + }); + + it('does not group unsupported drawing types', () => { + const drawings = [ + createDrawing('drawing-1', 10), + createDrawing('chart-1', 80, DrawingTypeEnum.DRAWING_CHART), + ]; + const commandService = injector.get(ICommandService); + const fWorksheet = createFacade(injector); + + expect(commandService.syncExecuteCommand(InsertSheetDrawingCommand.id, { unitId: 'test', drawings })).toBe(true); + + const groupId = fWorksheet.groupDrawings(['drawing-1', 'chart-1'], 'group-1'); + + expect(groupId).toBeNull(); + expect(fWorksheet.isDrawingGrouped('drawing-1')).toBe(false); + expect(fWorksheet.isDrawingGrouped('chart-1')).toBe(false); + }); + + it('supports undo and redo for group and ungroup facade operations', async () => { + const drawings = [ + createDrawing('drawing-1', 10), + createDrawing('drawing-2', 80), + ]; + const commandService = injector.get(ICommandService); + const sheetDrawingService = injector.get(ISheetDrawingService); + const fWorksheet = createFacade(injector); + + expect(commandService.syncExecuteCommand(InsertSheetDrawingCommand.id, { unitId: 'test', drawings })).toBe(true); + expect(fWorksheet.groupDrawings(['drawing-1', 'drawing-2'], 'group-1')).toBe('group-1'); + + expect(await commandService.executeCommand(UndoCommand.id)).toBe(true); + expect(sheetDrawingService.getDrawingByParam({ unitId: 'test', subUnitId: 'sheet1', drawingId: 'group-1' })).toBeUndefined(); + expect(fWorksheet.isDrawingGrouped('drawing-1')).toBe(false); + + expect(await commandService.executeCommand(RedoCommand.id)).toBe(true); + expect(sheetDrawingService.getDrawingByParam({ unitId: 'test', subUnitId: 'sheet1', drawingId: 'group-1' })?.drawingType) + .toBe(DrawingTypeEnum.DRAWING_GROUP); + expect(fWorksheet.isDrawingGrouped('drawing-1')).toBe(true); + + expect(fWorksheet.ungroupDrawings(['group-1'])).toBe(true); + expect(sheetDrawingService.getDrawingByParam({ unitId: 'test', subUnitId: 'sheet1', drawingId: 'group-1' })).toBeUndefined(); + + expect(await commandService.executeCommand(UndoCommand.id)).toBe(true); + expect(sheetDrawingService.getDrawingByParam({ unitId: 'test', subUnitId: 'sheet1', drawingId: 'group-1' })?.drawingType) + .toBe(DrawingTypeEnum.DRAWING_GROUP); + expect(fWorksheet.isDrawingGrouped('drawing-1')).toBe(true); + }); + + it('does not group with duplicate drawing ids or an existing group id', () => { + const drawings = [ + createDrawing('drawing-1', 10), + createDrawing('drawing-2', 80), + ]; + const commandService = injector.get(ICommandService); + const fWorksheet = createFacade(injector); + + expect(commandService.syncExecuteCommand(InsertSheetDrawingCommand.id, { unitId: 'test', drawings })).toBe(true); + + expect(fWorksheet.groupDrawings(['drawing-1', 'drawing-1'], 'group-1')).toBeNull(); + expect(fWorksheet.groupDrawings(['drawing-1', 'drawing-2'], 'drawing-1')).toBeNull(); + expect(fWorksheet.isDrawingGrouped('drawing-1')).toBe(false); + expect(fWorksheet.isDrawingGrouped('drawing-2')).toBe(false); + }); + + it('ungroups drawings and clears parent relationships', () => { + const drawings = [ + createDrawing('drawing-1', 10), + createDrawing('drawing-2', 80), + ]; + const commandService = injector.get(ICommandService); + const sheetDrawingService = injector.get(ISheetDrawingService); + const fWorksheet = createFacade(injector); + + expect(commandService.syncExecuteCommand(InsertSheetDrawingCommand.id, { unitId: 'test', drawings })).toBe(true); + expect(fWorksheet.groupDrawings(['drawing-1', 'drawing-2'], 'group-1')).toBe('group-1'); + + expect(fWorksheet.ungroupDrawings(['group-1'])).toBe(true); + + expect(sheetDrawingService.getDrawingByParam({ unitId: 'test', subUnitId: 'sheet1', drawingId: 'group-1' })).toBeUndefined(); + expect(fWorksheet.isDrawingGrouped('drawing-1')).toBe(false); + expect(fWorksheet.isDrawingGrouped('drawing-2')).toBe(false); + }); + + it('returns nested group children when recursive is enabled', () => { + const drawings = [ + createDrawing('drawing-1', 10), + createDrawing('drawing-2', 80), + createDrawing('drawing-3', 150), + ]; + const commandService = injector.get(ICommandService); + const fWorksheet = createFacade(injector); + + expect(commandService.syncExecuteCommand(InsertSheetDrawingCommand.id, { unitId: 'test', drawings })).toBe(true); + expect(fWorksheet.groupDrawings(['drawing-1', 'drawing-2'], 'group-1')).toBe('group-1'); + expect(fWorksheet.groupDrawings(['group-1', 'drawing-3'], 'group-2')).toBe('group-2'); + + expect(fWorksheet.getDrawingGroupChildren('group-2').map((drawing: ISheetDrawing) => drawing.drawingId)) + .toEqual(['drawing-3', 'group-1']); + const recursiveChildren = fWorksheet.getDrawingGroupChildren('group-2', true).map((drawing: ISheetDrawing) => drawing.drawingId); + expect(recursiveChildren).toHaveLength(4); + expect(recursiveChildren).toEqual(expect.arrayContaining(['drawing-1', 'drawing-2', 'drawing-3', 'group-1'])); + }); +}); + +function createFacade(injector: Injector): FWorksheetDrawingMixin { + const instance = Object.create(FWorksheetDrawingMixin.prototype) as any; + + instance._commandService = injector.get(ICommandService); + instance._fWorkbook = { getId: () => 'test' }; + instance._workbook = { getUnitId: () => 'test' }; + instance._worksheet = { getSheetId: () => 'sheet1' }; + instance._injector = injector; + + return instance as FWorksheetDrawingMixin; +} + +function createDrawing(drawingId: string, left: number, drawingType = DrawingTypeEnum.DRAWING_SHAPE): ISheetDrawing { + return { + unitId: 'test', + subUnitId: 'sheet1', + drawingId, + drawingType, + transform: { + left, + top: 20, + width: 40, + height: 30, + }, + sheetTransform: { + from: { row: 1, column: 1, rowOffset: 0, columnOffset: 0 }, + to: { row: 3, column: 3, rowOffset: 0, columnOffset: 0 }, + }, + axisAlignSheetTransform: { + from: { row: 1, column: 1, rowOffset: 0, columnOffset: 0 }, + to: { row: 3, column: 3, rowOffset: 0, columnOffset: 0 }, + }, + }; +} diff --git a/packages/sheets-drawing/src/facade/f-worksheet.ts b/packages/sheets-drawing/src/facade/f-worksheet.ts index cc994e6f58..695c07006d 100644 --- a/packages/sheets-drawing/src/facade/f-worksheet.ts +++ b/packages/sheets-drawing/src/facade/f-worksheet.ts @@ -15,12 +15,21 @@ */ import type { IFBlobSource } from '@univerjs/core/facade'; -import type { ISheetImage } from '@univerjs/sheets-drawing'; -import { DrawingTypeEnum, ImageSourceType } from '@univerjs/core'; -import { InsertSheetDrawingCommand, ISheetDrawingService, RemoveSheetDrawingCommand, SetSheetDrawingCommand } from '@univerjs/sheets-drawing'; +import type { IDrawingParam } from '@univerjs/core'; +import type { IDrawingGroupUpdateParam, IDrawingJsonUndo1 } from '@univerjs/drawing'; +import type { ISheetDrawing, ISheetImage } from '@univerjs/sheets-drawing'; +import { DrawingTypeEnum, generateRandomId, ImageSourceType, IUndoRedoService } from '@univerjs/core'; +import { getGroupState, transformObjectOutOfGroup } from '@univerjs/engine-render'; +import { DrawingApplyType, InsertSheetDrawingCommand, ISheetDrawingService, RemoveSheetDrawingCommand, SetDrawingApplyMutation, SetSheetDrawingCommand } from '@univerjs/sheets-drawing'; import { FWorksheet } from '@univerjs/sheets/facade'; import { FOverGridImage, FOverGridImageBuilder } from './f-over-grid-image'; +const GROUPABLE_DRAWING_TYPES = new Set([ + DrawingTypeEnum.DRAWING_IMAGE, + DrawingTypeEnum.DRAWING_SHAPE, + DrawingTypeEnum.DRAWING_GROUP, +]); + /** * @ignore */ @@ -217,6 +226,166 @@ export interface IFWorksheetDrawingMixin { * ``` */ newOverGridImage(): FOverGridImageBuilder; + + /** + * Group drawings on the current sheet. + * @param {string[]} drawingIds - The drawing ids to group. At least two drawings are required. + * @param {string} [groupId] - Optional group drawing id. If omitted, a new id will be generated. + * @returns {string | null} The group id if the operation succeeds, otherwise null. + * @example + * ```ts + * // Group two over-grid images on the active sheet. + * const fWorkbook = univerAPI.getActiveWorkbook(); + * const fWorksheet = fWorkbook.getActiveSheet(); + * const image1 = await fWorksheet.newOverGridImage() + * .setSource('https://avatars.githubusercontent.com/u/61444807?s=48&v=4', univerAPI.Enum.ImageSourceType.URL) + * .setColumn(1) + * .setRow(1) + * .setWidth(100) + * .setHeight(100) + * .buildAsync(); + * const image2 = await fWorksheet.newOverGridImage() + * .setSource('https://avatars.githubusercontent.com/u/61444807?s=48&v=4', univerAPI.Enum.ImageSourceType.URL) + * .setColumn(3) + * .setRow(1) + * .setWidth(100) + * .setHeight(100) + * .buildAsync(); + * fWorksheet.insertImages([image1, image2]); + * const groupId = fWorksheet.groupDrawings([image1.drawingId, image2.drawingId]); + * console.log(groupId); + * ``` + */ + groupDrawings(drawingIds: string[], groupId?: string): string | null; + + /** + * Ungroup drawing groups on the current sheet. + * @param {string[]} groupIds - The group drawing ids to ungroup. + * @returns {boolean} true if the operation succeeds, otherwise false. + * @example + * ```ts + * // Group two images, then ungroup the generated group. + * const fWorkbook = univerAPI.getActiveWorkbook(); + * const fWorksheet = fWorkbook.getActiveSheet(); + * const image1 = await fWorksheet.newOverGridImage() + * .setSource('https://avatars.githubusercontent.com/u/61444807?s=48&v=4', univerAPI.Enum.ImageSourceType.URL) + * .setColumn(1) + * .setRow(1) + * .setWidth(100) + * .setHeight(100) + * .buildAsync(); + * const image2 = await fWorksheet.newOverGridImage() + * .setSource('https://avatars.githubusercontent.com/u/61444807?s=48&v=4', univerAPI.Enum.ImageSourceType.URL) + * .setColumn(3) + * .setRow(1) + * .setWidth(100) + * .setHeight(100) + * .buildAsync(); + * fWorksheet.insertImages([image1, image2]); + * const groupId = fWorksheet.groupDrawings([image1.drawingId, image2.drawingId]); + * if (groupId) { + * const result = fWorksheet.ungroupDrawings([groupId]); + * console.log(result); + * } + * ``` + */ + ungroupDrawings(groupIds: string[]): boolean; + + /** + * Get children of a drawing group on the current sheet. + * @param {string} groupId - The group drawing id. + * @param {boolean} [recursive] - Whether to return all descendants. + * @returns {ISheetDrawing[]} The child drawings. + * @example + * ```ts + * // Get the direct children of a drawing group. + * const fWorkbook = univerAPI.getActiveWorkbook(); + * const fWorksheet = fWorkbook.getActiveSheet(); + * const image1 = await fWorksheet.newOverGridImage() + * .setSource('https://avatars.githubusercontent.com/u/61444807?s=48&v=4', univerAPI.Enum.ImageSourceType.URL) + * .setColumn(1) + * .setRow(1) + * .setWidth(100) + * .setHeight(100) + * .buildAsync(); + * const image2 = await fWorksheet.newOverGridImage() + * .setSource('https://avatars.githubusercontent.com/u/61444807?s=48&v=4', univerAPI.Enum.ImageSourceType.URL) + * .setColumn(3) + * .setRow(1) + * .setWidth(100) + * .setHeight(100) + * .buildAsync(); + * fWorksheet.insertImages([image1, image2]); + * const groupId = fWorksheet.groupDrawings([image1.drawingId, image2.drawingId]); + * if (groupId) { + * const children = fWorksheet.getDrawingGroupChildren(groupId); + * console.log(children.map((drawing) => drawing.drawingId)); + * } + * ``` + */ + getDrawingGroupChildren(groupId: string, recursive?: boolean): ISheetDrawing[]; + + /** + * Get the parent group of a drawing on the current sheet. + * @param {string} drawingId - The child drawing id. + * @returns {ISheetDrawing | null} The parent group drawing, or null if the drawing is not grouped. + * @example + * ```ts + * // Get the parent group of a drawing. + * const fWorkbook = univerAPI.getActiveWorkbook(); + * const fWorksheet = fWorkbook.getActiveSheet(); + * const image1 = await fWorksheet.newOverGridImage() + * .setSource('https://avatars.githubusercontent.com/u/61444807?s=48&v=4', univerAPI.Enum.ImageSourceType.URL) + * .setColumn(1) + * .setRow(1) + * .setWidth(100) + * .setHeight(100) + * .buildAsync(); + * const image2 = await fWorksheet.newOverGridImage() + * .setSource('https://avatars.githubusercontent.com/u/61444807?s=48&v=4', univerAPI.Enum.ImageSourceType.URL) + * .setColumn(3) + * .setRow(1) + * .setWidth(100) + * .setHeight(100) + * .buildAsync(); + * fWorksheet.insertImages([image1, image2]); + * fWorksheet.groupDrawings([image1.drawingId, image2.drawingId]); + * const parentGroup = fWorksheet.getDrawingParentGroup(image1.drawingId); + * console.log(parentGroup?.drawingId); + * ``` + */ + getDrawingParentGroup(drawingId: string): ISheetDrawing | null; + + /** + * Returns whether a drawing is inside a group on the current sheet. + * @param {string} drawingId - The drawing id. + * @returns {boolean} true if the drawing has a parent group. + * @example + * ```ts + * // Check whether a drawing is inside a group. + * const fWorkbook = univerAPI.getActiveWorkbook(); + * const fWorksheet = fWorkbook.getActiveSheet(); + * const image1 = await fWorksheet.newOverGridImage() + * .setSource('https://avatars.githubusercontent.com/u/61444807?s=48&v=4', univerAPI.Enum.ImageSourceType.URL) + * .setColumn(1) + * .setRow(1) + * .setWidth(100) + * .setHeight(100) + * .buildAsync(); + * const image2 = await fWorksheet.newOverGridImage() + * .setSource('https://avatars.githubusercontent.com/u/61444807?s=48&v=4', univerAPI.Enum.ImageSourceType.URL) + * .setColumn(3) + * .setRow(1) + * .setWidth(100) + * .setHeight(100) + * .buildAsync(); + * fWorksheet.insertImages([image1, image2]); + * fWorksheet.groupDrawings([image1.drawingId, image2.drawingId]); + * const isGrouped = fWorksheet.isDrawingGrouped(image1.drawingId); + * console.log(isGrouped); + * ``` + */ + isDrawingGrouped(drawingId: string): boolean; } export class FWorksheetDrawingMixin extends FWorksheet implements IFWorksheetDrawingMixin { @@ -329,6 +498,199 @@ export class FWorksheetDrawingMixin extends FWorksheet implements IFWorksheetDra const subUnitId = this.getSheetId(); return this._injector.createInstance(FOverGridImageBuilder, unitId, subUnitId); } + + override groupDrawings(drawingIds: string[], groupId = generateRandomId(10)): string | null { + const uniqueDrawingIds = Array.from(new Set(drawingIds)); + if (uniqueDrawingIds.length < 2) return null; + + const unitId = this._fWorkbook.getId(); + const subUnitId = this.getSheetId(); + const sheetDrawingService = this._injector.get(ISheetDrawingService); + if (sheetDrawingService.getDrawingByParam({ unitId, subUnitId, drawingId: groupId })) return null; + + const drawings = uniqueDrawingIds.map((drawingId) => sheetDrawingService.getDrawingByParam({ unitId, subUnitId, drawingId })); + + if (drawings.some((drawing) => !drawing)) return null; + if (drawings.some((drawing) => !GROUPABLE_DRAWING_TYPES.has(drawing!.drawingType))) return null; + + const validDrawings = drawings as ISheetDrawing[]; + const groupTransform = getGroupState(0, 0, validDrawings.map((drawing) => drawing.transform || {})); + const groupParam = { + unitId, + subUnitId, + drawingId: groupId, + drawingType: DrawingTypeEnum.DRAWING_GROUP, + transform: groupTransform, + groupBaseBound: { + left: groupTransform.left, + top: groupTransform.top, + width: groupTransform.width, + height: groupTransform.height, + }, + } as IDrawingParam; + const children = validDrawings.map((drawing) => ({ + ...drawing, + groupId, + })) as IDrawingParam[]; + + const result = this._applyGroupDrawingOperation([{ + parent: groupParam, + children, + }], DrawingApplyType.GROUP); + + return result ? groupId : null; + } + + override ungroupDrawings(groupIds: string[]): boolean { + const unitId = this._fWorkbook.getId(); + const subUnitId = this.getSheetId(); + const sheetDrawingService = this._injector.get(ISheetDrawingService); + const groupParams: IDrawingGroupUpdateParam[] = []; + + for (const groupId of groupIds) { + const groupDrawing = sheetDrawingService.getDrawingByParam({ unitId, subUnitId, drawingId: groupId }); + if (!groupDrawing || groupDrawing.drawingType !== DrawingTypeEnum.DRAWING_GROUP) continue; + + const groupTransform = groupDrawing.transform || { width: 0, height: 0 }; + const children = sheetDrawingService.getDrawingsByGroup({ unitId, subUnitId, drawingId: groupId }) + .map((drawing) => { + const newTransform = transformObjectOutOfGroup( + drawing.transform || {}, + groupTransform, + groupTransform.width || 0, + groupTransform.height || 0, + groupDrawing.groupBaseBound + ); + + return { + ...drawing, + transform: { + ...drawing.transform, + ...newTransform, + }, + groupId: undefined, + }; + }); + + if (children.length > 0) { + groupParams.push({ + parent: groupDrawing, + children, + }); + } + } + + return this._applyGroupDrawingOperation(groupParams, DrawingApplyType.UNGROUP); + } + + override getDrawingGroupChildren(groupId: string, recursive = false): ISheetDrawing[] { + const unitId = this._fWorkbook.getId(); + const subUnitId = this.getSheetId(); + const sheetDrawingService = this._injector.get(ISheetDrawingService); + + if (!recursive) { + return sheetDrawingService.getDrawingsByGroup({ unitId, subUnitId, drawingId: groupId }) as ISheetDrawing[]; + } + + const nested = sheetDrawingService.getDrawingsByGroupNested({ unitId, subUnitId, drawingId: groupId }); + if (!nested) return []; + + return [ + ...(nested.flatChildren || []), + ...nested.groups.filter((group) => group.drawingId !== groupId), + ] as ISheetDrawing[]; + } + + override getDrawingParentGroup(drawingId: string): ISheetDrawing | null { + const unitId = this._fWorkbook.getId(); + const subUnitId = this.getSheetId(); + const sheetDrawingService = this._injector.get(ISheetDrawingService); + const drawing = sheetDrawingService.getDrawingByParam({ unitId, subUnitId, drawingId }); + + if (!drawing?.groupId) return null; + + const groupDrawing = sheetDrawingService.getDrawingByParam({ unitId, subUnitId, drawingId: drawing.groupId }); + if (!groupDrawing || groupDrawing.drawingType !== DrawingTypeEnum.DRAWING_GROUP) return null; + + return groupDrawing; + } + + override isDrawingGrouped(drawingId: string): boolean { + return this.getDrawingParentGroup(drawingId) !== null; + } + + private _applyGroupDrawingOperation(groupParams: IDrawingGroupUpdateParam[], type: DrawingApplyType.GROUP | DrawingApplyType.UNGROUP): boolean { + if (groupParams.length === 0) return false; + + const sheetDrawingService = this._injector.get(ISheetDrawingService); + const commandService = this._commandService; + const undoRedoService = this._injector.get(IUndoRedoService); + const jsonOp = (type === DrawingApplyType.GROUP + ? sheetDrawingService.getGroupDrawingOp(groupParams) + : sheetDrawingService.getUngroupDrawingOp(groupParams)) as IDrawingJsonUndo1; + + const { unitId, subUnitId, undo, redo, objects } = jsonOp; + const result = commandService.syncExecuteCommand(SetDrawingApplyMutation.id, { op: redo, unitId, subUnitId, objects, type }); + + if (result) { + const inverseType = type === DrawingApplyType.GROUP ? DrawingApplyType.UNGROUP : DrawingApplyType.GROUP; + undoRedoService.pushUndoRedo({ + unitID: unitId, + undoMutations: [ + { id: SetDrawingApplyMutation.id, params: { op: undo, unitId, subUnitId, objects: this._invertGroupOperationObjects(objects as IDrawingGroupUpdateParam[], type), type: inverseType } }, + ], + redoMutations: [ + { id: SetDrawingApplyMutation.id, params: { op: redo, unitId, subUnitId, objects, type } }, + ], + }); + + return true; + } + + return false; + } + + private _invertGroupOperationObjects(groupParams: IDrawingGroupUpdateParam[], type: DrawingApplyType.GROUP | DrawingApplyType.UNGROUP): IDrawingGroupUpdateParam[] { + return type === DrawingApplyType.GROUP + ? groupParams.map((groupParam) => { + const { parent, children } = groupParam; + const groupTransform = parent.transform || { width: 0, height: 0 }; + return { + parent: { + ...parent, + transform: { + left: 0, + top: 0, + }, + }, + children: children.map((child) => ({ + ...child, + transform: transformObjectOutOfGroup( + child.transform || {}, + groupTransform, + groupTransform.width || 0, + groupTransform.height || 0, + parent.groupBaseBound + ), + groupId: undefined, + })), + }; + }) + : groupParams.map((groupParam) => { + const { parent, children } = groupParam; + const groupTransform = getGroupState(0, 0, children.map((child) => child.transform || {})); + return { + parent: { + ...parent, + transform: groupTransform, + }, + children: children.map((child) => ({ + ...child, + groupId: parent.drawingId, + })), + }; + }); + } } FWorksheet.extend(FWorksheetDrawingMixin);