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 ### 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 may need to sign in with GitHub and reload the page
- You can start coding instantly, and even send PRs from there! - 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, keyTest: (event) => event[KEYS.CTRL_OR_CMD] && event.key === KEYS.O,
PanelComponent: ({ updateData, appState }) => ( PanelComponent: ({ updateData }) => (
<ToolButton <ToolButton
type="button" type="button"
icon={load} icon={load}

View File

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

View File

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

View File

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

View File

@ -381,7 +381,7 @@ const LayerUI = ({
); );
}; };
const dialogs = ( return (
<> <>
{appState.isLoading && <LoadingMessage delay={250} />} {appState.isLoading && <LoadingMessage delay={250} />}
{appState.errorMessage && ( {appState.errorMessage && (
@ -409,12 +409,7 @@ const LayerUI = ({
} }
/> />
)} )}
</> {device.isMobile && (
);
return device.isMobile ? (
<>
{dialogs}
<MobileMenu <MobileMenu
appState={appState} appState={appState}
elements={elements} elements={elements}
@ -434,8 +429,9 @@ const LayerUI = ({
renderTopRightUI={renderTopRightUI} renderTopRightUI={renderTopRightUI}
renderCustomStats={renderCustomStats} renderCustomStats={renderCustomStats}
/> />
</> )}
) : (
{!device.isMobile && (
<> <>
<div <div
className={clsx("layer-ui__wrapper", { className={clsx("layer-ui__wrapper", {
@ -453,7 +449,6 @@ const LayerUI = ({
: {} : {}
} }
> >
{dialogs}
{renderFixedSideContainer()} {renderFixedSideContainer()}
<Footer <Footer
appState={appState} appState={appState}
@ -489,6 +484,8 @@ const LayerUI = ({
<div className="layer-ui__sidebar">{libraryMenu}</div> <div className="layer-ui__sidebar">{libraryMenu}</div>
)} )}
</> </>
)}
</>
); );
}; };

View File

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

View File

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

View File

@ -194,7 +194,13 @@ const initializeScene = async (opts: {
scene: { scene: {
...scene, ...scene,
appState: { 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 // necessary if we're invoking from a hashchange handler which doesn't
// go through App.initializeScene() that resets this flag // go through App.initializeScene() that resets this flag
isLoading: false, isLoading: false,

View File

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

View File

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

View File

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

View File

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

View File

@ -929,7 +929,8 @@ This function normalizes library items elements, adding missing values when need
elements, elements,
appState appState
getDimensions, getDimensions,
files files,
exportPadding?: number;
}: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/packages/utils.ts#L12">ExportOpts</a> }: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/packages/utils.ts#L12">ExportOpts</a>
</pre> </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. | | 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. | | 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. | | 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** **How to use**
@ -957,7 +959,8 @@ This function returns the canvas with the exported elements, appState and dimens
exportToBlob( exportToBlob(
opts: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/packages/utils.ts#L14">ExportOpts</a> & { opts: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/packages/utils.ts#L14">ExportOpts</a> & {
mimeType?: string, mimeType?: string,
quality?: number; quality?: number,
exportPadding?: number;
}) })
</pre> </pre>
@ -966,6 +969,7 @@ exportToBlob(
| opts | | | This param is passed to `exportToCanvas`. You can refer to [`exportToCanvas`](#exportToCanvas) | | opts | | | This param is passed to `exportToCanvas`. You can refer to [`exportToCanvas`](#exportToCanvas) |
| mimeType | string | "image/png" | Indicates the image format | | 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. | | 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** **How to use**

View File

@ -1,97 +1,18 @@
global.__childdir = __dirname;
const path = require("path"); const path = require("path");
const webpack = require("webpack"); const { merge } = require("webpack-merge");
const autoprefixer = require("autoprefixer"); const commonConfig = require("../common.webpack.dev.config");
const { parseEnvVariables } = require("./env");
const outputDir = process.env.EXAMPLE === "true" ? "example/public" : "dist"; const outputDir = process.env.EXAMPLE === "true" ? "example/public" : "dist";
module.exports = { const config = {
mode: "development",
devtool: false,
entry: { entry: {
"excalidraw.development": "./entry.js", "excalidraw.development": "./entry.js",
}, },
output: { output: {
path: path.resolve(__dirname, outputDir), path: path.resolve(__dirname, outputDir),
library: "ExcalidrawLib", library: "ExcalidrawLib",
libraryTarget: "umd",
filename: "[name].js",
chunkFilename: "excalidraw-assets-dev/[name]-[contenthash].js", chunkFilename: "excalidraw-assets-dev/[name]-[contenthash].js",
assetModuleFilename: "excalidraw-assets-dev/[name][ext]", 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 path = require("path");
const TerserPlugin = require("terser-webpack-plugin"); const { merge } = require("webpack-merge");
const BundleAnalyzerPlugin = const commonConfig = require("../common.webpack.prod.config");
require("webpack-bundle-analyzer").BundleAnalyzerPlugin;
const autoprefixer = require("autoprefixer");
const webpack = require("webpack");
const { parseEnvVariables } = require("./env");
module.exports = { const config = {
mode: "production",
entry: { entry: {
"excalidraw.production.min": "./entry.js", "excalidraw.production.min": "./entry.js",
}, },
output: { output: {
path: path.resolve(__dirname, "dist"), path: path.resolve(__dirname, "dist"),
library: "ExcalidrawLib", library: "ExcalidrawLib",
libraryTarget: "umd",
filename: "[name].js",
chunkFilename: "excalidraw-assets/[name]-[contenthash].js", chunkFilename: "excalidraw-assets/[name]-[contenthash].js",
assetModuleFilename: "excalidraw-assets/[name][ext]", 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, files,
maxWidthOrHeight, maxWidthOrHeight,
getDimensions, getDimensions,
}: ExportOpts) => { exportPadding,
}: ExportOpts & {
exportPadding?: number;
}) => {
const { elements: restoredElements, appState: restoredAppState } = restore( const { elements: restoredElements, appState: restoredAppState } = restore(
{ elements, appState }, { elements, appState },
null, null,
@ -46,7 +49,7 @@ export const exportToCanvas = ({
getNonDeletedElements(restoredElements), getNonDeletedElements(restoredElements),
{ ...restoredAppState, offsetTop: 0, offsetLeft: 0, width: 0, height: 0 }, { ...restoredAppState, offsetTop: 0, offsetLeft: 0, width: 0, height: 0 },
files || {}, files || {},
{ exportBackground, viewBackgroundColor }, { exportBackground, exportPadding, viewBackgroundColor },
(width: number, height: number) => { (width: number, height: number) => {
const canvas = document.createElement("canvas"); const canvas = document.createElement("canvas");
@ -87,6 +90,7 @@ export const exportToBlob = async (
opts: ExportOpts & { opts: ExportOpts & {
mimeType?: string; mimeType?: string;
quality?: number; quality?: number;
exportPadding?: number;
}, },
): Promise<Blob> => { ): Promise<Blob> => {
let { mimeType = MIME_TYPES.png, quality } = opts; let { mimeType = MIME_TYPES.png, quality } = opts;

View File

@ -1,60 +1,23 @@
global.__childdir = __dirname;
global.__noenv = true;
const webpack = require("webpack"); const webpack = require("webpack");
const path = require("path"); const path = require("path");
const BundleAnalyzerPlugin = const { merge } = require("webpack-merge");
require("webpack-bundle-analyzer").BundleAnalyzerPlugin; const commonConfig = require("../common.webpack.prod.config");
module.exports = { const config = {
mode: "production",
entry: { "excalidraw-utils.min": "./index.js" }, entry: { "excalidraw-utils.min": "./index.js" },
output: { output: {
path: path.resolve(__dirname, "dist"), path: path.resolve(__dirname, "dist"),
filename: "[name].js",
library: "ExcalidrawUtils", library: "ExcalidrawUtils",
libraryTarget: "umd",
},
resolve: {
extensions: [".tsx", ".ts", ".js", ".css", ".scss"],
}, },
optimization: { optimization: {
runtimeChunk: false, 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: [ plugins: [
new webpack.optimize.LimitChunkCountPlugin({ new webpack.optimize.LimitChunkCountPlugin({
maxChunks: 1, maxChunks: 1,
}), }),
...(process.env.ANALYZER === "true" ? [new BundleAnalyzerPlugin()] : []),
], ],
}; };
module.exports = merge(commonConfig, config);