diff --git a/packages/element/src/resizeElements.ts b/packages/element/src/resizeElements.ts index 8a1702afa..7ded4dc0a 100644 --- a/packages/element/src/resizeElements.ts +++ b/packages/element/src/resizeElements.ts @@ -1369,8 +1369,21 @@ export const resizeMultipleElements = ( ); if (keepAspectRatio) { - scaleX = scale; - scaleY = scale; + // If the elements are in a group and shouldMaintainAspectRatio is true(meaning user is holding shift), + // we need to adjust the scaleX or scaleY based on the handleDirection + if(targetElements.some(item => isInGroup(item.latest)) && shouldMaintainAspectRatio) { + if(handleDirection.length === 1) { + if(handleDirection.includes("e") || handleDirection.includes("w")) { + scaleX = scale; + } else if (handleDirection.includes("n") || handleDirection.includes("s")) { + scaleY = scale; + } + } + } + else { + scaleX = scale; + scaleY = scale; + } } /** @@ -1481,11 +1494,15 @@ export const resizeMultipleElements = ( } if (isTextElement(orig)) { - const metrics = measureFontSizeFromWidth(orig, elementsMap, width); - if (!metrics) { - return; + if (!shouldMaintainAspectRatio) { + const metrics = measureFontSizeFromWidth(orig, elementsMap, width); + if (!metrics) { + return; + } + update.fontSize = metrics.size; + } else { + update.fontSize = orig.fontSize; } - update.fontSize = metrics.size; } const boundTextElement = originalElementsMap.get( @@ -1493,7 +1510,7 @@ export const resizeMultipleElements = ( ) as ExcalidrawTextElementWithContainer | undefined; if (boundTextElement) { - if (keepAspectRatio) { + if (keepAspectRatio && !shouldMaintainAspectRatio) { const newFontSize = boundTextElement.fontSize * scale; if (newFontSize < MIN_FONT_SIZE) { return; diff --git a/packages/element/tests/groupResize.test.tsx b/packages/element/tests/groupResize.test.tsx new file mode 100644 index 000000000..c3be95be1 --- /dev/null +++ b/packages/element/tests/groupResize.test.tsx @@ -0,0 +1,116 @@ +import { API } from "@excalidraw/excalidraw/tests/helpers/api"; +import { UI, Keyboard, Pointer } from "@excalidraw/excalidraw/tests/helpers/ui"; +import { KEYS } from "@excalidraw/common"; +import { unmountComponent } from "@excalidraw/excalidraw/tests/test-utils"; +import { render } from "@excalidraw/excalidraw/tests/test-utils"; +import { Excalidraw } from "@excalidraw/excalidraw"; +const { h } = window; +const mouse = new Pointer("mouse"); + +unmountComponent(); + +describe("group resize", () => { + beforeEach(() => { + h.elements = []; + }); + + it("resizes group with locked aspect ratio using side handles", async () => { + await render(); + + UI.clickTool("rectangle"); + mouse.down(10, 10); + mouse.up(10, 10); + + UI.clickTool("rectangle"); + mouse.down(10, -10); + mouse.up(10, 10); + + UI.clickTool("rectangle"); + mouse.down(10, -10); + mouse.up(10, 10); + const end = mouse.getPosition(); + + mouse.reset(); + mouse.down(); + mouse.restorePosition(...end); + mouse.up(); + + expect(h.elements.length).toBe(3); + for (const element of h.elements) { + expect(element.groupIds.length).toBe(0); + expect(h.state.selectedElementIds[element.id]).toBe(true); + } + + Keyboard.withModifierKeys({ ctrl: true }, () => { + Keyboard.keyPress(KEYS.G); + }); + + for (const element of h.elements) { + expect(element.groupIds.length).toBe(1); + } + + mouse.select(h.elements[0]); + let originalWidth = h.elements[0].width; + let originalHeight = h.elements[0].height; + + UI.resize(h.elements[0], 'se', [50, 50], { shift: true }); + + expect(h.elements[0].width).toBe(originalWidth + 50); + expect(h.elements[0].height).toBe(originalHeight + 50); + + originalWidth = h.elements[0].width; + originalHeight = h.elements[0].height; + + UI.resize(h.elements[0], "e", [50, 0], { shift: true }); + + expect(h.elements[0].width).toBe(originalWidth - 50); + expect(h.elements[0].height).toBe(originalHeight - 50); + }); + + it("resizes group with locked aspect ratio using corner handles", async () => { + await render(); + + UI.clickTool("rectangle"); + mouse.down(10, 10); + mouse.up(10, 10); + + UI.clickTool("rectangle"); + mouse.down(10, -10); + mouse.up(10, 10); + + UI.clickTool("rectangle"); + mouse.down(10, -10); + mouse.up(10, 10); + const end = mouse.getPosition(); + + mouse.reset(); + mouse.down(); + mouse.restorePosition(...end); + mouse.up(); + + expect(h.elements.length).toBe(3); + for (const element of h.elements) { + expect(element.groupIds.length).toBe(0); + expect(h.state.selectedElementIds[element.id]).toBe(true); + } + + Keyboard.withModifierKeys({ ctrl: true }, () => { + Keyboard.keyPress(KEYS.G); + }); + + for (const element of h.elements) { + expect(element.groupIds.length).toBe(1); + } + + mouse.select(h.elements[0]); + + let originalWidth = h.elements[0].width; + let originalHeight = h.elements[0].height; + + UI.resize(h.elements[0], "se", [50, 50], { shift: true }); + + + expect(h.elements[0].width).toBe(originalWidth + 50); + expect(h.elements[0].height).toBe(originalHeight + 50); + }); +}); \ No newline at end of file