Compare commits

..
6 changed files with 93 additions and 46 deletions
+1 -1
View File
@@ -18,7 +18,7 @@
"@docusaurus/core": "2.2.0", "@docusaurus/core": "2.2.0",
"@docusaurus/preset-classic": "2.2.0", "@docusaurus/preset-classic": "2.2.0",
"@docusaurus/theme-live-codeblock": "2.2.0", "@docusaurus/theme-live-codeblock": "2.2.0",
"@excalidraw/excalidraw": "0.17.0", "@excalidraw/excalidraw": "0.17.0-7284-25ea35d",
"@mdx-js/react": "^1.6.22", "@mdx-js/react": "^1.6.22",
"clsx": "^1.2.1", "clsx": "^1.2.1",
"docusaurus-plugin-sass": "0.2.3", "docusaurus-plugin-sass": "0.2.3",
+4 -4
View File
@@ -1718,10 +1718,10 @@
url-loader "^4.1.1" url-loader "^4.1.1"
webpack "^5.73.0" webpack "^5.73.0"
"@excalidraw/excalidraw@0.17.0": "@excalidraw/excalidraw@0.17.0-7284-25ea35d":
version "0.17.0" version "0.17.0-7284-25ea35d"
resolved "https://registry.yarnpkg.com/@excalidraw/excalidraw/-/excalidraw-0.17.0.tgz#3c64aa8e36406ac171b008cfecbdce5bb0755725" resolved "https://registry.yarnpkg.com/@excalidraw/excalidraw/-/excalidraw-0.17.0-7284-25ea35d.tgz#dd42ccc757e81d064f55bb0cac96c344fb557358"
integrity sha512-NzP22v5xMqxYW27ZtTHhiGFe7kE8NeBk45aoeM/mDSkXiOXPDH+PcvwzHRN/Ei+Vj/0sTPHxejn8bZyRWKGjXg== integrity sha512-VVe0bdnmsZeIcbfoK2DgJefWmCmyQDFRvssCfxP3l2g/W8/6uKiKG+WNylu1D9dGveMfg1Io7XG2/PGRyD8OrQ==
"@hapi/hoek@^9.0.0": "@hapi/hoek@^9.0.0":
version "9.3.0" version "9.3.0"
+1 -1
View File
@@ -96,7 +96,7 @@
"vitest-canvas-mock": "0.3.2" "vitest-canvas-mock": "0.3.2"
}, },
"engines": { "engines": {
"node": "18.0.0 - 20.x.x" "node": ">=18.0.0"
}, },
"homepage": ".", "homepage": ".",
"name": "excalidraw", "name": "excalidraw",
+1 -15
View File
@@ -265,21 +265,7 @@ export const zoomToFit = ({
30.0, 30.0,
) as NormalizedZoomValue; ) as NormalizedZoomValue;
let appStateWidth = appState.width; scrollX = (appState.width / 2) * (1 / newZoomValue) - centerX;
if (appState.openSidebar) {
const sidebarDOMElem = document.querySelector(
".sidebar",
) as HTMLElement | null;
const sidebarWidth = sidebarDOMElem?.offsetWidth ?? 0;
const isRTL = document.documentElement.getAttribute("dir") === "rtl";
appStateWidth = !isRTL
? appState.width - sidebarWidth
: appState.width + sidebarWidth;
}
scrollX = (appStateWidth / 2) * (1 / newZoomValue) - centerX;
scrollY = (appState.height / 2) * (1 / newZoomValue) - centerY; scrollY = (appState.height / 2) * (1 / newZoomValue) - centerY;
} else { } else {
newZoomValue = zoomValueToFitBoundsOnViewport(commonBounds, { newZoomValue = zoomValueToFitBoundsOnViewport(commonBounds, {
+66 -20
View File
@@ -8238,8 +8238,27 @@ class App extends React.Component<AppProps, AppState> {
event[KEYS.CTRL_OR_CMD] ? null : this.state.gridSize, event[KEYS.CTRL_OR_CMD] ? null : this.state.gridSize,
); );
const resizingSingleFrameOnly = const frameElementsOffsetsMap = new Map<
selectedElements.length === 1 && selectedFrames.length === 1; string,
{
x: number;
y: number;
}
>();
selectedFrames.forEach((frame) => {
const elementsInFrame = getFrameChildren(
this.scene.getNonDeletedElements(),
frame.id,
);
elementsInFrame.forEach((element) => {
frameElementsOffsetsMap.set(frame.id + element.id, {
x: element.x - frame.x,
y: element.y - frame.y,
});
});
});
// check needed for avoiding flickering when a key gets pressed // check needed for avoiding flickering when a key gets pressed
// during dragging // during dragging
@@ -8280,12 +8299,7 @@ class App extends React.Component<AppProps, AppState> {
transformElements( transformElements(
pointerDownState, pointerDownState,
transformHandleType, transformHandleType,
resizingSingleFrameOnly selectedElements,
? selectedElements
: this.scene.getSelectedElements({
selectedElementIds: this.state.selectedElementIds,
includeElementsInFrames: true,
}),
pointerDownState.resize.arrowDirection, pointerDownState.resize.arrowDirection,
shouldRotateWithDiscreteAngle(event), shouldRotateWithDiscreteAngle(event),
shouldResizeFromCenter(event), shouldResizeFromCenter(event),
@@ -8301,19 +8315,51 @@ class App extends React.Component<AppProps, AppState> {
) { ) {
this.maybeSuggestBindingForAll(selectedElements); this.maybeSuggestBindingForAll(selectedElements);
// highlight frame children ONLY when resizing a single frame const elementsToHighlight = new Set<ExcalidrawElement>();
if (resizingSingleFrameOnly) { selectedFrames.forEach((frame) => {
const selectedFrame = selectedFrames[0]; const elementsInFrame = getFrameChildren(
if (selectedFrame) { this.scene.getNonDeletedElements(),
this.setState({ frame.id,
elementsToHighlight: getElementsInResizingFrame( );
this.scene.getNonDeletedElements(),
selectedFrame, // keep elements' positions relative to their frames on frames resizing
this.state, if (transformHandleType) {
), if (transformHandleType.includes("w")) {
}); elementsInFrame.forEach((element) => {
mutateElement(element, {
x:
frame.x +
(frameElementsOffsetsMap.get(frame.id + element.id)?.x || 0),
y:
frame.y +
(frameElementsOffsetsMap.get(frame.id + element.id)?.y || 0),
});
});
}
if (transformHandleType.includes("n")) {
elementsInFrame.forEach((element) => {
mutateElement(element, {
x:
frame.x +
(frameElementsOffsetsMap.get(frame.id + element.id)?.x || 0),
y:
frame.y +
(frameElementsOffsetsMap.get(frame.id + element.id)?.y || 0),
});
});
}
} }
}
getElementsInResizingFrame(
this.scene.getNonDeletedElements(),
frame,
this.state,
).forEach((element) => elementsToHighlight.add(element));
});
this.setState({
elementsToHighlight: [...elementsToHighlight],
});
return true; return true;
} }
+20 -5
View File
@@ -13,6 +13,7 @@ import { Point } from "../types";
import { generateRoughOptions } from "../scene/Shape"; import { generateRoughOptions } from "../scene/Shape";
import { import {
isArrowElement, isArrowElement,
isBoundToContainer,
isFreeDrawElement, isFreeDrawElement,
isLinearElement, isLinearElement,
isTextElement, isTextElement,
@@ -22,6 +23,7 @@ import { getBoundTextElement, getContainerElement } from "./textElement";
import { LinearElementEditor } from "./linearElementEditor"; import { LinearElementEditor } from "./linearElementEditor";
import { Mutable } from "../utility-types"; import { Mutable } from "../utility-types";
import { ShapeCache } from "../scene/ShapeCache"; import { ShapeCache } from "../scene/ShapeCache";
import Scene from "../scene/Scene";
export type RectangleBox = { export type RectangleBox = {
x: number; x: number;
@@ -53,16 +55,29 @@ export class ElementBounds {
static getBounds(element: ExcalidrawElement) { static getBounds(element: ExcalidrawElement) {
const cachedBounds = ElementBounds.boundsCache.get(element); const cachedBounds = ElementBounds.boundsCache.get(element);
if (cachedBounds?.version && cachedBounds.version === element.version) { if (
cachedBounds?.version &&
cachedBounds.version === element.version &&
// we don't invalidate cache when we update containers and not labels,
// which is causing problems down the line. Fix TBA.
!isBoundToContainer(element)
) {
return cachedBounds.bounds; return cachedBounds.bounds;
} }
const bounds = ElementBounds.calculateBounds(element); const bounds = ElementBounds.calculateBounds(element);
ElementBounds.boundsCache.set(element, { // hack to ensure that downstream checks could retrieve element Scene
version: element.version, // so as to have correctly calculated bounds
bounds, // FIXME remove when we get rid of all the id:Scene / element:Scene mapping
}); const shouldCache = Scene.getScene(element);
if (shouldCache) {
ElementBounds.boundsCache.set(element, {
version: element.version,
bounds,
});
}
return bounds; return bounds;
} }