Browse Source
Co-authored-by: Maximilian Massing <maximilian.massing@googlemail.com> Co-authored-by: Sven Kube <github@sven-kube.de> Co-authored-by: Maximilian Massing <massing@sipgate.de>pull/2324/head
9 changed files with 1087 additions and 2 deletions
@ -0,0 +1,221 @@
@@ -0,0 +1,221 @@
|
||||
import React from "react"; |
||||
import { KEYS } from "../keys"; |
||||
import { t } from "../i18n"; |
||||
import { register } from "./register"; |
||||
import { |
||||
AlignBottomIcon, |
||||
AlignLeftIcon, |
||||
AlignRightIcon, |
||||
AlignTopIcon, |
||||
CenterHorizontallyIcon, |
||||
CenterVerticallyIcon, |
||||
} from "../components/icons"; |
||||
import { getSelectedElements, isSomeElementSelected } from "../scene"; |
||||
import { getElementMap, getNonDeletedElements } from "../element"; |
||||
import { ToolButton } from "../components/ToolButton"; |
||||
import { ExcalidrawElement } from "../element/types"; |
||||
import { AppState } from "../types"; |
||||
import { alignElements, Alignment } from "../align"; |
||||
import { getShortcutKey } from "../utils"; |
||||
|
||||
const enableActionGroup = ( |
||||
elements: readonly ExcalidrawElement[], |
||||
appState: AppState, |
||||
) => getSelectedElements(getNonDeletedElements(elements), appState).length > 1; |
||||
|
||||
function alignSelectedElements( |
||||
elements: readonly ExcalidrawElement[], |
||||
appState: Readonly<AppState>, |
||||
alignment: Alignment, |
||||
) { |
||||
const selectedElements = getSelectedElements( |
||||
getNonDeletedElements(elements), |
||||
appState, |
||||
); |
||||
|
||||
const updatedElements = alignElements(selectedElements, alignment); |
||||
|
||||
const updatedElementsMap = getElementMap(updatedElements); |
||||
|
||||
return elements.map((element) => updatedElementsMap[element.id] || element); |
||||
} |
||||
|
||||
export const actionAlignTop = register({ |
||||
name: "alignTop", |
||||
perform: (elements, appState) => { |
||||
return { |
||||
appState, |
||||
elements: alignSelectedElements(elements, appState, { |
||||
position: "start", |
||||
axis: "y", |
||||
}), |
||||
commitToHistory: true, |
||||
}; |
||||
}, |
||||
keyTest: (event) => { |
||||
return ( |
||||
event[KEYS.CTRL_OR_CMD] && event.shiftKey && event.key === KEYS.ARROW_UP |
||||
); |
||||
}, |
||||
PanelComponent: ({ elements, appState, updateData }) => ( |
||||
<ToolButton |
||||
hidden={!enableActionGroup(elements, appState)} |
||||
type="button" |
||||
icon={<AlignTopIcon appearance={appState.appearance} />} |
||||
onClick={() => updateData(null)} |
||||
title={`${t("labels.alignTop")} — ${getShortcutKey( |
||||
"CtrlOrCmd+Shift+Up", |
||||
)}`}
|
||||
aria-label={t("labels.alignTop")} |
||||
visible={isSomeElementSelected(getNonDeletedElements(elements), appState)} |
||||
/> |
||||
), |
||||
}); |
||||
|
||||
export const actionAlignBottom = register({ |
||||
name: "alignBottom", |
||||
perform: (elements, appState) => { |
||||
return { |
||||
appState, |
||||
elements: alignSelectedElements(elements, appState, { |
||||
position: "end", |
||||
axis: "y", |
||||
}), |
||||
commitToHistory: true, |
||||
}; |
||||
}, |
||||
keyTest: (event) => { |
||||
return ( |
||||
event[KEYS.CTRL_OR_CMD] && event.shiftKey && event.key === KEYS.ARROW_DOWN |
||||
); |
||||
}, |
||||
PanelComponent: ({ elements, appState, updateData }) => ( |
||||
<ToolButton |
||||
hidden={!enableActionGroup(elements, appState)} |
||||
type="button" |
||||
icon={<AlignBottomIcon appearance={appState.appearance} />} |
||||
onClick={() => updateData(null)} |
||||
title={`${t("labels.alignBottom")} — ${getShortcutKey( |
||||
"CtrlOrCmd+Shift+Down", |
||||
)}`}
|
||||
aria-label={t("labels.alignBottom")} |
||||
visible={isSomeElementSelected(getNonDeletedElements(elements), appState)} |
||||
/> |
||||
), |
||||
}); |
||||
|
||||
export const actionAlignLeft = register({ |
||||
name: "alignLeft", |
||||
perform: (elements, appState) => { |
||||
return { |
||||
appState, |
||||
elements: alignSelectedElements(elements, appState, { |
||||
position: "start", |
||||
axis: "x", |
||||
}), |
||||
commitToHistory: true, |
||||
}; |
||||
}, |
||||
keyTest: (event) => { |
||||
return ( |
||||
event[KEYS.CTRL_OR_CMD] && event.shiftKey && event.key === KEYS.ARROW_LEFT |
||||
); |
||||
}, |
||||
PanelComponent: ({ elements, appState, updateData }) => ( |
||||
<ToolButton |
||||
hidden={!enableActionGroup(elements, appState)} |
||||
type="button" |
||||
icon={<AlignLeftIcon appearance={appState.appearance} />} |
||||
onClick={() => updateData(null)} |
||||
title={`${t("labels.alignLeft")} — ${getShortcutKey( |
||||
"CtrlOrCmd+Shift+Left", |
||||
)}`}
|
||||
aria-label={t("labels.alignLeft")} |
||||
visible={isSomeElementSelected(getNonDeletedElements(elements), appState)} |
||||
/> |
||||
), |
||||
}); |
||||
|
||||
export const actionAlignRight = register({ |
||||
name: "alignRight", |
||||
perform: (elements, appState) => { |
||||
return { |
||||
appState, |
||||
elements: alignSelectedElements(elements, appState, { |
||||
position: "end", |
||||
axis: "x", |
||||
}), |
||||
commitToHistory: true, |
||||
}; |
||||
}, |
||||
keyTest: (event) => { |
||||
return ( |
||||
event[KEYS.CTRL_OR_CMD] && |
||||
event.shiftKey && |
||||
event.key === KEYS.ARROW_RIGHT |
||||
); |
||||
}, |
||||
PanelComponent: ({ elements, appState, updateData }) => ( |
||||
<ToolButton |
||||
hidden={!enableActionGroup(elements, appState)} |
||||
type="button" |
||||
icon={<AlignRightIcon appearance={appState.appearance} />} |
||||
onClick={() => updateData(null)} |
||||
title={`${t("labels.alignRight")} — ${getShortcutKey( |
||||
"CtrlOrCmd+Shift+Right", |
||||
)}`}
|
||||
aria-label={t("labels.alignRight")} |
||||
visible={isSomeElementSelected(getNonDeletedElements(elements), appState)} |
||||
/> |
||||
), |
||||
}); |
||||
|
||||
export const actionAlignVerticallyCentered = register({ |
||||
name: "alignVerticallyCentered", |
||||
perform: (elements, appState) => { |
||||
return { |
||||
appState, |
||||
elements: alignSelectedElements(elements, appState, { |
||||
position: "center", |
||||
axis: "y", |
||||
}), |
||||
commitToHistory: true, |
||||
}; |
||||
}, |
||||
PanelComponent: ({ elements, appState, updateData }) => ( |
||||
<ToolButton |
||||
hidden={!enableActionGroup(elements, appState)} |
||||
type="button" |
||||
icon={<CenterVerticallyIcon appearance={appState.appearance} />} |
||||
onClick={() => updateData(null)} |
||||
title={t("labels.centerVertically")} |
||||
aria-label={t("labels.centerVertically")} |
||||
visible={isSomeElementSelected(getNonDeletedElements(elements), appState)} |
||||
/> |
||||
), |
||||
}); |
||||
|
||||
export const actionAlignHorizontallyCentered = register({ |
||||
name: "alignHorizontallyCentered", |
||||
perform: (elements, appState) => { |
||||
return { |
||||
appState, |
||||
elements: alignSelectedElements(elements, appState, { |
||||
position: "center", |
||||
axis: "x", |
||||
}), |
||||
commitToHistory: true, |
||||
}; |
||||
}, |
||||
PanelComponent: ({ elements, appState, updateData }) => ( |
||||
<ToolButton |
||||
hidden={!enableActionGroup(elements, appState)} |
||||
type="button" |
||||
icon={<CenterHorizontallyIcon appearance={appState.appearance} />} |
||||
onClick={() => updateData(null)} |
||||
title={t("labels.centerHorizontally")} |
||||
aria-label={t("labels.centerHorizontally")} |
||||
visible={isSomeElementSelected(getNonDeletedElements(elements), appState)} |
||||
/> |
||||
), |
||||
}); |
||||
@ -0,0 +1,95 @@
@@ -0,0 +1,95 @@
|
||||
import { ExcalidrawElement } from "./element/types"; |
||||
import { newElementWith } from "./element/mutateElement"; |
||||
import { getCommonBounds } from "./element"; |
||||
|
||||
interface Box { |
||||
minX: number; |
||||
minY: number; |
||||
maxX: number; |
||||
maxY: number; |
||||
} |
||||
|
||||
export interface Alignment { |
||||
position: "start" | "center" | "end"; |
||||
axis: "x" | "y"; |
||||
} |
||||
|
||||
export const alignElements = ( |
||||
selectedElements: ExcalidrawElement[], |
||||
alignment: Alignment, |
||||
): ExcalidrawElement[] => { |
||||
const groups: ExcalidrawElement[][] = getMaximumGroups(selectedElements); |
||||
|
||||
const selectionBoundingBox = getCommonBoundingBox(selectedElements); |
||||
|
||||
return groups.flatMap((group) => { |
||||
const translation = calculateTranslation( |
||||
group, |
||||
selectionBoundingBox, |
||||
alignment, |
||||
); |
||||
return group.map((element) => |
||||
newElementWith(element, { |
||||
x: element.x + translation.x, |
||||
y: element.y + translation.y, |
||||
}), |
||||
); |
||||
}); |
||||
}; |
||||
|
||||
export const getMaximumGroups = ( |
||||
elements: ExcalidrawElement[], |
||||
): ExcalidrawElement[][] => { |
||||
const groups: Map<String, ExcalidrawElement[]> = new Map< |
||||
String, |
||||
ExcalidrawElement[] |
||||
>(); |
||||
|
||||
elements.forEach((element: ExcalidrawElement) => { |
||||
const groupId = |
||||
element.groupIds.length === 0 |
||||
? element.id |
||||
: element.groupIds[element.groupIds.length - 1]; |
||||
|
||||
const currentGroupMembers = groups.get(groupId) || []; |
||||
|
||||
groups.set(groupId, [...currentGroupMembers, element]); |
||||
}); |
||||
|
||||
return Array.from(groups.values()); |
||||
}; |
||||
|
||||
const calculateTranslation = ( |
||||
group: ExcalidrawElement[], |
||||
selectionBoundingBox: Box, |
||||
{ axis, position }: Alignment, |
||||
): { x: number; y: number } => { |
||||
const groupBoundingBox = getCommonBoundingBox(group); |
||||
|
||||
const [min, max]: ["minX" | "minY", "maxX" | "maxY"] = |
||||
axis === "x" ? ["minX", "maxX"] : ["minY", "maxY"]; |
||||
|
||||
const noTranslation = { x: 0, y: 0 }; |
||||
if (position === "start") { |
||||
return { |
||||
...noTranslation, |
||||
[axis]: selectionBoundingBox[min] - groupBoundingBox[min], |
||||
}; |
||||
} else if (position === "end") { |
||||
return { |
||||
...noTranslation, |
||||
[axis]: selectionBoundingBox[max] - groupBoundingBox[max], |
||||
}; |
||||
} // else if (position === "center") {
|
||||
return { |
||||
...noTranslation, |
||||
[axis]: |
||||
(selectionBoundingBox[min] + selectionBoundingBox[max]) / 2 - |
||||
(groupBoundingBox[min] + groupBoundingBox[max]) / 2, |
||||
}; |
||||
}; |
||||
|
||||
function getCommonBoundingBox(elements: ExcalidrawElement[]): Box { |
||||
const [minX, minY, maxX, maxY] = getCommonBounds(elements); |
||||
return { minX, minY, maxX, maxY }; |
||||
} |
||||
@ -0,0 +1,579 @@
@@ -0,0 +1,579 @@
|
||||
import React from "react"; |
||||
import ReactDOM from "react-dom"; |
||||
import { render } from "./test-utils"; |
||||
import App from "../components/App"; |
||||
import { setLanguage } from "../i18n"; |
||||
import { UI, Pointer, Keyboard } from "./helpers/ui"; |
||||
import { API } from "./helpers/api"; |
||||
import { KEYS } from "../keys"; |
||||
import { |
||||
actionAlignVerticallyCentered, |
||||
actionAlignHorizontallyCentered, |
||||
actionGroup, |
||||
actionAlignTop, |
||||
actionAlignBottom, |
||||
actionAlignLeft, |
||||
actionAlignRight, |
||||
} from "../actions"; |
||||
|
||||
const { h } = window; |
||||
|
||||
const mouse = new Pointer("mouse"); |
||||
|
||||
beforeEach(async () => { |
||||
// Unmount ReactDOM from root
|
||||
ReactDOM.unmountComponentAtNode(document.getElementById("root")!); |
||||
mouse.reset(); |
||||
|
||||
await setLanguage("en.json"); |
||||
render(<App />); |
||||
}); |
||||
|
||||
function createAndSelectTwoRectangles() { |
||||
UI.clickTool("rectangle"); |
||||
mouse.down(); |
||||
mouse.up(100, 100); |
||||
|
||||
UI.clickTool("rectangle"); |
||||
mouse.down(10, 10); |
||||
mouse.up(100, 100); |
||||
|
||||
// Select the first element.
|
||||
// The second rectangle is already reselected because it was the last element created
|
||||
mouse.reset(); |
||||
Keyboard.withModifierKeys({ shift: true }, () => { |
||||
mouse.click(); |
||||
}); |
||||
} |
||||
|
||||
function createAndSelectTwoRectanglesWithDifferentSizes() { |
||||
UI.clickTool("rectangle"); |
||||
mouse.down(); |
||||
mouse.up(100, 100); |
||||
|
||||
UI.clickTool("rectangle"); |
||||
mouse.down(10, 10); |
||||
mouse.up(110, 110); |
||||
|
||||
// Select the first element.
|
||||
// The second rectangle is already reselected because it was the last element created
|
||||
mouse.reset(); |
||||
Keyboard.withModifierKeys({ shift: true }, () => { |
||||
mouse.click(); |
||||
}); |
||||
} |
||||
|
||||
it("aligns two objects correctly to the top", () => { |
||||
createAndSelectTwoRectangles(); |
||||
|
||||
expect(API.getSelectedElements()[0].x).toEqual(0); |
||||
expect(API.getSelectedElements()[1].x).toEqual(110); |
||||
|
||||
expect(API.getSelectedElements()[0].y).toEqual(0); |
||||
expect(API.getSelectedElements()[1].y).toEqual(110); |
||||
|
||||
Keyboard.withModifierKeys({ ctrl: true, shift: true }, () => { |
||||
Keyboard.keyPress(KEYS.ARROW_UP); |
||||
}); |
||||
|
||||
// Check if x position did not change
|
||||
expect(API.getSelectedElements()[0].x).toEqual(0); |
||||
expect(API.getSelectedElements()[1].x).toEqual(110); |
||||
|
||||
expect(API.getSelectedElements()[0].y).toEqual(0); |
||||
expect(API.getSelectedElements()[1].y).toEqual(0); |
||||
}); |
||||
|
||||
it("aligns two objects correctly to the bottom", () => { |
||||
createAndSelectTwoRectangles(); |
||||
|
||||
expect(API.getSelectedElements()[0].x).toEqual(0); |
||||
expect(API.getSelectedElements()[1].x).toEqual(110); |
||||
|
||||
expect(API.getSelectedElements()[0].y).toEqual(0); |
||||
expect(API.getSelectedElements()[1].y).toEqual(110); |
||||
|
||||
Keyboard.withModifierKeys({ ctrl: true, shift: true }, () => { |
||||
Keyboard.keyPress(KEYS.ARROW_DOWN); |
||||
}); |
||||
|
||||
// Check if x position did not change
|
||||
expect(API.getSelectedElements()[0].x).toEqual(0); |
||||
expect(API.getSelectedElements()[1].x).toEqual(110); |
||||
|
||||
expect(API.getSelectedElements()[0].y).toEqual(110); |
||||
expect(API.getSelectedElements()[1].y).toEqual(110); |
||||
}); |
||||
|
||||
it("aligns two objects correctly to the left", () => { |
||||
createAndSelectTwoRectangles(); |
||||
|
||||
expect(API.getSelectedElements()[0].x).toEqual(0); |
||||
expect(API.getSelectedElements()[1].x).toEqual(110); |
||||
|
||||
expect(API.getSelectedElements()[0].y).toEqual(0); |
||||
expect(API.getSelectedElements()[1].y).toEqual(110); |
||||
|
||||
Keyboard.withModifierKeys({ ctrl: true, shift: true }, () => { |
||||
Keyboard.keyPress(KEYS.ARROW_LEFT); |
||||
}); |
||||
|
||||
expect(API.getSelectedElements()[0].x).toEqual(0); |
||||
expect(API.getSelectedElements()[1].x).toEqual(0); |
||||
|
||||
// Check if y position did not change
|
||||
expect(API.getSelectedElements()[0].y).toEqual(0); |
||||
expect(API.getSelectedElements()[1].y).toEqual(110); |
||||
}); |
||||
|
||||
it("aligns two objects correctly to the right", () => { |
||||
createAndSelectTwoRectangles(); |
||||
|
||||
expect(API.getSelectedElements()[0].x).toEqual(0); |
||||
expect(API.getSelectedElements()[1].x).toEqual(110); |
||||
|
||||
expect(API.getSelectedElements()[0].y).toEqual(0); |
||||
expect(API.getSelectedElements()[1].y).toEqual(110); |
||||
|
||||
Keyboard.withModifierKeys({ ctrl: true, shift: true }, () => { |
||||
Keyboard.keyPress(KEYS.ARROW_RIGHT); |
||||
}); |
||||
|
||||
expect(API.getSelectedElements()[0].x).toEqual(110); |
||||
expect(API.getSelectedElements()[1].x).toEqual(110); |
||||
|
||||
// Check if y position did not change
|
||||
expect(API.getSelectedElements()[0].y).toEqual(0); |
||||
expect(API.getSelectedElements()[1].y).toEqual(110); |
||||
}); |
||||
|
||||
it("centers two objects with different sizes correctly vertically", () => { |
||||
createAndSelectTwoRectanglesWithDifferentSizes(); |
||||
|
||||
expect(API.getSelectedElements()[0].x).toEqual(0); |
||||
expect(API.getSelectedElements()[1].x).toEqual(110); |
||||
|
||||
expect(API.getSelectedElements()[0].y).toEqual(0); |
||||
expect(API.getSelectedElements()[1].y).toEqual(110); |
||||
|
||||
h.app.actionManager.executeAction(actionAlignVerticallyCentered); |
||||
|
||||
// Check if x position did not change
|
||||
expect(API.getSelectedElements()[0].x).toEqual(0); |
||||
expect(API.getSelectedElements()[1].x).toEqual(110); |
||||
|
||||
expect(API.getSelectedElements()[0].y).toEqual(60); |
||||
expect(API.getSelectedElements()[1].y).toEqual(55); |
||||
}); |
||||
|
||||
it("centers two objects with different sizes correctly horizontally", () => { |
||||
createAndSelectTwoRectanglesWithDifferentSizes(); |
||||
|
||||
expect(API.getSelectedElements()[0].x).toEqual(0); |
||||
expect(API.getSelectedElements()[1].x).toEqual(110); |
||||
|
||||
expect(API.getSelectedElements()[0].y).toEqual(0); |
||||
expect(API.getSelectedElements()[1].y).toEqual(110); |
||||
|
||||
h.app.actionManager.executeAction(actionAlignHorizontallyCentered); |
||||
|
||||
expect(API.getSelectedElements()[0].x).toEqual(60); |
||||
expect(API.getSelectedElements()[1].x).toEqual(55); |
||||
|
||||
// Check if y position did not change
|
||||
expect(API.getSelectedElements()[0].y).toEqual(0); |
||||
expect(API.getSelectedElements()[1].y).toEqual(110); |
||||
}); |
||||
|
||||
function createAndSelectGroupAndRectangle() { |
||||
UI.clickTool("rectangle"); |
||||
mouse.down(); |
||||
mouse.up(100, 100); |
||||
|
||||
UI.clickTool("rectangle"); |
||||
mouse.down(0, 0); |
||||
mouse.up(100, 100); |
||||
|
||||
// Select the first element.
|
||||
// The second rectangle is already reselected because it was the last element created
|
||||
mouse.reset(); |
||||
Keyboard.withModifierKeys({ shift: true }, () => { |
||||
mouse.click(); |
||||
}); |
||||
|
||||
h.app.actionManager.executeAction(actionGroup); |
||||
|
||||
mouse.reset(); |
||||
UI.clickTool("rectangle"); |
||||
mouse.down(200, 200); |
||||
mouse.up(100, 100); |
||||
|
||||
// Add the created group to the current selection
|
||||
mouse.restorePosition(0, 0); |
||||
Keyboard.withModifierKeys({ shift: true }, () => { |
||||
mouse.click(); |
||||
}); |
||||
} |
||||
|
||||
it("aligns a group with another element correctly to the top", () => { |
||||
createAndSelectGroupAndRectangle(); |
||||
|
||||
expect(API.getSelectedElements()[0].y).toEqual(0); |
||||
expect(API.getSelectedElements()[1].y).toEqual(100); |
||||
expect(API.getSelectedElements()[2].y).toEqual(200); |
||||
|
||||
h.app.actionManager.executeAction(actionAlignTop); |
||||
|
||||
expect(API.getSelectedElements()[0].y).toEqual(0); |
||||
expect(API.getSelectedElements()[1].y).toEqual(100); |
||||
expect(API.getSelectedElements()[2].y).toEqual(0); |
||||
}); |
||||
|
||||
it("aligns a group with another element correctly to the bottom", () => { |
||||
createAndSelectGroupAndRectangle(); |
||||
|
||||
expect(API.getSelectedElements()[0].y).toEqual(0); |
||||
expect(API.getSelectedElements()[1].y).toEqual(100); |
||||
expect(API.getSelectedElements()[2].y).toEqual(200); |
||||
|
||||
h.app.actionManager.executeAction(actionAlignBottom); |
||||
|
||||
expect(API.getSelectedElements()[0].y).toEqual(100); |
||||
expect(API.getSelectedElements()[1].y).toEqual(200); |
||||
expect(API.getSelectedElements()[2].y).toEqual(200); |
||||
}); |
||||
|
||||
it("aligns a group with another element correctly to the left", () => { |
||||
createAndSelectGroupAndRectangle(); |
||||
|
||||
expect(API.getSelectedElements()[0].x).toEqual(0); |
||||
expect(API.getSelectedElements()[1].x).toEqual(100); |
||||
expect(API.getSelectedElements()[2].x).toEqual(200); |
||||
|
||||
h.app.actionManager.executeAction(actionAlignLeft); |
||||
|
||||
expect(API.getSelectedElements()[0].x).toEqual(0); |
||||
expect(API.getSelectedElements()[1].x).toEqual(100); |
||||
expect(API.getSelectedElements()[2].x).toEqual(0); |
||||
}); |
||||
|
||||
it("aligns a group with another element correctly to the right", () => { |
||||
createAndSelectGroupAndRectangle(); |
||||
|
||||
expect(API.getSelectedElements()[0].x).toEqual(0); |
||||
expect(API.getSelectedElements()[1].x).toEqual(100); |
||||
expect(API.getSelectedElements()[2].x).toEqual(200); |
||||
|
||||
h.app.actionManager.executeAction(actionAlignRight); |
||||
|
||||
expect(API.getSelectedElements()[0].x).toEqual(100); |
||||
expect(API.getSelectedElements()[1].x).toEqual(200); |
||||
expect(API.getSelectedElements()[2].x).toEqual(200); |
||||
}); |
||||
|
||||
it("centers a group with another element correctly vertically", () => { |
||||
createAndSelectGroupAndRectangle(); |
||||
|
||||
expect(API.getSelectedElements()[0].y).toEqual(0); |
||||
expect(API.getSelectedElements()[1].y).toEqual(100); |
||||
expect(API.getSelectedElements()[2].y).toEqual(200); |
||||
|
||||
h.app.actionManager.executeAction(actionAlignVerticallyCentered); |
||||
|
||||
expect(API.getSelectedElements()[0].y).toEqual(50); |
||||
expect(API.getSelectedElements()[1].y).toEqual(150); |
||||
expect(API.getSelectedElements()[2].y).toEqual(100); |
||||
}); |
||||
|
||||
it("centers a group with another element correctly horizontally", () => { |
||||
createAndSelectGroupAndRectangle(); |
||||
|
||||
expect(API.getSelectedElements()[0].x).toEqual(0); |
||||
expect(API.getSelectedElements()[1].x).toEqual(100); |
||||
expect(API.getSelectedElements()[2].x).toEqual(200); |
||||
|
||||
h.app.actionManager.executeAction(actionAlignHorizontallyCentered); |
||||
|
||||
expect(API.getSelectedElements()[0].x).toEqual(50); |
||||
expect(API.getSelectedElements()[1].x).toEqual(150); |
||||
expect(API.getSelectedElements()[2].x).toEqual(100); |
||||
}); |
||||
|
||||
function createAndSelectTwoGroups() { |
||||
UI.clickTool("rectangle"); |
||||
mouse.down(); |
||||
mouse.up(100, 100); |
||||
|
||||
UI.clickTool("rectangle"); |
||||
mouse.down(0, 0); |
||||
mouse.up(100, 100); |
||||
|
||||
// Select the first element.
|
||||
// The second rectangle is already selected because it was the last element created
|
||||
mouse.reset(); |
||||
Keyboard.withModifierKeys({ shift: true }, () => { |
||||
mouse.click(); |
||||
}); |
||||
|
||||
h.app.actionManager.executeAction(actionGroup); |
||||
|
||||
mouse.reset(); |
||||
UI.clickTool("rectangle"); |
||||
mouse.down(200, 200); |
||||
mouse.up(100, 100); |
||||
|
||||
UI.clickTool("rectangle"); |
||||
mouse.down(); |
||||
mouse.up(100, 100); |
||||
|
||||
mouse.restorePosition(200, 200); |
||||
Keyboard.withModifierKeys({ shift: true }, () => { |
||||
mouse.click(); |
||||
}); |
||||
|
||||
h.app.actionManager.executeAction(actionGroup); |
||||
|
||||
// Select the first group.
|
||||
// The second group is already selected because it was the last group created
|
||||
mouse.reset(); |
||||
Keyboard.withModifierKeys({ shift: true }, () => { |
||||
mouse.click(); |
||||
}); |
||||
} |
||||
|
||||
it("aligns two groups correctly to the top", () => { |
||||
createAndSelectTwoGroups(); |
||||
|
||||
expect(API.getSelectedElements()[0].y).toEqual(0); |
||||
expect(API.getSelectedElements()[1].y).toEqual(100); |
||||
expect(API.getSelectedElements()[2].y).toEqual(200); |
||||
expect(API.getSelectedElements()[3].y).toEqual(300); |
||||
|
||||
h.app.actionManager.executeAction(actionAlignTop); |
||||
|
||||
expect(API.getSelectedElements()[0].y).toEqual(0); |
||||
expect(API.getSelectedElements()[1].y).toEqual(100); |
||||
expect(API.getSelectedElements()[2].y).toEqual(0); |
||||
expect(API.getSelectedElements()[3].y).toEqual(100); |
||||
}); |
||||
|
||||
it("aligns two groups correctly to the bottom", () => { |
||||
createAndSelectTwoGroups(); |
||||
|
||||
expect(API.getSelectedElements()[0].y).toEqual(0); |
||||
expect(API.getSelectedElements()[1].y).toEqual(100); |
||||
expect(API.getSelectedElements()[2].y).toEqual(200); |
||||
expect(API.getSelectedElements()[3].y).toEqual(300); |
||||
|
||||
h.app.actionManager.executeAction(actionAlignBottom); |
||||
|
||||
expect(API.getSelectedElements()[0].y).toEqual(200); |
||||
expect(API.getSelectedElements()[1].y).toEqual(300); |
||||
expect(API.getSelectedElements()[2].y).toEqual(200); |
||||
expect(API.getSelectedElements()[3].y).toEqual(300); |
||||
}); |
||||
|
||||
it("aligns two groups correctly to the left", () => { |
||||
createAndSelectTwoGroups(); |
||||
|
||||
expect(API.getSelectedElements()[0].x).toEqual(0); |
||||
expect(API.getSelectedElements()[1].x).toEqual(100); |
||||
expect(API.getSelectedElements()[2].x).toEqual(200); |
||||
expect(API.getSelectedElements()[3].x).toEqual(300); |
||||
|
||||
h.app.actionManager.executeAction(actionAlignLeft); |
||||
|
||||
expect(API.getSelectedElements()[0].x).toEqual(0); |
||||
expect(API.getSelectedElements()[1].x).toEqual(100); |
||||
expect(API.getSelectedElements()[2].x).toEqual(0); |
||||
expect(API.getSelectedElements()[3].x).toEqual(100); |
||||
}); |
||||
|
||||
it("aligns two groups correctly to the right", () => { |
||||
createAndSelectTwoGroups(); |
||||
|
||||
expect(API.getSelectedElements()[0].x).toEqual(0); |
||||
expect(API.getSelectedElements()[1].x).toEqual(100); |
||||
expect(API.getSelectedElements()[2].x).toEqual(200); |
||||
expect(API.getSelectedElements()[3].x).toEqual(300); |
||||
|
||||
h.app.actionManager.executeAction(actionAlignRight); |
||||
|
||||
expect(API.getSelectedElements()[0].x).toEqual(200); |
||||
expect(API.getSelectedElements()[1].x).toEqual(300); |
||||
expect(API.getSelectedElements()[2].x).toEqual(200); |
||||
expect(API.getSelectedElements()[3].x).toEqual(300); |
||||
}); |
||||
|
||||
it("centers two groups correctly vertically", () => { |
||||
createAndSelectTwoGroups(); |
||||
|
||||
expect(API.getSelectedElements()[0].y).toEqual(0); |
||||
expect(API.getSelectedElements()[1].y).toEqual(100); |
||||
expect(API.getSelectedElements()[2].y).toEqual(200); |
||||
expect(API.getSelectedElements()[3].y).toEqual(300); |
||||
|
||||
h.app.actionManager.executeAction(actionAlignVerticallyCentered); |
||||
|
||||
expect(API.getSelectedElements()[0].y).toEqual(100); |
||||
expect(API.getSelectedElements()[1].y).toEqual(200); |
||||
expect(API.getSelectedElements()[2].y).toEqual(100); |
||||
expect(API.getSelectedElements()[3].y).toEqual(200); |
||||
}); |
||||
|
||||
it("centers two groups correctly horizontally", () => { |
||||
createAndSelectTwoGroups(); |
||||
|
||||
expect(API.getSelectedElements()[0].x).toEqual(0); |
||||
expect(API.getSelectedElements()[1].x).toEqual(100); |
||||
expect(API.getSelectedElements()[2].x).toEqual(200); |
||||
expect(API.getSelectedElements()[3].x).toEqual(300); |
||||
|
||||
h.app.actionManager.executeAction(actionAlignHorizontallyCentered); |
||||
|
||||
expect(API.getSelectedElements()[0].x).toEqual(100); |
||||
expect(API.getSelectedElements()[1].x).toEqual(200); |
||||
expect(API.getSelectedElements()[2].x).toEqual(100); |
||||
expect(API.getSelectedElements()[3].x).toEqual(200); |
||||
}); |
||||
|
||||
function createAndSelectNestedGroupAndRectangle() { |
||||
UI.clickTool("rectangle"); |
||||
mouse.down(); |
||||
mouse.up(100, 100); |
||||
|
||||
UI.clickTool("rectangle"); |
||||
mouse.down(0, 0); |
||||
mouse.up(100, 100); |
||||
|
||||
// Select the first element.
|
||||
// The second rectangle is already reselected because it was the last element created
|
||||
mouse.reset(); |
||||
Keyboard.withModifierKeys({ shift: true }, () => { |
||||
mouse.click(); |
||||
}); |
||||
|
||||
// Create first group of rectangles
|
||||
h.app.actionManager.executeAction(actionGroup); |
||||
|
||||
mouse.reset(); |
||||
UI.clickTool("rectangle"); |
||||
mouse.down(200, 200); |
||||
mouse.up(100, 100); |
||||
|
||||
// Add group to current selection
|
||||
mouse.restorePosition(0, 0); |
||||
Keyboard.withModifierKeys({ shift: true }, () => { |
||||
mouse.click(); |
||||
}); |
||||
|
||||
// Create the nested group
|
||||
h.app.actionManager.executeAction(actionGroup); |
||||
|
||||
mouse.reset(); |
||||
UI.clickTool("rectangle"); |
||||
mouse.down(300, 300); |
||||
mouse.up(100, 100); |
||||
|
||||
// Select the nested group, the rectangle is already selected
|
||||
mouse.reset(); |
||||
Keyboard.withModifierKeys({ shift: true }, () => { |
||||
mouse.click(); |
||||
}); |
||||
} |
||||
|
||||
it("aligns nested group and other element correctly to the top", () => { |
||||
createAndSelectNestedGroupAndRectangle(); |
||||
|
||||
expect(API.getSelectedElements()[0].y).toEqual(0); |
||||
expect(API.getSelectedElements()[1].y).toEqual(100); |
||||
expect(API.getSelectedElements()[2].y).toEqual(200); |
||||
expect(API.getSelectedElements()[3].y).toEqual(300); |
||||
|
||||
h.app.actionManager.executeAction(actionAlignTop); |
||||
|
||||
expect(API.getSelectedElements()[0].y).toEqual(0); |
||||
expect(API.getSelectedElements()[1].y).toEqual(100); |
||||
expect(API.getSelectedElements()[2].y).toEqual(200); |
||||
expect(API.getSelectedElements()[3].y).toEqual(0); |
||||
}); |
||||
|
||||
it("aligns nested group and other element correctly to the bottom", () => { |
||||
createAndSelectNestedGroupAndRectangle(); |
||||
|
||||
expect(API.getSelectedElements()[0].y).toEqual(0); |
||||
expect(API.getSelectedElements()[1].y).toEqual(100); |
||||
expect(API.getSelectedElements()[2].y).toEqual(200); |
||||
expect(API.getSelectedElements()[3].y).toEqual(300); |
||||
|
||||
h.app.actionManager.executeAction(actionAlignBottom); |
||||
|
||||
expect(API.getSelectedElements()[0].y).toEqual(100); |
||||
expect(API.getSelectedElements()[1].y).toEqual(200); |
||||
expect(API.getSelectedElements()[2].y).toEqual(300); |
||||
expect(API.getSelectedElements()[3].y).toEqual(300); |
||||
}); |
||||
|
||||
it("aligns nested group and other element correctly to the left", () => { |
||||
createAndSelectNestedGroupAndRectangle(); |
||||
|
||||
expect(API.getSelectedElements()[0].x).toEqual(0); |
||||
expect(API.getSelectedElements()[1].x).toEqual(100); |
||||
expect(API.getSelectedElements()[2].x).toEqual(200); |
||||
expect(API.getSelectedElements()[3].x).toEqual(300); |
||||
|
||||
h.app.actionManager.executeAction(actionAlignLeft); |
||||
|
||||
expect(API.getSelectedElements()[0].x).toEqual(0); |
||||
expect(API.getSelectedElements()[1].x).toEqual(100); |
||||
expect(API.getSelectedElements()[2].x).toEqual(200); |
||||
expect(API.getSelectedElements()[3].x).toEqual(0); |
||||
}); |
||||
|
||||
it("aligns nested group and other element correctly to the right", () => { |
||||
createAndSelectNestedGroupAndRectangle(); |
||||
|
||||
expect(API.getSelectedElements()[0].x).toEqual(0); |
||||
expect(API.getSelectedElements()[1].x).toEqual(100); |
||||
expect(API.getSelectedElements()[2].x).toEqual(200); |
||||
expect(API.getSelectedElements()[3].x).toEqual(300); |
||||
|
||||
h.app.actionManager.executeAction(actionAlignRight); |
||||
|
||||
expect(API.getSelectedElements()[0].x).toEqual(100); |
||||
expect(API.getSelectedElements()[1].x).toEqual(200); |
||||
expect(API.getSelectedElements()[2].x).toEqual(300); |
||||
expect(API.getSelectedElements()[3].x).toEqual(300); |
||||
}); |
||||
|
||||
it("centers nested group and other element correctly vertically", () => { |
||||
createAndSelectNestedGroupAndRectangle(); |
||||
|
||||
expect(API.getSelectedElements()[0].y).toEqual(0); |
||||
expect(API.getSelectedElements()[1].y).toEqual(100); |
||||
expect(API.getSelectedElements()[2].y).toEqual(200); |
||||
expect(API.getSelectedElements()[3].y).toEqual(300); |
||||
|
||||
h.app.actionManager.executeAction(actionAlignVerticallyCentered); |
||||
|
||||
expect(API.getSelectedElements()[0].y).toEqual(50); |
||||
expect(API.getSelectedElements()[1].y).toEqual(150); |
||||
expect(API.getSelectedElements()[2].y).toEqual(250); |
||||
expect(API.getSelectedElements()[3].y).toEqual(150); |
||||
}); |
||||
|
||||
it("centers nested group and other element correctly horizontally", () => { |
||||
createAndSelectNestedGroupAndRectangle(); |
||||
|
||||
expect(API.getSelectedElements()[0].x).toEqual(0); |
||||
expect(API.getSelectedElements()[1].x).toEqual(100); |
||||
expect(API.getSelectedElements()[2].x).toEqual(200); |
||||
expect(API.getSelectedElements()[3].x).toEqual(300); |
||||
|
||||
h.app.actionManager.executeAction(actionAlignHorizontallyCentered); |
||||
|
||||
expect(API.getSelectedElements()[0].x).toEqual(50); |
||||
expect(API.getSelectedElements()[1].x).toEqual(150); |
||||
expect(API.getSelectedElements()[2].x).toEqual(250); |
||||
expect(API.getSelectedElements()[3].x).toEqual(150); |
||||
}); |
||||
Loading…
Reference in new issue