Browse Source

Merge remote-tracking branch 'origin/master' into barnabasmolnar/mainmenu-radix

Aakansha Doshi 2 years ago
parent
commit
66e347f7d2

+ 10 - 2
src/data/restore.ts

@@ -3,6 +3,7 @@ import {
   ExcalidrawSelectionElement,
   ExcalidrawSelectionElement,
   ExcalidrawTextElement,
   ExcalidrawTextElement,
   FontFamilyValues,
   FontFamilyValues,
+  PointBinding,
   StrokeRoundness,
   StrokeRoundness,
 } from "../element/types";
 } from "../element/types";
 import {
 import {
@@ -83,6 +84,13 @@ const getFontFamilyByName = (fontFamilyName: string): FontFamilyValues => {
   return DEFAULT_FONT_FAMILY;
   return DEFAULT_FONT_FAMILY;
 };
 };
 
 
+const repairBinding = (binding: PointBinding | null) => {
+  if (!binding) {
+    return null;
+  }
+  return { ...binding, focus: binding.focus || 0 };
+};
+
 const restoreElementWithProperties = <
 const restoreElementWithProperties = <
   T extends Required<Omit<ExcalidrawElement, "customData">> & {
   T extends Required<Omit<ExcalidrawElement, "customData">> & {
     customData?: ExcalidrawElement["customData"];
     customData?: ExcalidrawElement["customData"];
@@ -258,8 +266,8 @@ const restoreElement = (
           (element.type as ExcalidrawElement["type"] | "draw") === "draw"
           (element.type as ExcalidrawElement["type"] | "draw") === "draw"
             ? "line"
             ? "line"
             : element.type,
             : element.type,
-        startBinding: element.startBinding,
-        endBinding: element.endBinding,
+        startBinding: repairBinding(element.startBinding),
+        endBinding: repairBinding(element.endBinding),
         lastCommittedPoint: null,
         lastCommittedPoint: null,
         startArrowhead,
         startArrowhead,
         endArrowhead,
         endArrowhead,

+ 8 - 3
src/element/collision.ts

@@ -655,18 +655,23 @@ export const determineFocusDistance = (
   const c = line[1];
   const c = line[1];
   const mabs = Math.abs(m);
   const mabs = Math.abs(m);
   const nabs = Math.abs(n);
   const nabs = Math.abs(n);
+  let ret;
   switch (element.type) {
   switch (element.type) {
     case "rectangle":
     case "rectangle":
     case "image":
     case "image":
     case "text":
     case "text":
     case "embeddable":
     case "embeddable":
     case "frame":
     case "frame":
-      return c / (hwidth * (nabs + q * mabs));
+      ret = c / (hwidth * (nabs + q * mabs));
+      break;
     case "diamond":
     case "diamond":
-      return mabs < nabs ? c / (nabs * hwidth) : c / (mabs * hheight);
+      ret = mabs < nabs ? c / (nabs * hwidth) : c / (mabs * hheight);
+      break;
     case "ellipse":
     case "ellipse":
-      return c / (hwidth * Math.sqrt(n ** 2 + q ** 2 * m ** 2));
+      ret = c / (hwidth * Math.sqrt(n ** 2 + q ** 2 * m ** 2));
+      break;
   }
   }
+  return ret || 0;
 };
 };
 
 
 export const determineFocusPoint = (
 export const determineFocusPoint = (

+ 1 - 0
src/element/embeddable.ts

@@ -52,6 +52,7 @@ const ALLOWED_DOMAINS = new Set([
   "link.excalidraw.com",
   "link.excalidraw.com",
   "gist.github.com",
   "gist.github.com",
   "twitter.com",
   "twitter.com",
+  "stackblitz.com",
 ]);
 ]);
 
 
 const createSrcDoc = (body: string) => {
 const createSrcDoc = (body: string) => {

+ 1 - 1
src/excalidraw-app/collab/Collab.tsx

@@ -333,7 +333,7 @@ class Collab extends PureComponent<Props, CollabState> {
      * Indicates whether to fetch files that are errored or pending and older
      * Indicates whether to fetch files that are errored or pending and older
      * than 10 seconds.
      * than 10 seconds.
      *
      *
-     * Use this as a machanism to fetch files which may be ok but for some
+     * Use this as a mechanism to fetch files which may be ok but for some
      * reason their status was not updated correctly.
      * reason their status was not updated correctly.
      */
      */
     forceFetchFiles?: boolean;
     forceFetchFiles?: boolean;

+ 6 - 0
src/global.d.ts

@@ -120,3 +120,9 @@ declare module "image-blob-reduce" {
   const reduce: ImageBlobReduce.ImageBlobReduceStatic;
   const reduce: ImageBlobReduce.ImageBlobReduceStatic;
   export = reduce;
   export = reduce;
 }
 }
+
+declare namespace jest {
+  interface Expect {
+    toBeNonNaNNumber(): void;
+  }
+}

+ 55 - 0
src/tests/binding.test.tsx

@@ -15,6 +15,61 @@ describe("element binding", () => {
     await render(<ExcalidrawApp />);
     await render(<ExcalidrawApp />);
   });
   });
 
 
+  it("should create valid binding if duplicate start/end points", async () => {
+    const rect = API.createElement({
+      type: "rectangle",
+      x: 0,
+      width: 50,
+      height: 50,
+    });
+    const arrow = API.createElement({
+      type: "arrow",
+      x: 100,
+      y: 0,
+      width: 100,
+      height: 1,
+      points: [
+        [0, 0],
+        [0, 0],
+        [100, 0],
+        [100, 0],
+      ],
+    });
+    h.elements = [rect, arrow];
+    expect(arrow.startBinding).toBe(null);
+
+    API.setSelectedElements([arrow]);
+
+    expect(API.getSelectedElements()).toEqual([arrow]);
+    mouse.downAt(100, 0);
+    mouse.moveTo(55, 0);
+    mouse.up(0, 0);
+    expect(arrow.startBinding).toEqual({
+      elementId: rect.id,
+      focus: expect.toBeNonNaNNumber(),
+      gap: expect.toBeNonNaNNumber(),
+    });
+
+    mouse.downAt(100, 0);
+    mouse.move(-45, 0);
+    mouse.up();
+    expect(arrow.startBinding).toEqual({
+      elementId: rect.id,
+      focus: expect.toBeNonNaNNumber(),
+      gap: expect.toBeNonNaNNumber(),
+    });
+
+    mouse.down();
+    mouse.move(-50, 0);
+    mouse.up();
+    expect(arrow.startBinding).toBe(null);
+    expect(arrow.endBinding).toEqual({
+      elementId: rect.id,
+      focus: expect.toBeNonNaNNumber(),
+      gap: expect.toBeNonNaNNumber(),
+    });
+  });
+
   //@TODO fix the test with rotation
   //@TODO fix the test with rotation
   it.skip("rotation of arrow should rebind both ends", () => {
   it.skip("rotation of arrow should rebind both ends", () => {
     const rectLeft = UI.createElement("rectangle", {
     const rectLeft = UI.createElement("rectangle", {

+ 16 - 0
src/tests/test-utils.ts

@@ -228,3 +228,19 @@ export const togglePopover = (label: string) => {
 
 
   UI.clickLabeledElement(label);
   UI.clickLabeledElement(label);
 };
 };
+
+expect.extend({
+  toBeNonNaNNumber(received) {
+    const pass = typeof received === "number" && !isNaN(received);
+    if (pass) {
+      return {
+        message: () => `expected ${received} not to be a non-NaN number`,
+        pass: true,
+      };
+    }
+    return {
+      message: () => `expected ${received} to be a non-NaN number`,
+      pass: false,
+    };
+  },
+});