Parcourir la source

Merge branch 'master' into cleanipp

Lipis il y a 4 ans
Parent
commit
e4a0254c47

+ 0 - 3
src/components/App.tsx

@@ -3587,9 +3587,6 @@ class App extends React.Component<ExcalidrawProps, AppState> {
       transformElements(
         pointerDownState,
         transformHandleType,
-        (newTransformHandle) => {
-          pointerDownState.resize.handleType = newTransformHandle;
-        },
         selectedElements,
         pointerDownState.resize.arrowDirection,
         getRotateWithDiscreteAngleKey(event),

+ 1 - 0
src/constants.ts

@@ -90,3 +90,4 @@ export const TAP_TWICE_TIMEOUT = 300;
 export const TITLE_TIMEOUT = 10000;
 export const TOAST_TIMEOUT = 5000;
 export const TOUCH_CTX_MENU_TIMEOUT = 500;
+export const VERSION_TIMEOUT = 15000;

+ 0 - 1
src/element/index.ts

@@ -34,7 +34,6 @@ export {
 export {
   resizeTest,
   getCursorForResizingElement,
-  normalizeTransformHandleType,
   getElementWithTransformHandleType,
   getTransformHandleTypeFromCoords,
 } from "./resizeTest";

+ 111 - 206
src/element/resizeElements.ts

@@ -4,7 +4,6 @@ import { rescalePoints } from "../points";
 import {
   rotate,
   adjustXYWithRotation,
-  getFlipAdjustment,
   centerPoint,
   rotatePoint,
 } from "../math";
@@ -13,21 +12,16 @@ import {
   ExcalidrawTextElement,
   NonDeletedExcalidrawElement,
   NonDeleted,
-  ExcalidrawGenericElement,
-  ExcalidrawElement,
 } from "./types";
 import {
   getElementAbsoluteCoords,
   getCommonBounds,
   getResizedElementAbsoluteCoords,
 } from "./bounds";
-import { isGenericElement, isLinearElement, isTextElement } from "./typeChecks";
+import { isLinearElement, isTextElement } from "./typeChecks";
 import { mutateElement } from "./mutateElement";
 import { getPerfectElementSize } from "./sizeHelpers";
-import {
-  getCursorForResizingElement,
-  normalizeTransformHandleType,
-} from "./resizeTest";
+import { getCursorForResizingElement } from "./resizeTest";
 import { measureText, getFontString } from "../utils";
 import { updateBoundElements } from "./binding";
 import {
@@ -49,7 +43,6 @@ const normalizeAngle = (angle: number): number => {
 export const transformElements = (
   pointerDownState: PointerDownState,
   transformHandleType: MaybeTransformHandleType,
-  setTransformHandle: (nextTransformHandle: MaybeTransformHandleType) => void,
   selectedElements: readonly NonDeletedExcalidrawElement[],
   resizeArrowDirection: "origin" | "end",
   isRotateWithDiscreteAngle: boolean,
@@ -101,36 +94,15 @@ export const transformElements = (
       );
       updateBoundElements(element);
     } else if (transformHandleType) {
-      if (isGenericElement(element)) {
-        resizeSingleGenericElement(
-          pointerDownState.originalElements.get(element.id) as typeof element,
-          shouldKeepSidesRatio,
-          element,
-          transformHandleType,
-          isResizeCenterPoint,
-          pointerX,
-          pointerY,
-        );
-      } else {
-        const keepSquareAspectRatio = shouldKeepSidesRatio;
-        resizeSingleNonGenericElement(
-          element,
-          transformHandleType,
-          isResizeCenterPoint,
-          keepSquareAspectRatio,
-          pointerX,
-          pointerY,
-        );
-        setTransformHandle(
-          normalizeTransformHandleType(element, transformHandleType),
-        );
-        if (element.width < 0) {
-          mutateElement(element, { width: -element.width });
-        }
-        if (element.height < 0) {
-          mutateElement(element, { height: -element.height });
-        }
-      }
+      resizeSingleElement(
+        pointerDownState.originalElements.get(element.id) as typeof element,
+        shouldKeepSidesRatio,
+        element,
+        transformHandleType,
+        isResizeCenterPoint,
+        pointerX,
+        pointerY,
+      );
     }
 
     // update cursor
@@ -414,8 +386,8 @@ const resizeSingleTextElement = (
   }
 };
 
-const resizeSingleGenericElement = (
-  stateAtResizeStart: NonDeleted<ExcalidrawGenericElement>,
+const resizeSingleElement = (
+  stateAtResizeStart: NonDeletedExcalidrawElement,
   shouldKeepSidesRatio: boolean,
   element: NonDeletedExcalidrawElement,
   transformHandleDirection: TransformHandleDirection,
@@ -423,251 +395,184 @@ const resizeSingleGenericElement = (
   pointerX: number,
   pointerY: number,
 ) => {
-  const [x1, y1, x2, y2] = getElementAbsoluteCoords(stateAtResizeStart);
+  // Gets bounds corners
+  const [x1, y1, x2, y2] = getResizedElementAbsoluteCoords(
+    stateAtResizeStart,
+    stateAtResizeStart.width,
+    stateAtResizeStart.height,
+  );
   const startTopLeft: Point = [x1, y1];
   const startBottomRight: Point = [x2, y2];
   const startCenter: Point = centerPoint(startTopLeft, startBottomRight);
 
   // Calculate new dimensions based on cursor position
-  let newWidth = stateAtResizeStart.width;
-  let newHeight = stateAtResizeStart.height;
   const rotatedPointer = rotatePoint(
     [pointerX, pointerY],
     startCenter,
     -stateAtResizeStart.angle,
   );
+
+  //Get bounds corners rendered on screen
+  const [esx1, esy1, esx2, esy2] = getResizedElementAbsoluteCoords(
+    element,
+    element.width,
+    element.height,
+  );
+  const boundsCurrentWidth = esx2 - esx1;
+  const boundsCurrentHeight = esy2 - esy1;
+
+  // It's important we set the initial scale value based on the width and height at resize start,
+  // otherwise previous dimensions affected by modifiers will be taken into account.
+  const atStartBoundsWidth = startBottomRight[0] - startTopLeft[0];
+  const atStartBoundsHeight = startBottomRight[1] - startTopLeft[1];
+  let scaleX = atStartBoundsWidth / boundsCurrentWidth;
+  let scaleY = atStartBoundsHeight / boundsCurrentHeight;
+
   if (transformHandleDirection.includes("e")) {
-    newWidth = rotatedPointer[0] - startTopLeft[0];
+    scaleX = (rotatedPointer[0] - startTopLeft[0]) / boundsCurrentWidth;
   }
   if (transformHandleDirection.includes("s")) {
-    newHeight = rotatedPointer[1] - startTopLeft[1];
+    scaleY = (rotatedPointer[1] - startTopLeft[1]) / boundsCurrentHeight;
   }
   if (transformHandleDirection.includes("w")) {
-    newWidth = startBottomRight[0] - rotatedPointer[0];
+    scaleX = (startBottomRight[0] - rotatedPointer[0]) / boundsCurrentWidth;
   }
   if (transformHandleDirection.includes("n")) {
-    newHeight = startBottomRight[1] - rotatedPointer[1];
+    scaleY = (startBottomRight[1] - rotatedPointer[1]) / boundsCurrentHeight;
   }
+  // Linear elements dimensions differ from bounds dimensions
+  const eleInitialWidth = stateAtResizeStart.width;
+  const eleInitialHeight = stateAtResizeStart.height;
+  // We have to use dimensions of element on screen, otherwise the scaling of the
+  // dimensions won't match the cursor for linear elements.
+  let eleNewWidth = element.width * scaleX;
+  let eleNewHeight = element.height * scaleY;
 
   // adjust dimensions for resizing from center
   if (isResizeFromCenter) {
-    newWidth = 2 * newWidth - stateAtResizeStart.width;
-    newHeight = 2 * newHeight - stateAtResizeStart.height;
+    eleNewWidth = 2 * eleNewWidth - eleInitialWidth;
+    eleNewHeight = 2 * eleNewHeight - eleInitialHeight;
   }
 
   // adjust dimensions to keep sides ratio
   if (shouldKeepSidesRatio) {
-    const widthRatio = Math.abs(newWidth) / stateAtResizeStart.width;
-    const heightRatio = Math.abs(newHeight) / stateAtResizeStart.height;
+    const widthRatio = Math.abs(eleNewWidth) / eleInitialWidth;
+    const heightRatio = Math.abs(eleNewHeight) / eleInitialHeight;
     if (transformHandleDirection.length === 1) {
-      newHeight *= widthRatio;
-      newWidth *= heightRatio;
+      eleNewHeight *= widthRatio;
+      eleNewWidth *= heightRatio;
     }
     if (transformHandleDirection.length === 2) {
       const ratio = Math.max(widthRatio, heightRatio);
-      newWidth = stateAtResizeStart.width * ratio * Math.sign(newWidth);
-      newHeight = stateAtResizeStart.height * ratio * Math.sign(newHeight);
+      eleNewWidth = eleInitialWidth * ratio * Math.sign(eleNewWidth);
+      eleNewHeight = eleInitialHeight * ratio * Math.sign(eleNewHeight);
     }
   }
 
+  const [
+    newBoundsX1,
+    newBoundsY1,
+    newBoundsX2,
+    newBoundsY2,
+  ] = getResizedElementAbsoluteCoords(
+    stateAtResizeStart,
+    eleNewWidth,
+    eleNewHeight,
+  );
+  const newBoundsWidth = newBoundsX2 - newBoundsX1;
+  const newBoundsHeight = newBoundsY2 - newBoundsY1;
+
   // Calculate new topLeft based on fixed corner during resize
-  let newTopLeft = startTopLeft as [number, number];
+  let newTopLeft = [...startTopLeft] as [number, number];
   if (["n", "w", "nw"].includes(transformHandleDirection)) {
     newTopLeft = [
-      startBottomRight[0] - Math.abs(newWidth),
-      startBottomRight[1] - Math.abs(newHeight),
+      startBottomRight[0] - Math.abs(newBoundsWidth),
+      startBottomRight[1] - Math.abs(newBoundsHeight),
     ];
   }
   if (transformHandleDirection === "ne") {
-    const bottomLeft = [
-      stateAtResizeStart.x,
-      stateAtResizeStart.y + stateAtResizeStart.height,
-    ];
-    newTopLeft = [bottomLeft[0], bottomLeft[1] - Math.abs(newHeight)];
+    const bottomLeft = [startTopLeft[0], startBottomRight[1]];
+    newTopLeft = [bottomLeft[0], bottomLeft[1] - Math.abs(newBoundsHeight)];
   }
   if (transformHandleDirection === "sw") {
-    const topRight = [
-      stateAtResizeStart.x + stateAtResizeStart.width,
-      stateAtResizeStart.y,
-    ];
-    newTopLeft = [topRight[0] - Math.abs(newWidth), topRight[1]];
+    const topRight = [startBottomRight[0], startTopLeft[1]];
+    newTopLeft = [topRight[0] - Math.abs(newBoundsWidth), topRight[1]];
   }
 
   // Keeps opposite handle fixed during resize
   if (shouldKeepSidesRatio) {
     if (["s", "n"].includes(transformHandleDirection)) {
-      newTopLeft[0] = startCenter[0] - newWidth / 2;
+      newTopLeft[0] = startCenter[0] - newBoundsWidth / 2;
     }
     if (["e", "w"].includes(transformHandleDirection)) {
-      newTopLeft[1] = startCenter[1] - newHeight / 2;
+      newTopLeft[1] = startCenter[1] - newBoundsHeight / 2;
     }
   }
 
   // Flip horizontally
-  if (newWidth < 0) {
+  if (eleNewWidth < 0) {
     if (transformHandleDirection.includes("e")) {
-      newTopLeft[0] -= Math.abs(newWidth);
+      newTopLeft[0] -= Math.abs(newBoundsWidth);
     }
     if (transformHandleDirection.includes("w")) {
-      newTopLeft[0] += Math.abs(newWidth);
+      newTopLeft[0] += Math.abs(newBoundsWidth);
     }
   }
   // Flip vertically
-  if (newHeight < 0) {
+  if (eleNewHeight < 0) {
     if (transformHandleDirection.includes("s")) {
-      newTopLeft[1] -= Math.abs(newHeight);
+      newTopLeft[1] -= Math.abs(newBoundsHeight);
     }
     if (transformHandleDirection.includes("n")) {
-      newTopLeft[1] += Math.abs(newHeight);
+      newTopLeft[1] += Math.abs(newBoundsHeight);
     }
   }
 
   if (isResizeFromCenter) {
-    newTopLeft[0] = startCenter[0] - Math.abs(newWidth) / 2;
-    newTopLeft[1] = startCenter[1] - Math.abs(newHeight) / 2;
+    newTopLeft[0] = startCenter[0] - Math.abs(newBoundsWidth) / 2;
+    newTopLeft[1] = startCenter[1] - Math.abs(newBoundsHeight) / 2;
   }
 
   // adjust topLeft to new rotation point
   const angle = stateAtResizeStart.angle;
   const rotatedTopLeft = rotatePoint(newTopLeft, startCenter, angle);
   const newCenter: Point = [
-    newTopLeft[0] + Math.abs(newWidth) / 2,
-    newTopLeft[1] + Math.abs(newHeight) / 2,
+    newTopLeft[0] + Math.abs(newBoundsWidth) / 2,
+    newTopLeft[1] + Math.abs(newBoundsHeight) / 2,
   ];
   const rotatedNewCenter = rotatePoint(newCenter, startCenter, angle);
   newTopLeft = rotatePoint(rotatedTopLeft, rotatedNewCenter, -angle);
 
-  const resizedElement = {
-    width: Math.abs(newWidth),
-    height: Math.abs(newHeight),
-    x: newTopLeft[0],
-    y: newTopLeft[1],
-  };
-  updateBoundElements(element, {
-    newSize: { width: resizedElement.width, height: resizedElement.height },
-  });
-  mutateElement(element, resizedElement);
-};
-
-const resizeSingleNonGenericElement = (
-  element: NonDeleted<Exclude<ExcalidrawElement, ExcalidrawGenericElement>>,
-  transformHandleType: "n" | "s" | "w" | "e" | "nw" | "ne" | "sw" | "se",
-  isResizeFromCenter: boolean,
-  keepSquareAspectRatio: boolean,
-  pointerX: number,
-  pointerY: number,
-) => {
-  const [x1, y1, x2, y2] = getElementAbsoluteCoords(element);
-  const cx = (x1 + x2) / 2;
-  const cy = (y1 + y2) / 2;
-
-  // rotation pointer with reverse angle
-  const [rotatedX, rotatedY] = rotate(
-    pointerX,
-    pointerY,
-    cx,
-    cy,
-    -element.angle,
+  // Readjust points for linear elements
+  const rescaledPoints = rescalePointsInElement(
+    stateAtResizeStart,
+    eleNewWidth,
+    eleNewHeight,
   );
+  // For linear elements (x,y) are the coordinates of the first drawn point not the top-left corner
+  // So we need to readjust (x,y) to be where the first point should be
+  const newOrigin = [...newTopLeft];
+  newOrigin[0] += stateAtResizeStart.x - newBoundsX1;
+  newOrigin[1] += stateAtResizeStart.y - newBoundsY1;
 
-  let scaleX = 1;
-  let scaleY = 1;
-  if (
-    transformHandleType === "e" ||
-    transformHandleType === "ne" ||
-    transformHandleType === "se"
-  ) {
-    scaleX = (rotatedX - x1) / (x2 - x1);
-  }
-  if (
-    transformHandleType === "s" ||
-    transformHandleType === "sw" ||
-    transformHandleType === "se"
-  ) {
-    scaleY = (rotatedY - y1) / (y2 - y1);
-  }
-  if (
-    transformHandleType === "w" ||
-    transformHandleType === "nw" ||
-    transformHandleType === "sw"
-  ) {
-    scaleX = (x2 - rotatedX) / (x2 - x1);
-  }
-  if (
-    transformHandleType === "n" ||
-    transformHandleType === "nw" ||
-    transformHandleType === "ne"
-  ) {
-    scaleY = (y2 - rotatedY) / (y2 - y1);
-  }
-  let nextWidth = element.width * scaleX;
-  let nextHeight = element.height * scaleY;
-  if (keepSquareAspectRatio) {
-    nextWidth = nextHeight = Math.max(nextWidth, nextHeight);
-  }
-
-  const [nextX1, nextY1, nextX2, nextY2] = getResizedElementAbsoluteCoords(
-    element,
-    nextWidth,
-    nextHeight,
-  );
-  const deltaX1 = (x1 - nextX1) / 2;
-  const deltaY1 = (y1 - nextY1) / 2;
-  const deltaX2 = (x2 - nextX2) / 2;
-  const deltaY2 = (y2 - nextY2) / 2;
-
-  const rescaledPoints = rescalePointsInElement(element, nextWidth, nextHeight);
-
-  updateBoundElements(element, {
-    newSize: { width: nextWidth, height: nextHeight },
-  });
-  const [finalX1, finalY1, finalX2, finalY2] = getResizedElementAbsoluteCoords(
-    {
-      ...element,
-      ...rescaledPoints,
-    },
-    Math.abs(nextWidth),
-    Math.abs(nextHeight),
-  );
-  const [flipDiffX, flipDiffY] = getFlipAdjustment(
-    transformHandleType,
-    nextWidth,
-    nextHeight,
-    nextX1,
-    nextY1,
-    nextX2,
-    nextY2,
-    finalX1,
-    finalY1,
-    finalX2,
-    finalY2,
-    isLinearElement(element),
-    element.angle,
-  );
-  const [nextElementX, nextElementY] = adjustXYWithRotation(
-    getSidesForTransformHandle(transformHandleType, isResizeFromCenter),
-    element.x - flipDiffX,
-    element.y - flipDiffY,
-    element.angle,
-    deltaX1,
-    deltaY1,
-    deltaX2,
-    deltaY2,
-  );
+  const resizedElement = {
+    width: Math.abs(eleNewWidth),
+    height: Math.abs(eleNewHeight),
+    x: newOrigin[0],
+    y: newOrigin[1],
+    ...rescaledPoints,
+  };
 
   if (
-    nextWidth !== 0 &&
-    nextHeight !== 0 &&
-    Number.isFinite(nextElementX) &&
-    Number.isFinite(nextElementY)
+    resizedElement.width !== 0 &&
+    resizedElement.height !== 0 &&
+    Number.isFinite(resizedElement.x) &&
+    Number.isFinite(resizedElement.y)
   ) {
-    mutateElement(element, {
-      width: nextWidth,
-      height: nextHeight,
-      x: nextElementX,
-      y: nextElementY,
-      ...rescaledPoints,
+    updateBoundElements(element, {
+      newSize: { width: resizedElement.width, height: resizedElement.height },
     });
+    mutateElement(element, resizedElement);
   }
 };
 

+ 0 - 54
src/element/resizeTest.ts

@@ -173,57 +173,3 @@ export const getCursorForResizingElement = (resizingElement: {
 
   return cursor ? `${cursor}-resize` : "";
 };
-
-export const normalizeTransformHandleType = (
-  element: ExcalidrawElement,
-  transformHandleType: TransformHandleType,
-): TransformHandleType => {
-  if (element.width >= 0 && element.height >= 0) {
-    return transformHandleType;
-  }
-
-  if (element.width < 0 && element.height < 0) {
-    switch (transformHandleType) {
-      case "nw":
-        return "se";
-      case "ne":
-        return "sw";
-      case "se":
-        return "nw";
-      case "sw":
-        return "ne";
-    }
-  } else if (element.width < 0) {
-    switch (transformHandleType) {
-      case "nw":
-        return "ne";
-      case "ne":
-        return "nw";
-      case "se":
-        return "sw";
-      case "sw":
-        return "se";
-      case "e":
-        return "w";
-      case "w":
-        return "e";
-    }
-  } else {
-    switch (transformHandleType) {
-      case "nw":
-        return "sw";
-      case "ne":
-        return "se";
-      case "se":
-        return "ne";
-      case "sw":
-        return "nw";
-      case "n":
-        return "s";
-      case "s":
-        return "n";
-    }
-  }
-
-  return transformHandleType;
-};

+ 4 - 12
src/excalidraw-app/index.tsx

@@ -11,7 +11,7 @@ import { getDefaultAppState } from "../appState";
 import { ExcalidrawImperativeAPI } from "../components/App";
 import { ErrorDialog } from "../components/ErrorDialog";
 import { TopErrorBoundary } from "../components/TopErrorBoundary";
-import { APP_NAME, EVENT, TITLE_TIMEOUT } from "../constants";
+import { APP_NAME, EVENT, TITLE_TIMEOUT, VERSION_TIMEOUT } from "../constants";
 import { ImportedDataState } from "../data/types";
 import {
   ExcalidrawElement,
@@ -229,18 +229,10 @@ const ExcalidrawWrapper = (props: { collab: CollabAPI }) => {
   const { collab } = props;
 
   useEffect(() => {
-    // delayed by 15 sec so that the app has a time to load the latest SW
+    // Delayed so that the app has a time to load the latest SW
     setTimeout(() => {
-      const version = getVersion();
-      const loggedVersion = window.localStorage.getItem(
-        "excalidraw-lastLoggedVersion",
-      );
-      // prevent logging on multiple visits
-      if (version && version !== loggedVersion) {
-        window.localStorage.setItem("excalidraw-lastLoggedVersion", version);
-        trackEvent("load", "version", version);
-      }
-    }, 15000);
+      trackEvent("load", "version", getVersion());
+    }, VERSION_TIMEOUT);
 
     excalidrawRef.current!.readyPromise.then((excalidrawApi) => {
       initializeScene({

+ 0 - 58
src/math.ts

@@ -70,64 +70,6 @@ export const adjustXYWithRotation = (
   return [x, y];
 };
 
-export const getFlipAdjustment = (
-  side: "n" | "s" | "w" | "e" | "nw" | "ne" | "sw" | "se",
-  nextWidth: number,
-  nextHeight: number,
-  nextX1: number,
-  nextY1: number,
-  nextX2: number,
-  nextY2: number,
-  finalX1: number,
-  finalY1: number,
-  finalX2: number,
-  finalY2: number,
-  needsRotation: boolean,
-  angle: number,
-): [number, number] => {
-  const cos = Math.cos(angle);
-  const sin = Math.sin(angle);
-  let flipDiffX = 0;
-  let flipDiffY = 0;
-  if (nextWidth < 0) {
-    if (side === "e" || side === "ne" || side === "se") {
-      if (needsRotation) {
-        flipDiffX += (finalX2 - nextX1) * cos;
-        flipDiffY += (finalX2 - nextX1) * sin;
-      } else {
-        flipDiffX += finalX2 - nextX1;
-      }
-    }
-    if (side === "w" || side === "nw" || side === "sw") {
-      if (needsRotation) {
-        flipDiffX += (finalX1 - nextX2) * cos;
-        flipDiffY += (finalX1 - nextX2) * sin;
-      } else {
-        flipDiffX += finalX1 - nextX2;
-      }
-    }
-  }
-  if (nextHeight < 0) {
-    if (side === "s" || side === "se" || side === "sw") {
-      if (needsRotation) {
-        flipDiffY += (finalY2 - nextY1) * cos;
-        flipDiffX += (finalY2 - nextY1) * -sin;
-      } else {
-        flipDiffY += finalY2 - nextY1;
-      }
-    }
-    if (side === "n" || side === "ne" || side === "nw") {
-      if (needsRotation) {
-        flipDiffY += (finalY1 - nextY2) * cos;
-        flipDiffX += (finalY1 - nextY2) * -sin;
-      } else {
-        flipDiffY += finalY1 - nextY2;
-      }
-    }
-  }
-  return [flipDiffX, flipDiffY];
-};
-
 export const getPointOnAPath = (point: Point, path: Point[]) => {
   const [px, py] = point;
   const [start, ...other] = path;

+ 25 - 1
src/packages/excalidraw/CHANGELOG.md

@@ -12,15 +12,21 @@ The change should be grouped under one of the below section and must contain PR
 Please add the latest change on the top under the correct section.
 -->
 
-## [Unreleased]
+## 0.2.0
 
 ### Features
 
+- Add toast [#2772](https://github.com/excalidraw/excalidraw/pull/2772)
 - Add `cmd+o` shortcut to load scene [#2732](https://github.com/excalidraw/excalidraw/pull/2732)
 - Remove language picker, and add `langCode`, `renderFooter` [#2644](https://github.com/excalidraw/excalidraw/pull/2644):
   - BREAKING: removed the language picker from UI. It is now the host app's responsibility to implement a language picker if desirable, using the newly added [`renderFooter`](https://github.com/excalidraw/excalidraw/blob/master/src/packages/excalidraw/README.md#renderFooter) prop. The reasoning is that the i18n should be controlled by the app itself, not by the nested Excalidraw component.
   - Added [`langCode`](https://github.com/excalidraw/excalidraw/blob/master/src/packages/excalidraw/README.md#langCode) prop to control the UI language.
 - Add support for `exportToBackend` prop to allow host apps to implement shareable links [#2612](https://github.com/excalidraw/excalidraw/pull/2612/files)
+- Require use of a preset dialog size; adjust dialog sizing [#2684](https://github.com/excalidraw/excalidraw/pull/2684)
+- Add line chart and paste dialog selection [#2670](https://github.com/excalidraw/excalidraw/pull/2670)
+- Tweak editing behavior [#2668](https://github.com/excalidraw/excalidraw/pull/2668)
+- Change title to Excalidraw after a timeout
+- Checkmark to toggle context-menu-items [#2645](https://github.com/excalidraw/excalidraw/pull/2645)
 - Add zoom to selection [#2522](https://github.com/excalidraw/excalidraw/pull/2522)
 - Insert Library items in the middle of the screen [#2527](https://github.com/excalidraw/excalidraw/pull/2527)
 - Show shortcut context menu [#2501](https://github.com/excalidraw/excalidraw/pull/2501)
@@ -31,7 +37,12 @@ Please add the latest change on the top under the correct section.
 
 ### Fixes
 
+- Fix typo for initialData and point all links to master [#2707](https://github.com/excalidraw/excalidraw/pull/2707)
+- Fix compile error [#2685](https://github.com/excalidraw/excalidraw/pull/2685)
+- Center zoom on iPhone and iPad [#2642](https://github.com/excalidraw/excalidraw/pull/2642)
 - Allow text-selecting in dialogs & reset cursor [#2783](https://github.com/excalidraw/excalidraw/pull/2783)
+- Don't render due to zoom after unmount [#2779](https://github.com/excalidraw/excalidraw/pull/2779)
+- Track the chart type correctly [#2773](https://github.com/excalidraw/excalidraw/pull/2773)
 - Fix late-render due to debounced zoom [#2779](https://github.com/excalidraw/excalidraw/pull/2779)
 - Fix initialization when browser tab not focused [#2677](https://github.com/excalidraw/excalidraw/pull/2677)
 - Consistent case for export locale strings [#2622](https://github.com/excalidraw/excalidraw/pull/2622)
@@ -47,6 +58,11 @@ Please add the latest change on the top under the correct section.
 ### Improvements
 
 - Added Zen Mode to the context menu [#2734](https://github.com/excalidraw/excalidraw/pull/2734)
+- Do not reset to selection for draw tool [#2721]((https://github.com/excalidraw/excalidraw/pull/2721)
+- Make dialogs look more like dialogs [#2686](https://github.com/excalidraw/excalidraw/pull/2686)
+- Browse libraries styles fixed [#2694](https://github.com/excalidraw/excalidraw/pull/2694)
+- Change hint for 2-point lines on resize [#2655](https://github.com/excalidraw/excalidraw/pull/2655)
+- Align items in context menu [#2640](https://github.com/excalidraw/excalidraw/pull/2640)
 - Do not reset to selection when using the draw tool [#2721](https://github.com/excalidraw/excalidraw/pull/2721)
 - Display proper tooltip for 2-point lines during resize, and normalize modifier key labels in hints [#2655](https://github.com/excalidraw/excalidraw/pull/2655)
 - Improve error message around importing images [#2619](https://github.com/excalidraw/excalidraw/pull/2619)
@@ -56,6 +72,14 @@ Please add the latest change on the top under the correct section.
 - Hide shortcuts on pickers for mobile [#2508](https://github.com/excalidraw/excalidraw/pull/2508)
 - Hide stats and scrollToContent-button when mobile menus open [#2509](https://github.com/excalidraw/excalidraw/pull/2509)
 
+### Refactor
+
+- refactor: Converting span to kbd tag [#2774](https://github.com/excalidraw/excalidraw/pull/2774)
+- Media queries [#2680](https://github.com/excalidraw/excalidraw/pull/2680)
+- Remove duplicate entry from en.json[#2654](https://github.com/excalidraw/excalidraw/pull/2654)
+- Remove the word toggle from labels [#2648](https://github.com/excalidraw/excalidraw/pull/2648)
+-
+
 ### Chore
 
 - Bump ini from 1.3.5 to 1.3.7 in /src/packages/excalidraw [#2500](https://github.com/excalidraw/excalidraw/pull/2500)

+ 45 - 0
src/packages/excalidraw/README.md

@@ -142,6 +142,51 @@ export default function App() {
 | [`langCode`](#langCode) | string | `en` | Language code string |
 | [`renderFooter `](#renderFooter) | Function |  | Function that renders custom UI footer |
 
+### `Extra API's`
+
+#### `getSceneVersion`
+
+**How to use**
+
+<pre>
+import { getSceneVersion } from "@excalidraw/excalidraw";
+getSceneVersion(elements:  <a href="https://github.com/excalidraw/excalidraw/blob/master/src/element/types.ts#L78">ExcalidrawElement []</a>)
+</pre>
+
+This function returns the current scene version.
+
+#### `getSyncableElements`
+
+**_Signature_**
+
+<pre>
+getSyncableElements(elements:  <a href="https://github.com/excalidraw/excalidraw/blob/master/src/element/types.ts#L78">ExcalidrawElement []</a>):<a href="https://github.com/excalidraw/excalidraw/blob/master/src/element/types.ts#L78">ExcalidrawElement []</a>
+</pre>
+
+**How to use**
+
+```js
+import { getSyncableElements } from "@excalidraw/excalidraw";
+```
+
+This function returns all the deleted elements of the scene.
+
+### `getElementMap`
+
+**_Signature_**
+
+<pre>
+getElementsMap(elements:  <a href="https://github.com/excalidraw/excalidraw/blob/master/src/element/types.ts#L78">ExcalidrawElement []</a>): {[id: string]: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/element/types.ts#L78">ExcalidrawElement</a>}
+</pre>
+
+**How to use**
+
+```js
+import { getElementsMap } from "@excalidraw/excalidraw";
+```
+
+This function returns an object where each element is mapped to its id.
+
 #### `width`
 
 This props defines the `width` of the Excalidraw component. Defaults to `window.innerWidth` if not passed.

+ 1 - 1
src/packages/excalidraw/package-lock.json

@@ -1,6 +1,6 @@
 {
   "name": "@excalidraw/excalidraw",
-  "version": "0.1.1",
+  "version": "0.2.0",
   "lockfileVersion": 1,
   "requires": true,
   "dependencies": {

+ 1 - 1
src/packages/excalidraw/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@excalidraw/excalidraw",
-  "version": "0.1.1",
+  "version": "0.2.0",
   "main": "dist/excalidraw.min.js",
   "files": [
     "dist/*"

+ 1 - 1
src/tests/resize.test.tsx

@@ -41,7 +41,7 @@ describe("resize rectangle ellipses and diamond elements", () => {
     ${"s"}  | ${[_, 39]}      | ${[100, 139]} | ${[elemData.x, elemData.x]}
     ${"e"}  | ${[-20, _]}     | ${[80, 100]}  | ${[elemData.x, elemData.y]}
     ${"w"}  | ${[-20, _]}     | ${[120, 100]} | ${[-20, elemData.y]}
-    ${"ne"} | ${[10, 55]}     | ${[110, 45]}  | ${[elemData.x, 55]}
+    ${"ne"} | ${[5, 55]}      | ${[105, 45]}  | ${[elemData.x, 55]}
     ${"se"} | ${[-30, -10]}   | ${[70, 90]}   | ${[elemData.x, elemData.y]}
     ${"nw"} | ${[-300, -200]} | ${[400, 300]} | ${[-300, -200]}
     ${"sw"} | ${[40, -20]}    | ${[60, 80]}   | ${[40, 0]}