Enhance aspect ratio tools | Rectangle, Diamond, Ellipses (#2439)

Co-authored-by: dwelle <luzar.david@gmail.com>
This commit is contained in:
João Forja 2020-12-06 22:39:31 +00:00 committed by GitHub
parent 4c90ea5667
commit aa221837fc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 488 additions and 9492 deletions

View file

@ -1,11 +1,15 @@
import React from "react";
import ReactDOM from "react-dom";
import { render, fireEvent } from "./test-utils";
import ExcalidrawApp from "../excalidraw-app";
import { render } from "./test-utils";
import App from "../components/App";
import * as Renderer from "../renderer/renderScene";
import { reseed } from "../random";
import { UI, Pointer, Keyboard } from "./helpers/ui";
import { getTransformHandles } from "../element/transformHandles";
import { UI, Pointer, Keyboard, KeyboardModifiers } from "./helpers/ui";
import {
getTransformHandles,
TransformHandleDirection,
} from "../element/transformHandles";
import { ExcalidrawElement } from "../element/types";
const mouse = new Pointer("mouse");
@ -21,70 +25,119 @@ beforeEach(() => {
const { h } = window;
describe("resize element", () => {
it("rectangle", async () => {
const { getByToolName, container } = await render(<ExcalidrawApp />);
const canvas = container.querySelector("canvas")!;
describe("resize rectangle ellipses and diamond elements", () => {
const elemData = {
x: 0,
y: 0,
width: 100,
height: 100,
};
// Value for irrelevant cursor movements
const _ = 234;
{
// create element
const tool = getByToolName("rectangle");
fireEvent.click(tool);
fireEvent.pointerDown(canvas, { clientX: 30, clientY: 20 });
fireEvent.pointerMove(canvas, { clientX: 60, clientY: 70 });
fireEvent.pointerUp(canvas);
expect(renderScene).toHaveBeenCalledTimes(6);
expect(h.state.selectionElement).toBeNull();
expect(h.elements.length).toEqual(1);
expect(h.state.selectedElementIds[h.elements[0].id]).toBeTruthy();
expect([h.elements[0].x, h.elements[0].y]).toEqual([30, 20]);
expect([h.elements[0].width, h.elements[0].height]).toEqual([30, 50]);
renderScene.mockClear();
}
// select the element first
fireEvent.pointerDown(canvas, { clientX: 50, clientY: 20 });
fireEvent.pointerUp(canvas);
// select a handler rectangle (top-left)
fireEvent.pointerDown(canvas, { clientX: 21, clientY: 13 });
fireEvent.pointerMove(canvas, { clientX: 20, clientY: 40 });
fireEvent.pointerUp(canvas);
expect(renderScene).toHaveBeenCalledTimes(5);
expect(h.state.selectionElement).toBeNull();
expect(h.elements.length).toEqual(1);
expect([h.elements[0].x, h.elements[0].y]).toEqual([29, 47]);
expect([h.elements[0].width, h.elements[0].height]).toEqual([30, 50]);
h.elements.forEach((element) => expect(element).toMatchSnapshot());
it.each`
handle | move | dimensions | topLeft
${"n"} | ${[_, -100]} | ${[100, 200]} | ${[elemData.x, -100]}
${"s"} | ${[_, 39]} | ${[100, 139]} | ${[elemData.x, elemData.x]}
${"e"} | ${[-20, _]} | ${[80, 100]} | ${[elemData.x, elemData.y]}
${"w"} | ${[-20, _]} | ${[120, 100]} | ${[-20, elemData.y]}
${"ne"} | ${[10, 55]} | ${[110, 45]} | ${[elemData.x, 55]}
${"se"} | ${[-30, -10]} | ${[70, 90]} | ${[elemData.x, elemData.y]}
${"nw"} | ${[-300, -200]} | ${[400, 300]} | ${[-300, -200]}
${"sw"} | ${[40, -20]} | ${[60, 80]} | ${[40, 0]}
`("resizes with handle $handle", ({ handle, move, dimensions, topLeft }) => {
render(<App />);
const rectangle = UI.createElement("rectangle", elemData);
resize(rectangle, handle, move);
const element = h.elements[0];
expect([element.width, element.height]).toEqual(dimensions);
expect([element.x, element.y]).toEqual(topLeft);
});
it.each`
handle | move | dimensions | topLeft
${"n"} | ${[_, -100]} | ${[200, 200]} | ${[-50, -100]}
${"nw"} | ${[-300, -200]} | ${[400, 400]} | ${[-300, -300]}
${"sw"} | ${[40, -20]} | ${[80, 80]} | ${[20, 0]}
`(
"resizes with fixed side ratios on handle $handle",
({ handle, move, dimensions, topLeft }) => {
render(<App />);
const rectangle = UI.createElement("rectangle", elemData);
resize(rectangle, handle, move, { shift: true });
const element = h.elements[0];
expect([element.width, element.height]).toEqual(dimensions);
expect([element.x, element.y]).toEqual(topLeft);
},
);
it.each`
handle | move | dimensions | topLeft
${"nw"} | ${[0, 120]} | ${[100, 100]} | ${[0, 100]}
${"ne"} | ${[-120, 0]} | ${[100, 100]} | ${[-100, 0]}
${"sw"} | ${[200, -200]} | ${[100, 100]} | ${[100, -100]}
${"n"} | ${[_, 150]} | ${[50, 50]} | ${[25, 100]}
`(
"Flips while resizing and keeping side ratios on handle $handle",
({ handle, move, dimensions, topLeft }) => {
render(<App />);
const rectangle = UI.createElement("rectangle", elemData);
resize(rectangle, handle, move, { shift: true });
const element = h.elements[0];
expect([element.width, element.height]).toEqual(dimensions);
expect([element.x, element.y]).toEqual(topLeft);
},
);
it.each`
handle | move | dimensions | topLeft
${"ne"} | ${[50, -100]} | ${[200, 300]} | ${[-50, -100]}
${"s"} | ${[_, -20]} | ${[100, 60]} | ${[0, 20]}
`(
"Resizes from center on handle $handle",
({ handle, move, dimensions, topLeft }) => {
render(<App />);
const rectangle = UI.createElement("rectangle", elemData);
resize(rectangle, handle, move, { alt: true });
const element = h.elements[0];
expect([element.width, element.height]).toEqual(dimensions);
expect([element.x, element.y]).toEqual(topLeft);
},
);
it.each`
handle | move | dimensions | topLeft
${"nw"} | ${[100, 120]} | ${[140, 140]} | ${[-20, -20]}
${"e"} | ${[-130, _]} | ${[160, 160]} | ${[-30, -30]}
`(
"Resizes from center, flips and keeps side rations on handle $handle",
({ handle, move, dimensions, topLeft }) => {
render(<App />);
const rectangle = UI.createElement("rectangle", elemData);
resize(rectangle, handle, move, { alt: true, shift: true });
const element = h.elements[0];
expect([element.width, element.height]).toEqual(dimensions);
expect([element.x, element.y]).toEqual(topLeft);
},
);
});
describe("resize element with aspect ratio when SHIFT is clicked", () => {
it("rectangle", async () => {
await render(<ExcalidrawApp />);
const rectangle = UI.createElement("rectangle", {
x: 0,
width: 30,
height: 50,
});
mouse.select(rectangle);
const se = getTransformHandles(rectangle, h.state.zoom, "mouse").se!;
const clientX = se[0] + se[2] / 2;
const clientY = se[1] + se[3] / 2;
Keyboard.withModifierKeys({ shift: true }, () => {
mouse.reset();
mouse.down(clientX, clientY);
mouse.move(1, 1);
mouse.up();
});
expect([h.elements[0].width, h.elements[0].height]).toEqual([51, 51]);
function resize(
element: ExcalidrawElement,
handleDir: TransformHandleDirection,
mouseMove: [number, number],
keyboardModifiers: KeyboardModifiers = {},
) {
mouse.select(element);
const handle = getTransformHandles(element, h.state.zoom, "mouse")[
handleDir
]!;
const clientX = handle[0] + handle[2] / 2;
const clientY = handle[1] + handle[3] / 2;
Keyboard.withModifierKeys(keyboardModifiers, () => {
mouse.reset();
mouse.down(clientX, clientY);
mouse.move(mouseMove[0], mouseMove[1]);
mouse.up();
});
});
}