Skip to content

Commit

Permalink
Merge pull request #229 from excalidraw/master
Browse files Browse the repository at this point in the history
merge upstream
  • Loading branch information
zsviczian authored Jun 27, 2024
2 parents 39e9307 + 744b3e5 commit 2f60398
Show file tree
Hide file tree
Showing 17 changed files with 911 additions and 719 deletions.
2 changes: 2 additions & 0 deletions packages/excalidraw/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ Please add the latest change on the top under the correct section.

### Features

- `props.initialData` can now be a function that returns `ExcalidrawInitialDataState` or `Promise<ExcalidrawInitialDataState>`. [#8107](https://github.com/excalidraw/excalidraw/pull/8135)

- Added support for multiplayer undo/redo, by calculating invertible increments and storing them inside the local-only undo/redo stacks. [#7348](https://github.com/excalidraw/excalidraw/pull/7348)

- `MainMenu.DefaultItems.ToggleTheme` now supports `onSelect(theme: string)` callback, and optionally `allowSystemTheme: boolean` alongside `theme: string` to indicate you want to allow users to set to system theme (you need to handle this yourself). [#7853](https://github.com/excalidraw/excalidraw/pull/7853)
Expand Down
6 changes: 5 additions & 1 deletion packages/excalidraw/components/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2363,7 +2363,11 @@ class App extends React.Component<AppProps, AppState> {
}
let initialData = null;
try {
initialData = (await this.props.initialData) || null;
if (typeof this.props.initialData === "function") {
initialData = (await this.props.initialData()) || null;
} else {
initialData = (await this.props.initialData) || null;
}
if (initialData?.libraryItems) {
this.library
.updateLibrary({
Expand Down
93 changes: 53 additions & 40 deletions packages/excalidraw/components/Stats/Angle.tsx
Original file line number Diff line number Diff line change
@@ -1,67 +1,77 @@
import { mutateElement } from "../../element/mutateElement";
import { getBoundTextElement } from "../../element/textElement";
import { isArrowElement } from "../../element/typeChecks";
import type { ElementsMap, ExcalidrawElement } from "../../element/types";
import type { ExcalidrawElement } from "../../element/types";
import { degreeToRadian, radianToDegree } from "../../math";
import { angleIcon } from "../icons";
import DragInput from "./DragInput";
import type { DragInputCallbackType } from "./DragInput";
import { getStepSizedValue, isPropertyEditable } from "./utils";
import type Scene from "../../scene/Scene";
import type { AppState } from "../../types";

interface AngleProps {
element: ExcalidrawElement;
elementsMap: ElementsMap;
scene: Scene;
appState: AppState;
property: "angle";
}

const STEP_SIZE = 15;

const Angle = ({ element, elementsMap }: AngleProps) => {
const handleDegreeChange: DragInputCallbackType = ({
accumulatedChange,
originalElements,
shouldChangeByStepSize,
nextValue,
}) => {
const origElement = originalElements[0];
if (origElement) {
if (nextValue !== undefined) {
const nextAngle = degreeToRadian(nextValue);
mutateElement(element, {
angle: nextAngle,
});

const boundTextElement = getBoundTextElement(element, elementsMap);
if (boundTextElement && !isArrowElement(element)) {
mutateElement(boundTextElement, { angle: nextAngle });
}
const handleDegreeChange: DragInputCallbackType<AngleProps["property"]> = ({
accumulatedChange,
originalElements,
shouldChangeByStepSize,
nextValue,
scene,
}) => {
const elementsMap = scene.getNonDeletedElementsMap();
const origElement = originalElements[0];
if (origElement) {
const latestElement = elementsMap.get(origElement.id);
if (!latestElement) {
return;
}
if (nextValue !== undefined) {
const nextAngle = degreeToRadian(nextValue);
mutateElement(latestElement, {
angle: nextAngle,
});

return;
const boundTextElement = getBoundTextElement(latestElement, elementsMap);
if (boundTextElement && !isArrowElement(latestElement)) {
mutateElement(boundTextElement, { angle: nextAngle });
}

const originalAngleInDegrees =
Math.round(radianToDegree(origElement.angle) * 100) / 100;
const changeInDegrees = Math.round(accumulatedChange);
let nextAngleInDegrees = (originalAngleInDegrees + changeInDegrees) % 360;
if (shouldChangeByStepSize) {
nextAngleInDegrees = getStepSizedValue(nextAngleInDegrees, STEP_SIZE);
}
return;
}

nextAngleInDegrees =
nextAngleInDegrees < 0 ? nextAngleInDegrees + 360 : nextAngleInDegrees;
const originalAngleInDegrees =
Math.round(radianToDegree(origElement.angle) * 100) / 100;
const changeInDegrees = Math.round(accumulatedChange);
let nextAngleInDegrees = (originalAngleInDegrees + changeInDegrees) % 360;
if (shouldChangeByStepSize) {
nextAngleInDegrees = getStepSizedValue(nextAngleInDegrees, STEP_SIZE);
}

const nextAngle = degreeToRadian(nextAngleInDegrees);
nextAngleInDegrees =
nextAngleInDegrees < 0 ? nextAngleInDegrees + 360 : nextAngleInDegrees;

mutateElement(element, {
angle: nextAngle,
});
const nextAngle = degreeToRadian(nextAngleInDegrees);

const boundTextElement = getBoundTextElement(element, elementsMap);
if (boundTextElement && !isArrowElement(element)) {
mutateElement(boundTextElement, { angle: nextAngle });
}
mutateElement(latestElement, {
angle: nextAngle,
});

const boundTextElement = getBoundTextElement(latestElement, elementsMap);
if (boundTextElement && !isArrowElement(latestElement)) {
mutateElement(boundTextElement, { angle: nextAngle });
}
};
}
};

const Angle = ({ element, scene, appState, property }: AngleProps) => {
return (
<DragInput
label="A"
Expand All @@ -70,6 +80,9 @@ const Angle = ({ element, elementsMap }: AngleProps) => {
elements={[element]}
dragInputCallback={handleDegreeChange}
editable={isPropertyEditable(element, "angle")}
scene={scene}
appState={appState}
property={property}
/>
);
};
Expand Down
172 changes: 90 additions & 82 deletions packages/excalidraw/components/Stats/Dimension.tsx
Original file line number Diff line number Diff line change
@@ -1,113 +1,118 @@
import type { ElementsMap, ExcalidrawElement } from "../../element/types";
import type { ExcalidrawElement } from "../../element/types";
import DragInput from "./DragInput";
import type { DragInputCallbackType } from "./DragInput";
import { getStepSizedValue, isPropertyEditable, resizeElement } from "./utils";
import { MIN_WIDTH_OR_HEIGHT } from "../../constants";
import type Scene from "../../scene/Scene";
import type { AppState } from "../../types";

interface DimensionDragInputProps {
property: "width" | "height";
element: ExcalidrawElement;
elementsMap: ElementsMap;
scene: Scene;
appState: AppState;
}

const STEP_SIZE = 10;
const _shouldKeepAspectRatio = (element: ExcalidrawElement) => {
return element.type === "image";
};

const DimensionDragInput = ({
const handleDimensionChange: DragInputCallbackType<
DimensionDragInputProps["property"]
> = ({
accumulatedChange,
originalElements,
originalElementsMap,
shouldKeepAspectRatio,
shouldChangeByStepSize,
nextValue,
property,
element,
elementsMap,
}: DimensionDragInputProps) => {
const handleDimensionChange: DragInputCallbackType = ({
accumulatedChange,
originalElements,
originalElementsMap,
shouldKeepAspectRatio,
shouldChangeByStepSize,
nextValue,
}) => {
const origElement = originalElements[0];
if (origElement) {
const keepAspectRatio =
shouldKeepAspectRatio || _shouldKeepAspectRatio(element);
const aspectRatio = origElement.width / origElement.height;
scene,
}) => {
const elementsMap = scene.getNonDeletedElementsMap();
const origElement = originalElements[0];
if (origElement) {
const keepAspectRatio =
shouldKeepAspectRatio || _shouldKeepAspectRatio(origElement);
const aspectRatio = origElement.width / origElement.height;

if (nextValue !== undefined) {
const nextWidth = Math.max(
property === "width"
? nextValue
: keepAspectRatio
? nextValue * aspectRatio
: origElement.width,
MIN_WIDTH_OR_HEIGHT,
);
const nextHeight = Math.max(
property === "height"
? nextValue
: keepAspectRatio
? nextValue / aspectRatio
: origElement.height,
MIN_WIDTH_OR_HEIGHT,
);
if (nextValue !== undefined) {
const nextWidth = Math.max(
property === "width"
? nextValue
: keepAspectRatio
? nextValue * aspectRatio
: origElement.width,
MIN_WIDTH_OR_HEIGHT,
);
const nextHeight = Math.max(
property === "height"
? nextValue
: keepAspectRatio
? nextValue / aspectRatio
: origElement.height,
MIN_WIDTH_OR_HEIGHT,
);

resizeElement(
nextWidth,
nextHeight,
keepAspectRatio,
element,
origElement,
elementsMap,
originalElementsMap,
);
resizeElement(
nextWidth,
nextHeight,
keepAspectRatio,
origElement,
elementsMap,
);

return;
}
const changeInWidth = property === "width" ? accumulatedChange : 0;
const changeInHeight = property === "height" ? accumulatedChange : 0;
return;
}
const changeInWidth = property === "width" ? accumulatedChange : 0;
const changeInHeight = property === "height" ? accumulatedChange : 0;

let nextWidth = Math.max(0, origElement.width + changeInWidth);
if (property === "width") {
if (shouldChangeByStepSize) {
nextWidth = getStepSizedValue(nextWidth, STEP_SIZE);
} else {
nextWidth = Math.round(nextWidth);
}
let nextWidth = Math.max(0, origElement.width + changeInWidth);
if (property === "width") {
if (shouldChangeByStepSize) {
nextWidth = getStepSizedValue(nextWidth, STEP_SIZE);
} else {
nextWidth = Math.round(nextWidth);
}
}

let nextHeight = Math.max(0, origElement.height + changeInHeight);
if (property === "height") {
if (shouldChangeByStepSize) {
nextHeight = getStepSizedValue(nextHeight, STEP_SIZE);
} else {
nextHeight = Math.round(nextHeight);
}
let nextHeight = Math.max(0, origElement.height + changeInHeight);
if (property === "height") {
if (shouldChangeByStepSize) {
nextHeight = getStepSizedValue(nextHeight, STEP_SIZE);
} else {
nextHeight = Math.round(nextHeight);
}
}

if (keepAspectRatio) {
if (property === "width") {
nextHeight = Math.round((nextWidth / aspectRatio) * 100) / 100;
} else {
nextWidth = Math.round(nextHeight * aspectRatio * 100) / 100;
}
if (keepAspectRatio) {
if (property === "width") {
nextHeight = Math.round((nextWidth / aspectRatio) * 100) / 100;
} else {
nextWidth = Math.round(nextHeight * aspectRatio * 100) / 100;
}
}

nextHeight = Math.max(MIN_WIDTH_OR_HEIGHT, nextHeight);
nextWidth = Math.max(MIN_WIDTH_OR_HEIGHT, nextWidth);
nextHeight = Math.max(MIN_WIDTH_OR_HEIGHT, nextHeight);
nextWidth = Math.max(MIN_WIDTH_OR_HEIGHT, nextWidth);

resizeElement(
nextWidth,
nextHeight,
keepAspectRatio,
element,
origElement,
elementsMap,
originalElementsMap,
);
}
};
resizeElement(
nextWidth,
nextHeight,
keepAspectRatio,
origElement,
elementsMap,
);
}
};

const DimensionDragInput = ({
property,
element,
scene,
appState,
}: DimensionDragInputProps) => {
const value =
Math.round((property === "width" ? element.width : element.height) * 100) /
100;
Expand All @@ -119,6 +124,9 @@ const DimensionDragInput = ({
dragInputCallback={handleDimensionChange}
value={value}
editable={isPropertyEditable(element, property)}
scene={scene}
appState={appState}
property={property}
/>
);
};
Expand Down
Loading

0 comments on commit 2f60398

Please sign in to comment.