mirror of
https://github.com/excalidraw/excalidraw.git
synced 2025-05-03 10:00:07 -04:00
Merge branch 'master' into mtolmacs/fix/small-elbow-routing
This commit is contained in:
commit
6dfa5de66c
7 changed files with 161 additions and 34 deletions
|
@ -112,6 +112,7 @@ export const YOUTUBE_STATES = {
|
||||||
export const ENV = {
|
export const ENV = {
|
||||||
TEST: "test",
|
TEST: "test",
|
||||||
DEVELOPMENT: "development",
|
DEVELOPMENT: "development",
|
||||||
|
PRODUCTION: "production",
|
||||||
};
|
};
|
||||||
|
|
||||||
export const CLASSES = {
|
export const CLASSES = {
|
||||||
|
|
|
@ -739,6 +739,8 @@ export const isTestEnv = () => import.meta.env.MODE === ENV.TEST;
|
||||||
|
|
||||||
export const isDevEnv = () => import.meta.env.MODE === ENV.DEVELOPMENT;
|
export const isDevEnv = () => import.meta.env.MODE === ENV.DEVELOPMENT;
|
||||||
|
|
||||||
|
export const isProdEnv = () => import.meta.env.MODE === ENV.PRODUCTION;
|
||||||
|
|
||||||
export const isServerEnv = () =>
|
export const isServerEnv = () =>
|
||||||
typeof process !== "undefined" && !!process?.env?.NODE_ENV;
|
typeof process !== "undefined" && !!process?.env?.NODE_ENV;
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,8 @@ import {
|
||||||
TEXT_ALIGN,
|
TEXT_ALIGN,
|
||||||
VERTICAL_ALIGN,
|
VERTICAL_ALIGN,
|
||||||
getFontString,
|
getFontString,
|
||||||
|
isProdEnv,
|
||||||
|
invariant,
|
||||||
} from "@excalidraw/common";
|
} from "@excalidraw/common";
|
||||||
|
|
||||||
import type { AppState } from "@excalidraw/excalidraw/types";
|
import type { AppState } from "@excalidraw/excalidraw/types";
|
||||||
|
@ -26,6 +28,8 @@ import {
|
||||||
isTextElement,
|
isTextElement,
|
||||||
} from "./typeChecks";
|
} from "./typeChecks";
|
||||||
|
|
||||||
|
import type { Radians } from "../../math/src";
|
||||||
|
|
||||||
import type { MaybeTransformHandleType } from "./transformHandles";
|
import type { MaybeTransformHandleType } from "./transformHandles";
|
||||||
import type {
|
import type {
|
||||||
ElementsMap,
|
ElementsMap,
|
||||||
|
@ -44,13 +48,25 @@ export const redrawTextBoundingBox = (
|
||||||
informMutation = true,
|
informMutation = true,
|
||||||
) => {
|
) => {
|
||||||
let maxWidth = undefined;
|
let maxWidth = undefined;
|
||||||
|
|
||||||
|
if (!isProdEnv()) {
|
||||||
|
invariant(
|
||||||
|
!container || !isArrowElement(container) || textElement.angle === 0,
|
||||||
|
"text element angle must be 0 if bound to arrow container",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const boundTextUpdates = {
|
const boundTextUpdates = {
|
||||||
x: textElement.x,
|
x: textElement.x,
|
||||||
y: textElement.y,
|
y: textElement.y,
|
||||||
text: textElement.text,
|
text: textElement.text,
|
||||||
width: textElement.width,
|
width: textElement.width,
|
||||||
height: textElement.height,
|
height: textElement.height,
|
||||||
angle: container?.angle ?? textElement.angle,
|
angle: (container
|
||||||
|
? isArrowElement(container)
|
||||||
|
? 0
|
||||||
|
: container.angle
|
||||||
|
: textElement.angle) as Radians,
|
||||||
};
|
};
|
||||||
|
|
||||||
boundTextUpdates.text = textElement.text;
|
boundTextUpdates.text = textElement.text;
|
||||||
|
@ -335,7 +351,10 @@ export const getTextElementAngle = (
|
||||||
textElement: ExcalidrawTextElement,
|
textElement: ExcalidrawTextElement,
|
||||||
container: ExcalidrawTextContainer | null,
|
container: ExcalidrawTextContainer | null,
|
||||||
) => {
|
) => {
|
||||||
if (!container || isArrowElement(container)) {
|
if (isArrowElement(container)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (!container) {
|
||||||
return textElement.angle;
|
return textElement.angle;
|
||||||
}
|
}
|
||||||
return container.angle;
|
return container.angle;
|
||||||
|
|
|
@ -21,6 +21,7 @@ import {
|
||||||
|
|
||||||
import {
|
import {
|
||||||
hasBoundTextElement,
|
hasBoundTextElement,
|
||||||
|
isArrowElement,
|
||||||
isTextBindableContainer,
|
isTextBindableContainer,
|
||||||
isTextElement,
|
isTextElement,
|
||||||
isUsingAdaptiveRadius,
|
isUsingAdaptiveRadius,
|
||||||
|
@ -46,6 +47,8 @@ import { CaptureUpdateAction } from "../store";
|
||||||
|
|
||||||
import { register } from "./register";
|
import { register } from "./register";
|
||||||
|
|
||||||
|
import type { Radians } from "../../math/src";
|
||||||
|
|
||||||
import type { AppState } from "../types";
|
import type { AppState } from "../types";
|
||||||
|
|
||||||
export const actionUnbindText = register({
|
export const actionUnbindText = register({
|
||||||
|
@ -155,6 +158,7 @@ export const actionBindText = register({
|
||||||
verticalAlign: VERTICAL_ALIGN.MIDDLE,
|
verticalAlign: VERTICAL_ALIGN.MIDDLE,
|
||||||
textAlign: TEXT_ALIGN.CENTER,
|
textAlign: TEXT_ALIGN.CENTER,
|
||||||
autoResize: true,
|
autoResize: true,
|
||||||
|
angle: (isArrowElement(container) ? 0 : container?.angle ?? 0) as Radians,
|
||||||
});
|
});
|
||||||
mutateElement(container, {
|
mutateElement(container, {
|
||||||
boundElements: (container.boundElements || []).concat({
|
boundElements: (container.boundElements || []).concat({
|
||||||
|
|
|
@ -5352,15 +5352,11 @@ class App extends React.Component<AppProps, AppState> {
|
||||||
y: sceneY,
|
y: sceneY,
|
||||||
});
|
});
|
||||||
|
|
||||||
const element = existingTextElement
|
const element =
|
||||||
? existingTextElement
|
existingTextElement ||
|
||||||
: newTextElement({
|
newTextElement({
|
||||||
x: parentCenterPosition
|
x: parentCenterPosition ? parentCenterPosition.elementCenterX : sceneX,
|
||||||
? parentCenterPosition.elementCenterX
|
y: parentCenterPosition ? parentCenterPosition.elementCenterY : sceneY,
|
||||||
: sceneX,
|
|
||||||
y: parentCenterPosition
|
|
||||||
? parentCenterPosition.elementCenterY
|
|
||||||
: sceneY,
|
|
||||||
strokeColor: this.state.currentItemStrokeColor,
|
strokeColor: this.state.currentItemStrokeColor,
|
||||||
backgroundColor: this.state.currentItemBackgroundColor,
|
backgroundColor: this.state.currentItemBackgroundColor,
|
||||||
fillStyle: this.state.currentItemFillStyle,
|
fillStyle: this.state.currentItemFillStyle,
|
||||||
|
@ -5380,7 +5376,11 @@ class App extends React.Component<AppProps, AppState> {
|
||||||
containerId: shouldBindToContainer ? container?.id : undefined,
|
containerId: shouldBindToContainer ? container?.id : undefined,
|
||||||
groupIds: container?.groupIds ?? [],
|
groupIds: container?.groupIds ?? [],
|
||||||
lineHeight,
|
lineHeight,
|
||||||
angle: container?.angle ?? (0 as Radians),
|
angle: container
|
||||||
|
? isArrowElement(container)
|
||||||
|
? (0 as Radians)
|
||||||
|
: container.angle
|
||||||
|
: (0 as Radians),
|
||||||
frameId: topLayerFrame ? topLayerFrame.id : null,
|
frameId: topLayerFrame ? topLayerFrame.id : null,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -439,7 +439,7 @@ const repairContainerElement = (
|
||||||
// if defined, lest boundElements is stale
|
// if defined, lest boundElements is stale
|
||||||
!boundElement.containerId
|
!boundElement.containerId
|
||||||
) {
|
) {
|
||||||
(boundElement as Mutable<ExcalidrawTextElement>).containerId =
|
(boundElement as Mutable<typeof boundElement>).containerId =
|
||||||
container.id;
|
container.id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -464,6 +464,10 @@ const repairBoundElement = (
|
||||||
? elementsMap.get(boundElement.containerId)
|
? elementsMap.get(boundElement.containerId)
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
|
(boundElement as Mutable<typeof boundElement>).angle = (
|
||||||
|
isArrowElement(container) ? 0 : container?.angle ?? 0
|
||||||
|
) as Radians;
|
||||||
|
|
||||||
if (!container) {
|
if (!container) {
|
||||||
boundElement.containerId = null;
|
boundElement.containerId = null;
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -31,6 +31,7 @@ import {
|
||||||
mockBoundingClientRect,
|
mockBoundingClientRect,
|
||||||
restoreOriginalGetBoundingClientRect,
|
restoreOriginalGetBoundingClientRect,
|
||||||
} from "../tests/test-utils";
|
} from "../tests/test-utils";
|
||||||
|
import { actionBindText } from "../actions";
|
||||||
|
|
||||||
unmountComponent();
|
unmountComponent();
|
||||||
|
|
||||||
|
@ -1568,5 +1569,101 @@ describe("textWysiwyg", () => {
|
||||||
expect(text.containerId).toBe(null);
|
expect(text.containerId).toBe(null);
|
||||||
expect(text.text).toBe("Excalidraw");
|
expect(text.text).toBe("Excalidraw");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should reset the text element angle to the container's when binding to rotated non-arrow container", async () => {
|
||||||
|
const text = API.createElement({
|
||||||
|
type: "text",
|
||||||
|
text: "Hello World!",
|
||||||
|
angle: 45,
|
||||||
|
});
|
||||||
|
const rectangle = API.createElement({
|
||||||
|
type: "rectangle",
|
||||||
|
width: 90,
|
||||||
|
height: 75,
|
||||||
|
angle: 30,
|
||||||
|
});
|
||||||
|
|
||||||
|
API.setElements([rectangle, text]);
|
||||||
|
|
||||||
|
API.setSelectedElements([rectangle, text]);
|
||||||
|
|
||||||
|
h.app.actionManager.executeAction(actionBindText);
|
||||||
|
|
||||||
|
expect(text.angle).toBe(30);
|
||||||
|
expect(rectangle.angle).toBe(30);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should reset the text element angle to 0 when binding to rotated arrow container", async () => {
|
||||||
|
const text = API.createElement({
|
||||||
|
type: "text",
|
||||||
|
text: "Hello World!",
|
||||||
|
angle: 45,
|
||||||
|
});
|
||||||
|
const arrow = API.createElement({
|
||||||
|
type: "arrow",
|
||||||
|
width: 90,
|
||||||
|
height: 75,
|
||||||
|
angle: 30,
|
||||||
|
});
|
||||||
|
|
||||||
|
API.setElements([arrow, text]);
|
||||||
|
|
||||||
|
API.setSelectedElements([arrow, text]);
|
||||||
|
|
||||||
|
h.app.actionManager.executeAction(actionBindText);
|
||||||
|
|
||||||
|
expect(text.angle).toBe(0);
|
||||||
|
expect(arrow.angle).toBe(30);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should keep the text label at 0 degrees when used as an arrow label", async () => {
|
||||||
|
const arrow = API.createElement({
|
||||||
|
type: "arrow",
|
||||||
|
width: 90,
|
||||||
|
height: 75,
|
||||||
|
angle: 30,
|
||||||
|
});
|
||||||
|
|
||||||
|
API.setElements([arrow]);
|
||||||
|
API.setSelectedElements([arrow]);
|
||||||
|
|
||||||
|
mouse.doubleClickAt(
|
||||||
|
arrow.x + arrow.width / 2,
|
||||||
|
arrow.y + arrow.height / 2,
|
||||||
|
);
|
||||||
|
|
||||||
|
const editor = await getTextEditor(textEditorSelector, true);
|
||||||
|
|
||||||
|
updateTextEditor(editor, "Hello World!");
|
||||||
|
|
||||||
|
Keyboard.exitTextEditor(editor);
|
||||||
|
|
||||||
|
expect(h.elements[1].angle).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should keep the text label at the same degrees when used as a non-arrow label", async () => {
|
||||||
|
const rectangle = API.createElement({
|
||||||
|
type: "rectangle",
|
||||||
|
width: 90,
|
||||||
|
height: 75,
|
||||||
|
angle: 30,
|
||||||
|
});
|
||||||
|
|
||||||
|
API.setElements([rectangle]);
|
||||||
|
API.setSelectedElements([rectangle]);
|
||||||
|
|
||||||
|
mouse.doubleClickAt(
|
||||||
|
rectangle.x + rectangle.width / 2,
|
||||||
|
rectangle.y + rectangle.height / 2,
|
||||||
|
);
|
||||||
|
|
||||||
|
const editor = await getTextEditor(textEditorSelector, true);
|
||||||
|
|
||||||
|
updateTextEditor(editor, "Hello World!");
|
||||||
|
|
||||||
|
Keyboard.exitTextEditor(editor);
|
||||||
|
|
||||||
|
expect(h.elements[1].angle).toBe(30);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue