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

This commit is contained in:
Aakansha Doshi 2022-09-01 16:18:52 +05:30
commit 8971d06655
22 changed files with 453 additions and 405 deletions

43
.codesandbox/tasks.json Normal file
View File

@ -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"] }
}
}
}

View File

@ -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!

View File

@ -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}

View File

@ -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) => {

View File

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

View File

@ -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}

View File

@ -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>
)}
</>
)}
</>
);

View File

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

View File

@ -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;

View File

@ -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,

View File

@ -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": "",

View File

@ -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": ""
},

View File

@ -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

View File

@ -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": "",

View File

@ -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",
},
},
};

View File

@ -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",
},
},
};

View File

@ -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

View File

@ -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**

View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -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);