diff --git a/packages/excalidraw/components/TTDDialog/MermaidToExcalidraw.tsx b/packages/excalidraw/components/TTDDialog/MermaidToExcalidraw.tsx index 237cb26066..c6c8f1f1cc 100644 --- a/packages/excalidraw/components/TTDDialog/MermaidToExcalidraw.tsx +++ b/packages/excalidraw/components/TTDDialog/MermaidToExcalidraw.tsx @@ -18,7 +18,7 @@ import { TTDDialogInput } from "./TTDDialogInput"; import { TTDDialogOutput } from "./TTDDialogOutput"; import { EditorLocalStorage } from "../../data/EditorLocalStorage"; import { EDITOR_LS_KEYS } from "../../constants"; -import { debounce } from "../../utils"; +import { debounce, isDevEnv } from "../../utils"; import { TTDDialogSubmitShortcut } from "./TTDDialogSubmitShortcut"; const MERMAID_EXAMPLE = @@ -54,7 +54,11 @@ const MermaidToExcalidraw = ({ mermaidToExcalidrawLib, setError, mermaidDefinition: deferredText, - }).catch(() => {}); + }).catch((err) => { + if (isDevEnv()) { + console.error("Failed to parse mermaid definition", err); + } + }); debouncedSaveMermaidDefinition(deferredText); }, [deferredText, mermaidToExcalidrawLib]); diff --git a/packages/excalidraw/data/__snapshots__/transform.test.ts.snap b/packages/excalidraw/data/__snapshots__/transform.test.ts.snap index 6baac1d9de..adb5b03728 100644 --- a/packages/excalidraw/data/__snapshots__/transform.test.ts.snap +++ b/packages/excalidraw/data/__snapshots__/transform.test.ts.snap @@ -1271,6 +1271,546 @@ exports[`Test Transform > should transform text element 2`] = ` } `; +exports[`Test Transform > should transform the elements correctly when linear elements have single point 1`] = ` +{ + "angle": 0, + "backgroundColor": "transparent", + "boundElements": [ + { + "id": "id54", + "type": "text", + }, + { + "id": "Bob_B", + "type": "arrow", + }, + ], + "customData": undefined, + "fillStyle": "solid", + "frameId": null, + "groupIds": [ + "subgraph_group_B", + ], + "height": 163, + "id": Any, + "index": "a0", + "isDeleted": false, + "link": null, + "locked": false, + "opacity": 100, + "roughness": 1, + "roundness": null, + "seed": Any, + "strokeColor": "#1e1e1e", + "strokeStyle": "solid", + "strokeWidth": 2, + "type": "rectangle", + "updated": 1, + "version": 3, + "versionNonce": Any, + "width": 166.03125, + "x": 0, + "y": 0, +} +`; + +exports[`Test Transform > should transform the elements correctly when linear elements have single point 2`] = ` +{ + "angle": 0, + "backgroundColor": "transparent", + "boundElements": [ + { + "id": "id55", + "type": "text", + }, + ], + "customData": undefined, + "fillStyle": "solid", + "frameId": null, + "groupIds": [ + "subgraph_group_A", + ], + "height": 114, + "id": Any, + "index": "a1", + "isDeleted": false, + "link": null, + "locked": false, + "opacity": 100, + "roughness": 1, + "roundness": null, + "seed": Any, + "strokeColor": "#1e1e1e", + "strokeStyle": "solid", + "strokeWidth": 2, + "type": "rectangle", + "updated": 1, + "version": 2, + "versionNonce": Any, + "width": 120.265625, + "x": 364.546875, + "y": 0, +} +`; + +exports[`Test Transform > should transform the elements correctly when linear elements have single point 3`] = ` +{ + "angle": 0, + "backgroundColor": "transparent", + "boundElements": [ + { + "id": "id56", + "type": "text", + }, + { + "id": "Bob_Alice", + "type": "arrow", + }, + ], + "customData": undefined, + "fillStyle": "solid", + "frameId": null, + "groupIds": [ + "subgraph_group_A", + ], + "height": 44, + "id": Any, + "index": "a2", + "isDeleted": false, + "link": null, + "locked": false, + "opacity": 100, + "roughness": 1, + "roundness": null, + "seed": Any, + "strokeColor": "#1e1e1e", + "strokeStyle": "solid", + "strokeWidth": 2, + "type": "rectangle", + "updated": 1, + "version": 3, + "versionNonce": Any, + "width": 70.265625, + "x": 389.546875, + "y": 35, +} +`; + +exports[`Test Transform > should transform the elements correctly when linear elements have single point 4`] = ` +{ + "angle": 0, + "backgroundColor": "transparent", + "boundElements": [ + { + "id": "id57", + "type": "text", + }, + { + "id": "Bob_Alice", + "type": "arrow", + }, + { + "id": "Bob_B", + "type": "arrow", + }, + ], + "customData": undefined, + "fillStyle": "solid", + "frameId": null, + "groupIds": [ + "subgraph_group_B", + ], + "height": 44, + "id": Any, + "index": "a3", + "isDeleted": false, + "link": null, + "locked": false, + "opacity": 100, + "roughness": 1, + "roundness": null, + "seed": Any, + "strokeColor": "#1e1e1e", + "strokeStyle": "solid", + "strokeWidth": 2, + "type": "rectangle", + "updated": 1, + "version": 4, + "versionNonce": Any, + "width": 56.4921875, + "x": 54.76953125, + "y": 35, +} +`; + +exports[`Test Transform > should transform the elements correctly when linear elements have single point 5`] = ` +{ + "angle": 0, + "backgroundColor": "transparent", + "boundElements": [ + { + "id": "id58", + "type": "text", + }, + ], + "customData": undefined, + "endArrowhead": "arrow", + "endBinding": { + "elementId": "Alice", + "focus": 0, + "gap": 5.299874999999986, + }, + "fillStyle": "solid", + "frameId": null, + "groupIds": [], + "height": 0, + "id": Any, + "index": "a4", + "isDeleted": false, + "lastCommittedPoint": null, + "link": null, + "locked": false, + "opacity": 100, + "points": [ + [ + 0.5, + 0, + ], + [ + 272.485, + 0, + ], + ], + "roughness": 1, + "roundness": { + "type": 2, + }, + "seed": Any, + "startArrowhead": null, + "startBinding": { + "elementId": "Bob", + "focus": 0, + "gap": 1, + }, + "strokeColor": "#1e1e1e", + "strokeStyle": "solid", + "strokeWidth": 2, + "type": "arrow", + "updated": 1, + "version": 4, + "versionNonce": Any, + "width": 272.985, + "x": 111.262, + "y": 57, +} +`; + +exports[`Test Transform > should transform the elements correctly when linear elements have single point 6`] = ` +{ + "angle": 0, + "backgroundColor": "transparent", + "boundElements": [ + { + "id": "id59", + "type": "text", + }, + ], + "customData": undefined, + "endArrowhead": "arrow", + "endBinding": { + "elementId": "B", + "focus": 0, + "gap": 1, + }, + "fillStyle": "solid", + "frameId": null, + "groupIds": [], + "height": 0, + "id": Any, + "index": "a5", + "isDeleted": false, + "lastCommittedPoint": null, + "link": null, + "locked": false, + "opacity": 100, + "points": [ + [ + 0, + 0, + ], + ], + "roughness": 1, + "roundness": { + "type": 2, + }, + "seed": Any, + "startArrowhead": null, + "startBinding": { + "elementId": "Bob", + "focus": 0, + "gap": 1, + }, + "strokeColor": "#1e1e1e", + "strokeStyle": "solid", + "strokeWidth": 2, + "type": "arrow", + "updated": 1, + "version": 4, + "versionNonce": Any, + "width": 0, + "x": 77.017, + "y": 79, +} +`; + +exports[`Test Transform > should transform the elements correctly when linear elements have single point 7`] = ` +{ + "angle": 0, + "backgroundColor": "transparent", + "boundElements": null, + "containerId": "B", + "customData": undefined, + "fillStyle": "solid", + "fontFamily": 1, + "fontSize": 20, + "frameId": null, + "groupIds": [ + "subgraph_group_B", + ], + "height": 25, + "id": Any, + "index": "a6", + "isDeleted": false, + "lineHeight": 1.25, + "link": null, + "locked": false, + "opacity": 100, + "originalText": "B", + "roughness": 1, + "roundness": null, + "seed": Any, + "strokeColor": "#1e1e1e", + "strokeStyle": "solid", + "strokeWidth": 2, + "text": "B", + "textAlign": "center", + "type": "text", + "updated": 1, + "version": 3, + "versionNonce": Any, + "verticalAlign": "top", + "width": 10, + "x": 78.015625, + "y": 5, +} +`; + +exports[`Test Transform > should transform the elements correctly when linear elements have single point 8`] = ` +{ + "angle": 0, + "backgroundColor": "transparent", + "boundElements": null, + "containerId": "A", + "customData": undefined, + "fillStyle": "solid", + "fontFamily": 1, + "fontSize": 20, + "frameId": null, + "groupIds": [ + "subgraph_group_A", + ], + "height": 25, + "id": Any, + "index": "a7", + "isDeleted": false, + "lineHeight": 1.25, + "link": null, + "locked": false, + "opacity": 100, + "originalText": "A", + "roughness": 1, + "roundness": null, + "seed": Any, + "strokeColor": "#1e1e1e", + "strokeStyle": "solid", + "strokeWidth": 2, + "text": "A", + "textAlign": "center", + "type": "text", + "updated": 1, + "version": 3, + "versionNonce": Any, + "verticalAlign": "top", + "width": 10, + "x": 419.6796875, + "y": 5, +} +`; + +exports[`Test Transform > should transform the elements correctly when linear elements have single point 9`] = ` +{ + "angle": 0, + "backgroundColor": "transparent", + "boundElements": null, + "containerId": "Alice", + "customData": undefined, + "fillStyle": "solid", + "fontFamily": 1, + "fontSize": 20, + "frameId": null, + "groupIds": [ + "subgraph_group_A", + ], + "height": 25, + "id": Any, + "index": "a8", + "isDeleted": false, + "lineHeight": 1.25, + "link": null, + "locked": false, + "opacity": 100, + "originalText": "Alice", + "roughness": 1, + "roundness": null, + "seed": Any, + "strokeColor": "#1e1e1e", + "strokeStyle": "solid", + "strokeWidth": 2, + "text": "Alice", + "textAlign": "center", + "type": "text", + "updated": 1, + "version": 3, + "versionNonce": Any, + "verticalAlign": "middle", + "width": 50, + "x": 399.6796875, + "y": 44.5, +} +`; + +exports[`Test Transform > should transform the elements correctly when linear elements have single point 10`] = ` +{ + "angle": 0, + "backgroundColor": "transparent", + "boundElements": null, + "containerId": "Bob", + "customData": undefined, + "fillStyle": "solid", + "fontFamily": 1, + "fontSize": 20, + "frameId": null, + "groupIds": [ + "subgraph_group_B", + ], + "height": 25, + "id": Any, + "index": "a9", + "isDeleted": false, + "lineHeight": 1.25, + "link": null, + "locked": false, + "opacity": 100, + "originalText": "Bob", + "roughness": 1, + "roundness": null, + "seed": Any, + "strokeColor": "#1e1e1e", + "strokeStyle": "solid", + "strokeWidth": 2, + "text": "Bob", + "textAlign": "center", + "type": "text", + "updated": 1, + "version": 3, + "versionNonce": Any, + "verticalAlign": "middle", + "width": 30, + "x": 68.015625, + "y": 44.5, +} +`; + +exports[`Test Transform > should transform the elements correctly when linear elements have single point 11`] = ` +{ + "angle": 0, + "backgroundColor": "transparent", + "boundElements": null, + "containerId": "Bob_Alice", + "customData": undefined, + "fillStyle": "solid", + "fontFamily": 1, + "fontSize": 20, + "frameId": null, + "groupIds": [], + "height": 25, + "id": Any, + "index": "aA", + "isDeleted": false, + "lineHeight": 1.25, + "link": null, + "locked": false, + "opacity": 100, + "originalText": "How are you?", + "roughness": 1, + "roundness": null, + "seed": Any, + "strokeColor": "#1e1e1e", + "strokeStyle": "solid", + "strokeWidth": 2, + "text": "How are you?", + "textAlign": "center", + "type": "text", + "updated": 1, + "version": 3, + "versionNonce": Any, + "verticalAlign": "middle", + "width": 120, + "x": 187.7545, + "y": 44.5, +} +`; + +exports[`Test Transform > should transform the elements correctly when linear elements have single point 12`] = ` +{ + "angle": 0, + "backgroundColor": "transparent", + "boundElements": null, + "containerId": "Bob_B", + "customData": undefined, + "fillStyle": "solid", + "fontFamily": 1, + "fontSize": 20, + "frameId": null, + "groupIds": [], + "height": 25, + "id": Any, + "index": "aB", + "isDeleted": false, + "lineHeight": 1.25, + "link": null, + "locked": false, + "opacity": 100, + "originalText": "Friendship", + "roughness": 1, + "roundness": null, + "seed": Any, + "strokeColor": "#1e1e1e", + "strokeStyle": "solid", + "strokeWidth": 2, + "text": "Friendship", + "textAlign": "center", + "type": "text", + "updated": 1, + "version": 3, + "versionNonce": Any, + "verticalAlign": "middle", + "width": 100, + "x": 27.016999999999996, + "y": 66.5, +} +`; + exports[`Test Transform > should transform to labelled arrows when label provided for arrows 1`] = ` { "angle": 0, diff --git a/packages/excalidraw/data/transform.test.ts b/packages/excalidraw/data/transform.test.ts index 239cd2f4cc..4d58fbac62 100644 --- a/packages/excalidraw/data/transform.test.ts +++ b/packages/excalidraw/data/transform.test.ts @@ -152,14 +152,14 @@ describe("Test Transform", () => { strokeStyle: "dotted", }, ]; - const excaldrawElements = convertToExcalidrawElements( + const excalidrawElements = convertToExcalidrawElements( elements as ExcalidrawElementSkeleton[], opts, ); - expect(excaldrawElements.length).toBe(4); + expect(excalidrawElements.length).toBe(4); - excaldrawElements.forEach((ele) => { + excalidrawElements.forEach((ele) => { expect(ele).toMatchSnapshot({ seed: expect.any(Number), versionNonce: expect.any(Number), @@ -235,14 +235,14 @@ describe("Test Transform", () => { }, }, ]; - const excaldrawElements = convertToExcalidrawElements( + const excalidrawElements = convertToExcalidrawElements( elements as ExcalidrawElementSkeleton[], opts, ); - expect(excaldrawElements.length).toBe(12); + expect(excalidrawElements.length).toBe(12); - excaldrawElements.forEach((ele) => { + excalidrawElements.forEach((ele) => { expect(ele).toMatchSnapshot({ seed: expect.any(Number), versionNonce: expect.any(Number), @@ -293,14 +293,14 @@ describe("Test Transform", () => { }, }, ]; - const excaldrawElements = convertToExcalidrawElements( + const excalidrawElements = convertToExcalidrawElements( elements as ExcalidrawElementSkeleton[], opts, ); - expect(excaldrawElements.length).toBe(8); + expect(excalidrawElements.length).toBe(8); - excaldrawElements.forEach((ele) => { + excalidrawElements.forEach((ele) => { expect(ele).toMatchSnapshot({ seed: expect.any(Number), versionNonce: expect.any(Number), @@ -338,13 +338,13 @@ describe("Test Transform", () => { name: "My frame", }, ]; - const excaldrawElements = convertToExcalidrawElements( + const excalidrawElements = convertToExcalidrawElements( elementsSkeleton, opts, ); - expect(excaldrawElements.length).toBe(4); + expect(excalidrawElements.length).toBe(4); - excaldrawElements.forEach((ele) => { + excalidrawElements.forEach((ele) => { expect(ele).toMatchObject({ seed: expect.any(Number), versionNonce: expect.any(Number), @@ -383,11 +383,11 @@ describe("Test Transform", () => { height: 100, }, ]; - const excaldrawElements = convertToExcalidrawElements( + const excalidrawElements = convertToExcalidrawElements( elementsSkeleton, opts, ); - const frame = excaldrawElements.find((ele) => ele.type === "frame")!; + const frame = excalidrawElements.find((ele) => ele.type === "frame")!; expect(frame.width).toBe(800); expect(frame.height).toBe(126); }); @@ -411,13 +411,13 @@ describe("Test Transform", () => { }, }, ]; - const excaldrawElements = convertToExcalidrawElements( + const excalidrawElements = convertToExcalidrawElements( elements as ExcalidrawElementSkeleton[], opts, ); - expect(excaldrawElements.length).toBe(4); - const [arrow, text, rectangle, ellipse] = excaldrawElements; + expect(excalidrawElements.length).toBe(4); + const [arrow, text, rectangle, ellipse] = excalidrawElements; expect(arrow).toMatchObject({ type: "arrow", x: 255, @@ -466,7 +466,7 @@ describe("Test Transform", () => { ], }); - excaldrawElements.forEach((ele) => { + excalidrawElements.forEach((ele) => { expect(ele).toMatchSnapshot({ seed: expect.any(Number), versionNonce: expect.any(Number), @@ -495,13 +495,13 @@ describe("Test Transform", () => { }, ]; - const excaldrawElements = convertToExcalidrawElements( + const excalidrawElements = convertToExcalidrawElements( elements as ExcalidrawElementSkeleton[], opts, ); - expect(excaldrawElements.length).toBe(4); - const [arrow, text1, text2, text3] = excaldrawElements; + expect(excalidrawElements.length).toBe(4); + const [arrow, text1, text2, text3] = excalidrawElements; expect(arrow).toMatchObject({ type: "arrow", @@ -551,7 +551,7 @@ describe("Test Transform", () => { ], }); - excaldrawElements.forEach((ele) => { + excalidrawElements.forEach((ele) => { expect(ele).toMatchSnapshot({ seed: expect.any(Number), versionNonce: expect.any(Number), @@ -611,14 +611,14 @@ describe("Test Transform", () => { }, ]; - const excaldrawElements = convertToExcalidrawElements( + const excalidrawElements = convertToExcalidrawElements( elements as ExcalidrawElementSkeleton[], opts, ); - expect(excaldrawElements.length).toBe(5); + expect(excalidrawElements.length).toBe(5); - excaldrawElements.forEach((ele) => { + excalidrawElements.forEach((ele) => { expect(ele).toMatchSnapshot({ seed: expect.any(Number), versionNonce: expect.any(Number), @@ -660,14 +660,14 @@ describe("Test Transform", () => { }, ]; - const excaldrawElements = convertToExcalidrawElements( + const excalidrawElements = convertToExcalidrawElements( elements as ExcalidrawElementSkeleton[], opts, ); - expect(excaldrawElements.length).toBe(4); + expect(excalidrawElements.length).toBe(4); - excaldrawElements.forEach((ele) => { + excalidrawElements.forEach((ele) => { expect(ele).toMatchSnapshot({ seed: expect.any(Number), versionNonce: expect.any(Number), @@ -714,13 +714,13 @@ describe("Test Transform", () => { }, ]; - const excaldrawElements = convertToExcalidrawElements( + const excalidrawElements = convertToExcalidrawElements( elements as ExcalidrawElementSkeleton[], opts, ); - expect(excaldrawElements.length).toBe(4); - const [, , arrow, text] = excaldrawElements; + expect(excalidrawElements.length).toBe(4); + const [, , arrow, text] = excalidrawElements; expect(arrow).toMatchObject({ type: "arrow", x: 255, @@ -765,12 +765,12 @@ describe("Test Transform", () => { backgroundColor: "#bac8ff", }, ]; - const excaldrawElements = convertToExcalidrawElements( + const excalidrawElements = convertToExcalidrawElements( elements as ExcalidrawElementSkeleton[], opts, ); - expect(excaldrawElements.length).toBe(2); - const [arrow, rect] = excaldrawElements; + expect(excalidrawElements.length).toBe(2); + const [arrow, rect] = excalidrawElements; expect((arrow as ExcalidrawArrowElement).endBinding).toStrictEqual({ elementId: "rect-1", focus: 0, @@ -808,13 +808,13 @@ describe("Test Transform", () => { height: 200, }, ]; - const excaldrawElements = convertToExcalidrawElements( + const excalidrawElements = convertToExcalidrawElements( elements as ExcalidrawElementSkeleton[], opts, ); - expect(excaldrawElements.length).toBe(1); - expect(excaldrawElements[0]).toMatchSnapshot({ + expect(excalidrawElements.length).toBe(1); + expect(excalidrawElements[0]).toMatchSnapshot({ seed: expect.any(Number), versionNonce: expect.any(Number), }); @@ -840,4 +840,130 @@ describe("Test Transform", () => { createdBy: "user01", }); }); + + it("should transform the elements correctly when linear elements have single point", () => { + const elements: ExcalidrawElementSkeleton[] = [ + { + id: "B", + type: "rectangle", + groupIds: ["subgraph_group_B"], + x: 0, + y: 0, + width: 166.03125, + height: 163, + label: { + groupIds: ["subgraph_group_B"], + text: "B", + fontSize: 20, + verticalAlign: "top", + }, + }, + { + id: "A", + type: "rectangle", + groupIds: ["subgraph_group_A"], + x: 364.546875, + y: 0, + width: 120.265625, + height: 114, + label: { + groupIds: ["subgraph_group_A"], + text: "A", + fontSize: 20, + verticalAlign: "top", + }, + }, + { + id: "Alice", + type: "rectangle", + groupIds: ["subgraph_group_A"], + x: 389.546875, + y: 35, + width: 70.265625, + height: 44, + strokeWidth: 2, + label: { + groupIds: ["subgraph_group_A"], + text: "Alice", + fontSize: 20, + }, + link: null, + }, + { + id: "Bob", + type: "rectangle", + groupIds: ["subgraph_group_B"], + x: 54.76953125, + y: 35, + width: 56.4921875, + height: 44, + strokeWidth: 2, + label: { + groupIds: ["subgraph_group_B"], + text: "Bob", + fontSize: 20, + }, + link: null, + }, + { + id: "Bob_Alice", + type: "arrow", + groupIds: [], + x: 111.262, + y: 57, + strokeWidth: 2, + points: [ + [0, 0], + [272.985, 0], + ], + label: { + text: "How are you?", + fontSize: 20, + groupIds: [], + }, + roundness: { + type: 2, + }, + start: { + id: "Bob", + }, + end: { + id: "Alice", + }, + }, + { + id: "Bob_B", + type: "arrow", + groupIds: [], + x: 77.017, + y: 79, + strokeWidth: 2, + points: [[0, 0]], + label: { + text: "Friendship", + fontSize: 20, + groupIds: [], + }, + roundness: { + type: 2, + }, + start: { + id: "Bob", + }, + end: { + id: "B", + }, + }, + ]; + + const excalidrawElements = convertToExcalidrawElements(elements, opts); + expect(excalidrawElements.length).toBe(12); + excalidrawElements.forEach((ele) => { + expect(ele).toMatchSnapshot({ + seed: expect.any(Number), + versionNonce: expect.any(Number), + id: expect.any(String), + }); + }); + }); }); diff --git a/packages/excalidraw/data/transform.ts b/packages/excalidraw/data/transform.ts index 9706f849fd..79f9af9628 100644 --- a/packages/excalidraw/data/transform.ts +++ b/packages/excalidraw/data/transform.ts @@ -405,11 +405,21 @@ const bindLinearElementToElement = ( } } + // Safe check to early return for single point + if (linearElement.points.length < 2) { + return { + linearElement, + startBoundElement, + endBoundElement, + }; + } + // Update start/end points by 0.5 so bindings don't overlap with start/end bound element coordinates. const endPointIndex = linearElement.points.length - 1; const delta = 0.5; const newPoints = cloneJSON(linearElement.points) as [number, number][]; + // left to right so shift the arrow towards right if ( linearElement.points[endPointIndex][0] > diff --git a/packages/excalidraw/utils.ts b/packages/excalidraw/utils.ts index 493dce340c..88a89a68bf 100644 --- a/packages/excalidraw/utils.ts +++ b/packages/excalidraw/utils.ts @@ -673,6 +673,8 @@ export const arrayToMapWithIndex = ( export const isTestEnv = () => import.meta.env.MODE === "test"; +export const isDevEnv = () => import.meta.env.MODE === "development"; + export const wrapEvent = (name: EVENT, nativeEvent: T) => { return new CustomEvent(name, { detail: {