|
@@ -7,6 +7,9 @@ import {
|
|
} from "@excalidraw/element";
|
|
} from "@excalidraw/element";
|
|
import { resizeSingleElement } from "@excalidraw/element";
|
|
import { resizeSingleElement } from "@excalidraw/element";
|
|
import { isImageElement } from "@excalidraw/element";
|
|
import { isImageElement } from "@excalidraw/element";
|
|
|
|
+import { isFrameLikeElement } from "@excalidraw/element";
|
|
|
|
+import { getElementsInResizingFrame } from "@excalidraw/element";
|
|
|
|
+import { replaceAllElementsInFrame } from "@excalidraw/element";
|
|
|
|
|
|
import type { ExcalidrawElement } from "@excalidraw/element/types";
|
|
import type { ExcalidrawElement } from "@excalidraw/element/types";
|
|
|
|
|
|
@@ -15,7 +18,10 @@ import type { Scene } from "@excalidraw/element";
|
|
import DragInput from "./DragInput";
|
|
import DragInput from "./DragInput";
|
|
import { getStepSizedValue, isPropertyEditable } from "./utils";
|
|
import { getStepSizedValue, isPropertyEditable } from "./utils";
|
|
|
|
|
|
-import type { DragInputCallbackType } from "./DragInput";
|
|
|
|
|
|
+import type {
|
|
|
|
+ DragFinishedCallbackType,
|
|
|
|
+ DragInputCallbackType,
|
|
|
|
+} from "./DragInput";
|
|
import type { AppState } from "../../types";
|
|
import type { AppState } from "../../types";
|
|
|
|
|
|
interface DimensionDragInputProps {
|
|
interface DimensionDragInputProps {
|
|
@@ -43,6 +49,8 @@ const handleDimensionChange: DragInputCallbackType<
|
|
originalAppState,
|
|
originalAppState,
|
|
instantChange,
|
|
instantChange,
|
|
scene,
|
|
scene,
|
|
|
|
+ app,
|
|
|
|
+ setAppState,
|
|
}) => {
|
|
}) => {
|
|
const elementsMap = scene.getNonDeletedElementsMap();
|
|
const elementsMap = scene.getNonDeletedElementsMap();
|
|
const origElement = originalElements[0];
|
|
const origElement = originalElements[0];
|
|
@@ -153,6 +161,7 @@ const handleDimensionChange: DragInputCallbackType<
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ // User types in a value to stats then presses Enter
|
|
if (nextValue !== undefined) {
|
|
if (nextValue !== undefined) {
|
|
const nextWidth = Math.max(
|
|
const nextWidth = Math.max(
|
|
property === "width"
|
|
property === "width"
|
|
@@ -184,52 +193,123 @@ const handleDimensionChange: DragInputCallbackType<
|
|
},
|
|
},
|
|
);
|
|
);
|
|
|
|
|
|
|
|
+ // Handle frame membership update for resized frames
|
|
|
|
+ if (isFrameLikeElement(latestElement)) {
|
|
|
|
+ const nextElementsInFrame = getElementsInResizingFrame(
|
|
|
|
+ scene.getElementsIncludingDeleted(),
|
|
|
|
+ latestElement,
|
|
|
|
+ originalAppState,
|
|
|
|
+ scene.getNonDeletedElementsMap(),
|
|
|
|
+ );
|
|
|
|
+
|
|
|
|
+ const updatedElements = replaceAllElementsInFrame(
|
|
|
|
+ scene.getElementsIncludingDeleted(),
|
|
|
|
+ nextElementsInFrame,
|
|
|
|
+ latestElement,
|
|
|
|
+ app,
|
|
|
|
+ );
|
|
|
|
+
|
|
|
|
+ scene.replaceAllElements(updatedElements);
|
|
|
|
+ }
|
|
|
|
+
|
|
return;
|
|
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);
|
|
|
|
|
|
+ // Stats slider is dragged
|
|
|
|
+ {
|
|
|
|
+ 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 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);
|
|
|
|
+
|
|
|
|
+ resizeSingleElement(
|
|
|
|
+ nextWidth,
|
|
|
|
+ nextHeight,
|
|
|
|
+ latestElement,
|
|
|
|
+ origElement,
|
|
|
|
+ originalElementsMap,
|
|
|
|
+ scene,
|
|
|
|
+ property === "width" ? "e" : "s",
|
|
|
|
+ {
|
|
|
|
+ shouldMaintainAspectRatio: keepAspectRatio,
|
|
|
|
+ },
|
|
|
|
+ );
|
|
|
|
+
|
|
|
|
+ // Handle highlighting frame element candidates
|
|
|
|
+ if (isFrameLikeElement(latestElement)) {
|
|
|
|
+ const nextElementsInFrame = getElementsInResizingFrame(
|
|
|
|
+ scene.getElementsIncludingDeleted(),
|
|
|
|
+ latestElement,
|
|
|
|
+ originalAppState,
|
|
|
|
+ scene.getNonDeletedElementsMap(),
|
|
|
|
+ );
|
|
|
|
+
|
|
|
|
+ setAppState({
|
|
|
|
+ elementsToHighlight: nextElementsInFrame,
|
|
|
|
+ });
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
+ }
|
|
|
|
+};
|
|
|
|
|
|
- nextHeight = Math.max(MIN_WIDTH_OR_HEIGHT, nextHeight);
|
|
|
|
- nextWidth = Math.max(MIN_WIDTH_OR_HEIGHT, nextWidth);
|
|
|
|
|
|
+const handleDragFinished: DragFinishedCallbackType = ({
|
|
|
|
+ setAppState,
|
|
|
|
+ app,
|
|
|
|
+ originalElements,
|
|
|
|
+ originalAppState,
|
|
|
|
+}) => {
|
|
|
|
+ const elementsMap = app.scene.getNonDeletedElementsMap();
|
|
|
|
+ const origElement = originalElements?.[0];
|
|
|
|
+ const latestElement = origElement && elementsMap.get(origElement.id);
|
|
|
|
+
|
|
|
|
+ // Handle frame membership update for resized frames
|
|
|
|
+ if (latestElement && isFrameLikeElement(latestElement)) {
|
|
|
|
+ const nextElementsInFrame = getElementsInResizingFrame(
|
|
|
|
+ app.scene.getElementsIncludingDeleted(),
|
|
|
|
+ latestElement,
|
|
|
|
+ originalAppState,
|
|
|
|
+ app.scene.getNonDeletedElementsMap(),
|
|
|
|
+ );
|
|
|
|
|
|
- resizeSingleElement(
|
|
|
|
- nextWidth,
|
|
|
|
- nextHeight,
|
|
|
|
|
|
+ const updatedElements = replaceAllElementsInFrame(
|
|
|
|
+ app.scene.getElementsIncludingDeleted(),
|
|
|
|
+ nextElementsInFrame,
|
|
latestElement,
|
|
latestElement,
|
|
- origElement,
|
|
|
|
- originalElementsMap,
|
|
|
|
- scene,
|
|
|
|
- property === "width" ? "e" : "s",
|
|
|
|
- {
|
|
|
|
- shouldMaintainAspectRatio: keepAspectRatio,
|
|
|
|
- },
|
|
|
|
|
|
+ app,
|
|
);
|
|
);
|
|
|
|
+
|
|
|
|
+ app.scene.replaceAllElements(updatedElements);
|
|
|
|
+
|
|
|
|
+ setAppState({
|
|
|
|
+ elementsToHighlight: null,
|
|
|
|
+ });
|
|
}
|
|
}
|
|
};
|
|
};
|
|
|
|
|
|
@@ -269,6 +349,7 @@ const DimensionDragInput = ({
|
|
scene={scene}
|
|
scene={scene}
|
|
appState={appState}
|
|
appState={appState}
|
|
property={property}
|
|
property={property}
|
|
|
|
+ dragFinishedCallback={handleDragFinished}
|
|
/>
|
|
/>
|
|
);
|
|
);
|
|
};
|
|
};
|