Browse Source

Merge remote-tracking branch 'origin/master' into aakansha-hide-close-linear-element-points

Aakansha Doshi 3 years ago
parent
commit
8971d06655

+ 43 - 0
.codesandbox/tasks.json

@@ -0,0 +1,43 @@
+{
+  // These tasks will run in order when initializing your CodeSandbox project.
+  "setupTasks": [
+    {
+      "name": "Install Dependencies",
+      "command": "yarn install"
+    }
+  ],
+
+  // These tasks can be run from CodeSandbox. Running one will open a log in the app.
+  "tasks": {
+    "build": {
+      "name": "Build",
+      "command": "yarn build",
+      "runAtStart": false
+    },
+    "fix": {
+      "name": "Fix",
+      "command": "yarn fix",
+      "runAtStart": false
+    },
+    "prettier": {
+      "name": "Prettify",
+      "command": "yarn prettier",
+      "runAtStart": false
+    },
+    "start": {
+      "name": "Start Excalidraw",
+      "command": "yarn start",
+      "runAtStart": true
+    },
+    "test": {
+      "name": "Run Tests",
+      "command": "yarn test",
+      "runAtStart": false
+    },
+    "install-deps": {
+      "name": "Install Dependencies",
+      "command": "yarn install",
+      "restartOn": { "files": ["yarn.lock"] }
+    }
+  }
+}

+ 1 - 1
README.md

@@ -88,7 +88,7 @@ Try out [`@excalidraw/excalidraw`](https://www.npmjs.com/package/@excalidraw/exc
 
 ### Code Sandbox
 
-- Go to https://codesandbox.io/s/github/excalidraw/excalidraw
+- Go to https://codesandbox.io/p/github/excalidraw/excalidraw
   - You may need to sign in with GitHub and reload the page
 - You can start coding instantly, and even send PRs from there!
 

+ 1 - 1
src/actions/actionExport.tsx

@@ -244,7 +244,7 @@ export const actionLoadScene = register({
     }
   },
   keyTest: (event) => event[KEYS.CTRL_OR_CMD] && event.key === KEYS.O,
-  PanelComponent: ({ updateData, appState }) => (
+  PanelComponent: ({ updateData }) => (
     <ToolButton
       type="button"
       icon={load}

+ 1 - 0
src/actions/manager.tsx

@@ -147,6 +147,7 @@ export class ActionManager {
     ) {
       const action = this.actions[name];
       const PanelComponent = action.PanelComponent!;
+      PanelComponent.displayName = "PanelComponent";
       const elements = this.getElementsIncludingDeleted();
       const appState = this.getAppState();
       const updateData = (formState?: any) => {

+ 2 - 0
src/components/ColorPicker.tsx

@@ -343,6 +343,8 @@ const ColorInput = React.forwardRef(
   },
 );
 
+ColorInput.displayName = "ColorInput";
+
 export const ColorPicker = ({
   type,
   color,

+ 7 - 0
src/components/Footer.tsx

@@ -88,6 +88,13 @@ const Footer = ({
       >
         {renderCustomFooter?.(false, appState)}
       </div>
+      <div
+        className={clsx("layer-ui__wrapper__footer-right zen-mode-transition", {
+          "transition-right disable-pointerEvents": appState.zenModeEnabled,
+        })}
+      >
+        {actionManager.renderAction("toggleShortcuts")}
+      </div>
       <ExitZenModeAction
         executeAction={actionManager.executeAction}
         showExitZenModeBtn={showExitZenModeBtn}

+ 71 - 74
src/components/LayerUI.tsx

@@ -381,7 +381,7 @@ const LayerUI = ({
     );
   };
 
-  const dialogs = (
+  return (
     <>
       {appState.isLoading && <LoadingMessage delay={250} />}
       {appState.errorMessage && (
@@ -409,84 +409,81 @@ const LayerUI = ({
           }
         />
       )}
-    </>
-  );
-
-  return device.isMobile ? (
-    <>
-      {dialogs}
-      <MobileMenu
-        appState={appState}
-        elements={elements}
-        actionManager={actionManager}
-        libraryMenu={libraryMenu}
-        renderJSONExportDialog={renderJSONExportDialog}
-        renderImageExportDialog={renderImageExportDialog}
-        setAppState={setAppState}
-        onCollabButtonClick={onCollabButtonClick}
-        onLockToggle={() => onLockToggle()}
-        onPenModeToggle={onPenModeToggle}
-        canvas={canvas}
-        isCollaborating={isCollaborating}
-        renderCustomFooter={renderCustomFooter}
-        showThemeBtn={showThemeBtn}
-        onImageAction={onImageAction}
-        renderTopRightUI={renderTopRightUI}
-        renderCustomStats={renderCustomStats}
-      />
-    </>
-  ) : (
-    <>
-      <div
-        className={clsx("layer-ui__wrapper", {
-          "disable-pointerEvents":
-            appState.draggingElement ||
-            appState.resizingElement ||
-            (appState.editingElement &&
-              !isTextElement(appState.editingElement)),
-        })}
-        style={
-          appState.isLibraryOpen &&
-          appState.isLibraryMenuDocked &&
-          device.canDeviceFitSidebar
-            ? { width: `calc(100% - ${LIBRARY_SIDEBAR_WIDTH}px)` }
-            : {}
-        }
-      >
-        {dialogs}
-        {renderFixedSideContainer()}
-        <Footer
+      {device.isMobile && (
+        <MobileMenu
           appState={appState}
+          elements={elements}
           actionManager={actionManager}
+          libraryMenu={libraryMenu}
+          renderJSONExportDialog={renderJSONExportDialog}
+          renderImageExportDialog={renderImageExportDialog}
+          setAppState={setAppState}
+          onCollabButtonClick={onCollabButtonClick}
+          onLockToggle={() => onLockToggle()}
+          onPenModeToggle={onPenModeToggle}
+          canvas={canvas}
+          isCollaborating={isCollaborating}
           renderCustomFooter={renderCustomFooter}
-          showExitZenModeBtn={showExitZenModeBtn}
+          showThemeBtn={showThemeBtn}
+          onImageAction={onImageAction}
+          renderTopRightUI={renderTopRightUI}
+          renderCustomStats={renderCustomStats}
         />
-        {appState.showStats && (
-          <Stats
-            appState={appState}
-            setAppState={setAppState}
-            elements={elements}
-            onClose={() => {
-              actionManager.executeAction(actionToggleStats);
-            }}
-            renderCustomStats={renderCustomStats}
-          />
-        )}
-        {appState.scrolledOutside && (
-          <button
-            className="scroll-back-to-content"
-            onClick={() => {
-              setAppState({
-                ...calculateScrollCenter(elements, appState, canvas),
-              });
-            }}
+      )}
+
+      {!device.isMobile && (
+        <>
+          <div
+            className={clsx("layer-ui__wrapper", {
+              "disable-pointerEvents":
+                appState.draggingElement ||
+                appState.resizingElement ||
+                (appState.editingElement &&
+                  !isTextElement(appState.editingElement)),
+            })}
+            style={
+              appState.isLibraryOpen &&
+              appState.isLibraryMenuDocked &&
+              device.canDeviceFitSidebar
+                ? { width: `calc(100% - ${LIBRARY_SIDEBAR_WIDTH}px)` }
+                : {}
+            }
           >
-            {t("buttons.scrollBackToContent")}
-          </button>
-        )}
-      </div>
-      {appState.isLibraryOpen && (
-        <div className="layer-ui__sidebar">{libraryMenu}</div>
+            {renderFixedSideContainer()}
+            <Footer
+              appState={appState}
+              actionManager={actionManager}
+              renderCustomFooter={renderCustomFooter}
+              showExitZenModeBtn={showExitZenModeBtn}
+            />
+            {appState.showStats && (
+              <Stats
+                appState={appState}
+                setAppState={setAppState}
+                elements={elements}
+                onClose={() => {
+                  actionManager.executeAction(actionToggleStats);
+                }}
+                renderCustomStats={renderCustomStats}
+              />
+            )}
+            {appState.scrolledOutside && (
+              <button
+                className="scroll-back-to-content"
+                onClick={() => {
+                  setAppState({
+                    ...calculateScrollCenter(elements, appState, canvas),
+                  });
+                }}
+              >
+                {t("buttons.scrollBackToContent")}
+              </button>
+            )}
+          </div>
+          {appState.isLibraryOpen && (
+            <div className="layer-ui__sidebar">{libraryMenu}</div>
+          )}
+        </>
       )}
     </>
   );

+ 2 - 0
src/components/ToolButton.tsx

@@ -187,3 +187,5 @@ ToolButton.defaultProps = {
   className: "",
   size: "medium",
 };
+
+ToolButton.displayName = "ToolButton";

+ 13 - 21
src/element/bounds.ts

@@ -107,12 +107,19 @@ const solveQuadratic = (
     return false;
   }
 
-  const t1 = (-b + Math.sqrt(sqrtPart)) / (2 * a);
-  const t2 = (-b - Math.sqrt(sqrtPart)) / (2 * a);
-
   let s1 = null;
   let s2 = null;
 
+  let t1 = Infinity;
+  let t2 = Infinity;
+
+  if (a === 0) {
+    t1 = t2 = -c / b;
+  } else {
+    t1 = (-b + Math.sqrt(sqrtPart)) / (2 * a);
+    t2 = (-b - Math.sqrt(sqrtPart)) / (2 * a);
+  }
+
   if (t1 >= 0 && t1 <= 1) {
     s1 = getBezierValueForT(t1, p0, p1, p2, p3);
   }
@@ -152,11 +159,6 @@ const getCubicBezierCurveBound = (
   return [minX, minY, maxX, maxY];
 };
 
-// TODO: https://github.com/excalidraw/excalidraw/issues/5617
-const getRandomOffset = () => {
-  return Math.random() / 1000000;
-};
-
 const getMinMaxXYFromCurvePathOps = (
   ops: Op[],
   transformXY?: (x: number, y: number) => [number, number],
@@ -173,19 +175,9 @@ const getMinMaxXYFromCurvePathOps = (
         // move operation does not draw anything; so, it always
         // returns false
       } else if (op === "bcurveTo") {
-        // random offset is needed to fix https://github.com/excalidraw/excalidraw/issues/5585
-        const _p1 = [
-          data[0] + getRandomOffset(),
-          data[1] + getRandomOffset(),
-        ] as Point;
-        const _p2 = [
-          data[2] + getRandomOffset(),
-          data[3] + getRandomOffset(),
-        ] as Point;
-        const _p3 = [
-          data[4] + getRandomOffset(),
-          data[5] + getRandomOffset(),
-        ] as Point;
+        const _p1 = [data[0], data[1]] as Point;
+        const _p2 = [data[2], data[3]] as Point;
+        const _p3 = [data[4], data[5]] as Point;
 
         const p1 = transformXY ? transformXY(..._p1) : _p1;
         const p2 = transformXY ? transformXY(..._p2) : _p2;

+ 7 - 1
src/excalidraw-app/index.tsx

@@ -194,7 +194,13 @@ const initializeScene = async (opts: {
       scene: {
         ...scene,
         appState: {
-          ...restoreAppState(scene?.appState, excalidrawAPI.getAppState()),
+          ...restoreAppState(
+            {
+              ...scene?.appState,
+              theme: localDataState?.appState?.theme || scene?.appState?.theme,
+            },
+            excalidrawAPI.getAppState(),
+          ),
           // necessary if we're invoking from a hashchange handler which doesn't
           // go through App.initializeScene() that resets this flag
           isLoading: false,

+ 50 - 50
src/locales/bn-BD.json

@@ -1,55 +1,55 @@
 {
   "labels": {
-    "paste": "",
-    "pasteCharts": "",
-    "selectAll": "",
-    "multiSelect": "",
-    "moveCanvas": "",
-    "cut": "",
-    "copy": "",
-    "copyAsPng": "",
-    "copyAsSvg": "",
-    "copyText": "",
-    "bringForward": "",
-    "sendToBack": "",
-    "bringToFront": "",
-    "sendBackward": "",
-    "delete": "",
-    "copyStyles": "",
-    "pasteStyles": "",
-    "stroke": "",
-    "background": "",
-    "fill": "",
-    "strokeWidth": "",
-    "strokeStyle": "",
-    "strokeStyle_solid": "",
-    "strokeStyle_dashed": "",
-    "strokeStyle_dotted": "",
-    "sloppiness": "",
-    "opacity": "",
-    "textAlign": "",
-    "edges": "",
-    "sharp": "",
-    "round": "",
-    "arrowheads": "",
-    "arrowhead_none": "",
-    "arrowhead_arrow": "",
-    "arrowhead_bar": "",
-    "arrowhead_dot": "",
-    "arrowhead_triangle": "",
-    "fontSize": "",
-    "fontFamily": "",
-    "onlySelected": "",
-    "withBackground": "",
-    "exportEmbedScene": "",
-    "exportEmbedScene_details": "",
+    "paste": "পেস্ট করুন",
+    "pasteCharts": "চার্টগুলো পেস্ট করুন",
+    "selectAll": "সব সিলেক্ট করুন",
+    "multiSelect": "সিলেকশনে এলিমেন্ট এ্যাড করুন",
+    "moveCanvas": "ক্যানভাস মুভ করুন",
+    "cut": "কাট করুন",
+    "copy": "কপি করুন",
+    "copyAsPng": "PNG হিসেবে ক্লিপবোর্ডে কপি করুন",
+    "copyAsSvg": "SVG হিসেবে ক্লিপবোর্ডে কপি করুন",
+    "copyText": "টেক্সট হিসেবে ক্লিপবোর্ডে কপি করুন",
+    "bringForward": "সামনে আনুন",
+    "sendToBack": "একদম পেছনে পাঠান",
+    "bringToFront": "একদম সামনে আনুন",
+    "sendBackward": "পেছনে পাঠান",
+    "delete": "ডিলিট করুন",
+    "copyStyles": "স্টাইলগুলো কপি করুন",
+    "pasteStyles": "স্টাইলগুলো পেস্ট করুন",
+    "stroke": "স্ট্রোক",
+    "background": "ব্যাকগ্রাউন্ড",
+    "fill": "ফিল",
+    "strokeWidth": "স্ট্রোকের পুরুত্ব",
+    "strokeStyle": "স্ট্রোকের স্টাইল",
+    "strokeStyle_solid": "সলিড",
+    "strokeStyle_dashed": "কাটা-কাটা",
+    "strokeStyle_dotted": "ফোটা-ফোটা",
+    "sloppiness": "স্ট্রোকের ধরণ",
+    "opacity": "অস্বচ্ছতা",
+    "textAlign": "লেখার দিক",
+    "edges": "কোণা",
+    "sharp": "তীক্ষ্ণ",
+    "round": "গোলাকার",
+    "arrowheads": "তীরের মাথা",
+    "arrowhead_none": "কিছু না",
+    "arrowhead_arrow": "তীর",
+    "arrowhead_bar": "বার",
+    "arrowhead_dot": "ডট",
+    "arrowhead_triangle": "ত্রিভুজ",
+    "fontSize": "ফন্ট সাইজ",
+    "fontFamily": "ফন্ট ফ্যামিলি",
+    "onlySelected": "শুধুমাত্র সিলেক্টেডগুলো",
+    "withBackground": "ব্যাকগ্রাউন্ড",
+    "exportEmbedScene": "সিন এম্বেড করুন",
+    "exportEmbedScene_details": "সিনের ডেটা এক্সপোর্টকৃত PNG/SVG ফাইলের মধ্যে সেভ করা হবে যাতে করে পরবর্তী সময়ে আপনি এডিট করতে পারেন । তবে এতে ফাইলের সাইজ বাড়বে ।.",
     "addWatermark": "",
-    "handDrawn": "",
-    "normal": "",
-    "code": "",
-    "small": "",
-    "medium": "",
-    "large": "",
+    "handDrawn": "হাতে আঁকা",
+    "normal": "স্বাভাবিক",
+    "code": "কোড",
+    "small": "ছোট",
+    "medium": "মধ্যবর্তী",
+    "large": "বড়",
     "veryLarge": "",
     "solid": "",
     "hachure": "",
@@ -99,7 +99,7 @@
     "flipVertical": "",
     "viewMode": "",
     "toggleExportColorScheme": "",
-    "share": "",
+    "share": "শেয়ার করুন",
     "showStroke": "",
     "showBackground": "",
     "toggleTheme": "",

+ 5 - 5
src/locales/lt-LT.json

@@ -110,13 +110,13 @@
     "unbindText": "",
     "bindText": "",
     "link": {
-      "edit": "",
-      "create": "",
-      "label": ""
+      "edit": "Redeguoti nuorodą",
+      "create": "Sukurti nuorodą",
+      "label": "Nuoroda"
     },
     "elementLock": {
-      "lock": "",
-      "unlock": "",
+      "lock": "Užrakinti",
+      "unlock": "Atrakinti",
       "lockAll": "",
       "unlockAll": ""
     },

+ 3 - 3
src/locales/percentages.json

@@ -1,7 +1,7 @@
 {
   "ar-SA": 91,
   "bg-BG": 58,
-  "bn-BD": 0,
+  "bn-BD": 13,
   "ca-ES": 99,
   "cs-CZ": 27,
   "da-DK": 34,
@@ -23,7 +23,7 @@
   "kab-KAB": 95,
   "kk-KZ": 22,
   "ko-KR": 99,
-  "lt-LT": 22,
+  "lt-LT": 24,
   "lv-LV": 100,
   "mr-IN": 100,
   "my-MM": 44,
@@ -44,7 +44,7 @@
   "ta-IN": 98,
   "tr-TR": 99,
   "uk-UA": 100,
-  "vi-VN": 13,
+  "vi-VN": 16,
   "zh-CN": 100,
   "zh-HK": 27,
   "zh-TW": 100

+ 11 - 11
src/locales/vi-VN.json

@@ -1,10 +1,10 @@
 {
   "labels": {
     "paste": "Dán",
-    "pasteCharts": "",
+    "pasteCharts": "Dán biểu đồ",
     "selectAll": "Chọn tất cả",
-    "multiSelect": "",
-    "moveCanvas": "",
+    "multiSelect": "Thêm mới vào Select",
+    "moveCanvas": "Di chuyển Canvas",
     "cut": "Cắt",
     "copy": "Sao chép",
     "copyAsPng": "Sao chép vào bộ nhớ tạm dưới dạng PNG",
@@ -43,22 +43,22 @@
     "withBackground": "Nền",
     "exportEmbedScene": "",
     "exportEmbedScene_details": "",
-    "addWatermark": "",
+    "addWatermark": "Làm với Excalidraw\"",
     "handDrawn": "",
     "normal": "Bình thường",
     "code": "Mã",
     "small": "Nhỏ",
     "medium": "Vừa",
     "large": "Lớn",
-    "veryLarge": "",
-    "solid": "",
+    "veryLarge": "Rất lớn",
+    "solid": "Đặc",
     "hachure": "",
     "crossHatch": "",
-    "thin": "",
-    "bold": "",
-    "left": "",
-    "center": "",
-    "right": "",
+    "thin": "Mỏng",
+    "bold": "In đậm",
+    "left": "Trái",
+    "center": "Giữa",
+    "right": "Phải",
     "extraBold": "",
     "architect": "",
     "artist": "",

+ 88 - 0
src/packages/common.webpack.dev.config.js

@@ -0,0 +1,88 @@
+const path = require("path");
+const autoprefixer = require("autoprefixer");
+const webpack = require("webpack");
+const { parseEnvVariables } = require(path.resolve(global.__childdir, "./env"));
+
+module.exports = {
+  mode: "development",
+  devtool: false,
+  output: {
+    libraryTarget: "umd",
+    filename: "[name].js",
+    publicPath: "",
+  },
+  resolve: {
+    extensions: [".js", ".ts", ".tsx", ".css", ".scss"],
+  },
+  module: {
+    rules: [
+      {
+        test: /\.(sa|sc|c)ss$/,
+        exclude: /node_modules/,
+        use: [
+          "style-loader",
+          { loader: "css-loader" },
+          {
+            loader: "postcss-loader",
+            options: {
+              postcssOptions: {
+                plugins: [autoprefixer()],
+              },
+            },
+          },
+          "sass-loader",
+        ],
+      },
+      {
+        test: /\.(ts|tsx|js|jsx|mjs)$/,
+        exclude: /node_modules\/(?!browser-fs-access)/,
+        use: [
+          {
+            loader: "ts-loader",
+            options: {
+              transpileOnly: true,
+              configFile: path.resolve(__dirname, "./tsconfig.dev.json"),
+            },
+          },
+        ],
+      },
+      {
+        test: /\.(woff|woff2|eot|ttf|otf)$/,
+        type: "asset/resource",
+      },
+    ],
+  },
+  optimization: {
+    splitChunks: {
+      chunks: "async",
+      cacheGroups: {
+        vendors: {
+          test: /[\\/]node_modules[\\/]/,
+          name: "vendor",
+        },
+      },
+    },
+  },
+  plugins: [
+    new webpack.EvalSourceMapDevToolPlugin({ exclude: /vendor/ }),
+    new webpack.DefinePlugin({
+      "process.env": parseEnvVariables(
+        path.resolve(__dirname, "../../.env.development"),
+      ),
+    }),
+  ],
+  externals: {
+    react: {
+      root: "React",
+      commonjs2: "react",
+      commonjs: "react",
+      amd: "react",
+    },
+    "react-dom": {
+      root: "ReactDOM",
+      commonjs2: "react-dom",
+      commonjs: "react-dom",
+      amd: "react-dom",
+    },
+  },
+};

+ 119 - 0
src/packages/common.webpack.prod.config.js

@@ -0,0 +1,119 @@
+const path = require("path");
+const autoprefixer = require("autoprefixer");
+const webpack = require("webpack");
+const BundleAnalyzerPlugin = require(path.resolve(
+  path.join(global.__childdir, "node_modules"),
+  "webpack-bundle-analyzer",
+)).BundleAnalyzerPlugin;
+const TerserPlugin = require("terser-webpack-plugin");
+const { parseEnvVariables } =
+  "__noenv" in global ? {} : require(path.resolve(global.__childdir, "./env"));
+
+module.exports = {
+  mode: "production",
+  output: {
+    libraryTarget: "umd",
+    filename: "[name].js",
+    publicPath: "",
+  },
+  resolve: {
+    extensions: [".js", ".ts", ".tsx", ".css", ".scss"],
+  },
+  module: {
+    rules: [
+      {
+        test: /\.(sa|sc|c)ss$/,
+        exclude: /node_modules/,
+        use: [
+          "style-loader",
+          {
+            loader: "css-loader",
+          },
+          {
+            loader: "postcss-loader",
+            options: {
+              postcssOptions: {
+                plugins: [autoprefixer()],
+              },
+            },
+          },
+          "sass-loader",
+        ],
+      },
+      {
+        test: /\.(ts|tsx|js|jsx|mjs)$/,
+        exclude: /node_modules\/(?!browser-fs-access)/,
+        use: [
+          {
+            loader: "ts-loader",
+            options: {
+              transpileOnly: true,
+              configFile: path.resolve(__dirname, "./tsconfig.prod.json"),
+            },
+          },
+          {
+            loader: "babel-loader",
+            options: {
+              presets: [
+                "@babel/preset-env",
+                ["@babel/preset-react", { runtime: "automatic" }],
+                "@babel/preset-typescript",
+              ],
+              plugins: [
+                "transform-class-properties",
+                "@babel/plugin-transform-runtime",
+              ],
+            },
+          },
+        ],
+      },
+      {
+        test: /\.(woff|woff2|eot|ttf|otf)$/,
+        type: "asset/resource",
+      },
+    ],
+  },
+  optimization: {
+    minimize: true,
+    minimizer: [
+      new TerserPlugin({
+        test: /\.js($|\?)/i,
+      }),
+    ],
+    splitChunks: {
+      chunks: "async",
+      cacheGroups: {
+        vendors: {
+          test: /[\\/]node_modules[\\/]/,
+          name: "vendor",
+        },
+      },
+    },
+  },
+  plugins: [
+    ...(process.env.ANALYZER === "true" ? [new BundleAnalyzerPlugin()] : []),
+    ...("__noenv" in global
+      ? []
+      : [
+          new webpack.DefinePlugin({
+            "process.env": parseEnvVariables(
+              path.resolve(__dirname, "../../.env.production"),
+            ),
+          }),
+        ]),
+  ],
+  externals: {
+    react: {
+      root: "React",
+      commonjs2: "react",
+      commonjs: "react",
+      amd: "react",
+    },
+    "react-dom": {
+      root: "ReactDOM",
+      commonjs2: "react-dom",
+      commonjs: "react-dom",
+      amd: "react-dom",
+    },
+  },
+};

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

@@ -18,6 +18,7 @@ Please add the latest change on the top under the correct section.
 #### Features
 
 - Added support for storing [`customData`](https://github.com/excalidraw/excalidraw/blob/master/src/packages/excalidraw/README.md#storing-custom-data-to-excalidraw-elements) on Excalidraw elements [#5592].
+- Added `exportPadding?: number;` to [exportToCanvas](https://github.com/excalidraw/excalidraw/blob/master/src/packages/excalidraw/README.md#exporttocanvas) and [exportToBlob](https://github.com/excalidraw/excalidraw/blob/master/src/packages/excalidraw/README.md#exporttoblob). The default value of the padding is 10.
 
 #### Breaking Changes
 

+ 6 - 2
src/packages/excalidraw/README.md

@@ -929,7 +929,8 @@ This function normalizes library items elements, adding missing values when need
   elements,
   appState
   getDimensions,
-  files
+  files,
+  exportPadding?: number;
 }: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/packages/utils.ts#L12">ExportOpts</a>
 </pre>
 
@@ -940,6 +941,7 @@ This function normalizes library items elements, adding missing values when need
 | getDimensions | `(width: number, height: number) => { width: number, height: number, scale?: number }` | undefined | A function which returns the `width`, `height`, and optionally `scale` (defaults `1`), with which canvas is to be exported. |
 | maxWidthOrHeight | `number` | undefined | The maximum width or height of the exported image. If provided, `getDimensions` is ignored. |
 | files | [BinaryFiles](The [`BinaryFiles`](<[BinaryFiles](https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L64)>) | undefined | The files added to the scene. |
+| exportPadding | number | 10 | The padding to be added on canvas |
 
 **How to use**
 
@@ -957,7 +959,8 @@ This function returns the canvas with the exported elements, appState and dimens
 exportToBlob(
   opts: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/packages/utils.ts#L14">ExportOpts</a> & {
   mimeType?: string,
-  quality?: number;
+  quality?: number,
+  exportPadding?: number;
 })
 </pre>
 
@@ -966,6 +969,7 @@ exportToBlob(
 | opts |  |  | This param is passed to `exportToCanvas`. You can refer to [`exportToCanvas`](#exportToCanvas) |
 | mimeType | string | "image/png" | Indicates the image format |
 | quality | number | 0.92 | A value between 0 and 1 indicating the [image quality](https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlob#parameters). Applies only to `image/jpeg`/`image/webp` MIME types. |
+| exportPadding | number | 10 | The padding to be added on canvas |
 
 **How to use**
 

+ 5 - 84
src/packages/excalidraw/webpack.dev.config.js

@@ -1,97 +1,18 @@
+global.__childdir = __dirname;
 const path = require("path");
-const webpack = require("webpack");
-const autoprefixer = require("autoprefixer");
-const { parseEnvVariables } = require("./env");
+const { merge } = require("webpack-merge");
+const commonConfig = require("../common.webpack.dev.config");
 
 const outputDir = process.env.EXAMPLE === "true" ? "example/public" : "dist";
-module.exports = {
-  mode: "development",
-  devtool: false,
+const config = {
   entry: {
     "excalidraw.development": "./entry.js",
   },
   output: {
     path: path.resolve(__dirname, outputDir),
     library: "ExcalidrawLib",
-    libraryTarget: "umd",
-    filename: "[name].js",
     chunkFilename: "excalidraw-assets-dev/[name]-[contenthash].js",
     assetModuleFilename: "excalidraw-assets-dev/[name][ext]",
-
-    publicPath: "",
-  },
-  resolve: {
-    extensions: [".js", ".ts", ".tsx", ".css", ".scss"],
-  },
-  module: {
-    rules: [
-      {
-        test: /\.(sa|sc|c)ss$/,
-        exclude: /node_modules/,
-        use: [
-          "style-loader",
-          { loader: "css-loader" },
-          {
-            loader: "postcss-loader",
-            options: {
-              postcssOptions: {
-                plugins: [autoprefixer()],
-              },
-            },
-          },
-          "sass-loader",
-        ],
-      },
-      {
-        test: /\.(ts|tsx|js|jsx|mjs)$/,
-        exclude: /node_modules\/(?!browser-fs-access)/,
-        use: [
-          {
-            loader: "ts-loader",
-            options: {
-              transpileOnly: true,
-              configFile: path.resolve(__dirname, "../tsconfig.dev.json"),
-            },
-          },
-        ],
-      },
-      {
-        test: /\.(woff|woff2|eot|ttf|otf)$/,
-        type: "asset/resource",
-      },
-    ],
-  },
-  optimization: {
-    splitChunks: {
-      chunks: "async",
-      cacheGroups: {
-        vendors: {
-          test: /[\\/]node_modules[\\/]/,
-          name: "vendor",
-        },
-      },
-    },
-  },
-  plugins: [
-    new webpack.EvalSourceMapDevToolPlugin({ exclude: /vendor/ }),
-    new webpack.DefinePlugin({
-      "process.env": parseEnvVariables(
-        path.resolve(__dirname, "../../../.env.development"),
-      ),
-    }),
-  ],
-  externals: {
-    react: {
-      root: "React",
-      commonjs2: "react",
-      commonjs: "react",
-      amd: "react",
-    },
-    "react-dom": {
-      root: "ReactDOM",
-      commonjs2: "react-dom",
-      commonjs: "react-dom",
-      amd: "react-dom",
-    },
   },
 };
+module.exports = merge(commonConfig, config);

+ 5 - 107
src/packages/excalidraw/webpack.prod.config.js

@@ -1,119 +1,17 @@
+global.__childdir = __dirname;
 const path = require("path");
-const TerserPlugin = require("terser-webpack-plugin");
-const BundleAnalyzerPlugin =
-  require("webpack-bundle-analyzer").BundleAnalyzerPlugin;
-const autoprefixer = require("autoprefixer");
-const webpack = require("webpack");
-const { parseEnvVariables } = require("./env");
+const { merge } = require("webpack-merge");
+const commonConfig = require("../common.webpack.prod.config");
 
-module.exports = {
-  mode: "production",
+const config = {
   entry: {
     "excalidraw.production.min": "./entry.js",
   },
   output: {
     path: path.resolve(__dirname, "dist"),
     library: "ExcalidrawLib",
-    libraryTarget: "umd",
-    filename: "[name].js",
     chunkFilename: "excalidraw-assets/[name]-[contenthash].js",
     assetModuleFilename: "excalidraw-assets/[name][ext]",
-    publicPath: "",
-  },
-  resolve: {
-    extensions: [".js", ".ts", ".tsx", ".css", ".scss"],
-  },
-  module: {
-    rules: [
-      {
-        test: /\.(sa|sc|c)ss$/,
-        exclude: /node_modules/,
-        use: [
-          "style-loader",
-          {
-            loader: "css-loader",
-          },
-          {
-            loader: "postcss-loader",
-            options: {
-              postcssOptions: {
-                plugins: [autoprefixer()],
-              },
-            },
-          },
-          "sass-loader",
-        ],
-      },
-      {
-        test: /\.(ts|tsx|js|jsx|mjs)$/,
-        exclude: /node_modules\/(?!browser-fs-access)/,
-        use: [
-          {
-            loader: "ts-loader",
-            options: {
-              transpileOnly: true,
-              configFile: path.resolve(__dirname, "../tsconfig.prod.json"),
-            },
-          },
-          {
-            loader: "babel-loader",
-            options: {
-              presets: [
-                "@babel/preset-env",
-                ["@babel/preset-react", { runtime: "automatic" }],
-                "@babel/preset-typescript",
-              ],
-              plugins: [
-                "transform-class-properties",
-                "@babel/plugin-transform-runtime",
-              ],
-            },
-          },
-        ],
-      },
-      {
-        test: /\.(woff|woff2|eot|ttf|otf)$/,
-        type: "asset/resource",
-      },
-    ],
-  },
-  optimization: {
-    minimize: true,
-    minimizer: [
-      new TerserPlugin({
-        test: /\.js($|\?)/i,
-      }),
-    ],
-    splitChunks: {
-      chunks: "async",
-      cacheGroups: {
-        vendors: {
-          test: /[\\/]node_modules[\\/]/,
-          name: "vendor",
-        },
-      },
-    },
-  },
-  plugins: [
-    ...(process.env.ANALYZER === "true" ? [new BundleAnalyzerPlugin()] : []),
-    new webpack.DefinePlugin({
-      "process.env": parseEnvVariables(
-        path.resolve(__dirname, "../../../.env.production"),
-      ),
-    }),
-  ],
-  externals: {
-    react: {
-      root: "React",
-      commonjs2: "react",
-      commonjs: "react",
-      amd: "react",
-    },
-    "react-dom": {
-      root: "ReactDOM",
-      commonjs2: "react-dom",
-      commonjs: "react-dom",
-      amd: "react-dom",
-    },
   },
 };
+module.exports = merge(commonConfig, config);

+ 6 - 2
src/packages/utils.ts

@@ -35,7 +35,10 @@ export const exportToCanvas = ({
   files,
   maxWidthOrHeight,
   getDimensions,
-}: ExportOpts) => {
+  exportPadding,
+}: ExportOpts & {
+  exportPadding?: number;
+}) => {
   const { elements: restoredElements, appState: restoredAppState } = restore(
     { elements, appState },
     null,
@@ -46,7 +49,7 @@ export const exportToCanvas = ({
     getNonDeletedElements(restoredElements),
     { ...restoredAppState, offsetTop: 0, offsetLeft: 0, width: 0, height: 0 },
     files || {},
-    { exportBackground, viewBackgroundColor },
+    { exportBackground, exportPadding, viewBackgroundColor },
     (width: number, height: number) => {
       const canvas = document.createElement("canvas");
 
@@ -87,6 +90,7 @@ export const exportToBlob = async (
   opts: ExportOpts & {
     mimeType?: string;
     quality?: number;
+    exportPadding?: number;
   },
 ): Promise<Blob> => {
   let { mimeType = MIME_TYPES.png, quality } = opts;

+ 6 - 43
src/packages/utils/webpack.prod.config.js

@@ -1,60 +1,23 @@
+global.__childdir = __dirname;
+global.__noenv = true;
 const webpack = require("webpack");
 const path = require("path");
-const BundleAnalyzerPlugin =
-  require("webpack-bundle-analyzer").BundleAnalyzerPlugin;
+const { merge } = require("webpack-merge");
+const commonConfig = require("../common.webpack.prod.config");
 
-module.exports = {
-  mode: "production",
+const config = {
   entry: { "excalidraw-utils.min": "./index.js" },
   output: {
     path: path.resolve(__dirname, "dist"),
-    filename: "[name].js",
     library: "ExcalidrawUtils",
-    libraryTarget: "umd",
-  },
-  resolve: {
-    extensions: [".tsx", ".ts", ".js", ".css", ".scss"],
   },
   optimization: {
     runtimeChunk: false,
   },
-  module: {
-    rules: [
-      {
-        test: /\.(sa|sc|c)ss$/,
-        exclude: /node_modules/,
-        use: ["style-loader", { loader: "css-loader" }, "sass-loader"],
-      },
-      {
-        test: /\.(ts|tsx|js)$/,
-        use: [
-          {
-            loader: "ts-loader",
-            options: {
-              transpileOnly: true,
-              configFile: path.resolve(__dirname, "../tsconfig.prod.json"),
-            },
-          },
-          {
-            loader: "babel-loader",
-
-            options: {
-              presets: [
-                "@babel/preset-env",
-                ["@babel/preset-react", { runtime: "automatic" }],
-                "@babel/preset-typescript",
-              ],
-              plugins: [["@babel/plugin-transform-runtime"]],
-            },
-          },
-        ],
-      },
-    ],
-  },
   plugins: [
     new webpack.optimize.LimitChunkCountPlugin({
       maxChunks: 1,
     }),
-    ...(process.env.ANALYZER === "true" ? [new BundleAnalyzerPlugin()] : []),
   ],
 };
+module.exports = merge(commonConfig, config);