build: Added example folder for testing @excalidraw/excalidraw in local (#4488)
* build: Added example folder for testing @excalidraw/excalidraw in local * remove unnecessary files * use scss * update docs * newline * remove index * remove yarn * use the bundled excalidraw.development.js for better testing and font will also be available * remove src folder from example
This commit is contained in:
parent
f463c047c0
commit
04f852a40a
@ -70,6 +70,8 @@ Please add the latest change on the top under the correct section.
|
||||
|
||||
### Build
|
||||
|
||||
- Added an example to test and develop the package [locally](https://github.com/excalidraw/excalidraw/blob/master/src/packages/excalidraw/README.md#Development) using `yarn start`
|
||||
|
||||
- Remove `file-loader` so font assets are not duplicated by webpack and use webpack asset modules for font generation [#4380](https://github.com/excalidraw/excalidraw/pull/4380)
|
||||
|
||||
- We're now compiling to `es2017` target. Notably, `async/await` is not compiled down to generators. [#4341](https://github.com/excalidraw/excalidraw/pull/4341)
|
||||
|
@ -1008,3 +1008,21 @@ Defaults to `THEME.LIGHT` unless passed in `initialData.appState.theme`
|
||||
## Need help?
|
||||
|
||||
Check out the existing [Q&A](https://github.com/excalidraw/excalidraw/discussions?discussions_q=label%3Apackage%3Aexcalidraw). If you have any queries or need help, ask us [here](https://github.com/excalidraw/excalidraw/discussions?discussions_q=label%3Apackage%3Aexcalidraw).
|
||||
|
||||
### Development
|
||||
|
||||
#### Install the dependencies
|
||||
|
||||
```bash
|
||||
yarn
|
||||
```
|
||||
|
||||
#### Start the server
|
||||
|
||||
```bash
|
||||
yarn start
|
||||
```
|
||||
|
||||
[http://localhost:3001](http://localhost:3001) will open in your default browser.
|
||||
|
||||
The example is same as the [codesandbox example](https://ehlz3.csb.app/)
|
||||
|
249
src/packages/excalidraw/example/App.js
Normal file
249
src/packages/excalidraw/example/App.js
Normal file
@ -0,0 +1,249 @@
|
||||
import { useEffect, useState, useRef } from "react";
|
||||
|
||||
import InitialData from "./initialData";
|
||||
import Sidebar from "./sidebar/Sidebar";
|
||||
|
||||
import "./App.scss";
|
||||
import initialData from "./initialData";
|
||||
|
||||
// This is so that we use the bundled excalidraw.developement.js file instead
|
||||
// of the actual source code
|
||||
const { exportToCanvas, exportToSvg, exportToBlob } = window.Excalidraw;
|
||||
const Excalidraw = window.Excalidraw.default;
|
||||
|
||||
const renderTopRightUI = () => {
|
||||
return (
|
||||
<button onClick={() => alert("This is dummy top right UI")}>
|
||||
{" "}
|
||||
Click me{" "}
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
const renderFooter = () => {
|
||||
return (
|
||||
<button onClick={() => alert("This is dummy footer")}>
|
||||
{" "}
|
||||
custom footer{" "}
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
export default function App() {
|
||||
const excalidrawRef = useRef(null);
|
||||
|
||||
const [viewModeEnabled, setViewModeEnabled] = useState(false);
|
||||
const [zenModeEnabled, setZenModeEnabled] = useState(false);
|
||||
const [gridModeEnabled, setGridModeEnabled] = useState(false);
|
||||
const [blobUrl, setBlobUrl] = useState(null);
|
||||
const [canvasUrl, setCanvasUrl] = useState(null);
|
||||
const [exportWithDarkMode, setExportWithDarkMode] = useState(false);
|
||||
const [shouldAddWatermark, setShouldAddWatermark] = useState(false);
|
||||
const [theme, setTheme] = useState("light");
|
||||
|
||||
useEffect(() => {
|
||||
const onHashChange = () => {
|
||||
const hash = new URLSearchParams(window.location.hash.slice(1));
|
||||
const libraryUrl = hash.get("addLibrary");
|
||||
if (libraryUrl) {
|
||||
excalidrawRef.current.importLibrary(libraryUrl, hash.get("token"));
|
||||
}
|
||||
};
|
||||
window.addEventListener("hashchange", onHashChange, false);
|
||||
return () => {
|
||||
window.removeEventListener("hashchange", onHashChange);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const updateScene = () => {
|
||||
const sceneData = {
|
||||
elements: [
|
||||
{
|
||||
type: "rectangle",
|
||||
version: 141,
|
||||
versionNonce: 361174001,
|
||||
isDeleted: false,
|
||||
id: "oDVXy8D6rom3H1-LLH2-f",
|
||||
fillStyle: "hachure",
|
||||
strokeWidth: 1,
|
||||
strokeStyle: "solid",
|
||||
roughness: 1,
|
||||
opacity: 100,
|
||||
angle: 0,
|
||||
x: 100.50390625,
|
||||
y: 93.67578125,
|
||||
strokeColor: "#c92a2a",
|
||||
backgroundColor: "transparent",
|
||||
width: 186.47265625,
|
||||
height: 141.9765625,
|
||||
seed: 1968410350,
|
||||
groupIds: [],
|
||||
},
|
||||
],
|
||||
appState: {
|
||||
viewBackgroundColor: "#edf2ff",
|
||||
},
|
||||
};
|
||||
excalidrawRef.current.updateScene(sceneData);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="App">
|
||||
<h1> Excalidraw Example</h1>
|
||||
<Sidebar>
|
||||
<div className="button-wrapper">
|
||||
<button className="update-scene" onClick={updateScene}>
|
||||
Update Scene
|
||||
</button>
|
||||
<button
|
||||
className="reset-scene"
|
||||
onClick={() => {
|
||||
excalidrawRef.current.resetScene();
|
||||
}}
|
||||
>
|
||||
Reset Scene
|
||||
</button>
|
||||
<label>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={viewModeEnabled}
|
||||
onChange={() => setViewModeEnabled(!viewModeEnabled)}
|
||||
/>
|
||||
View mode
|
||||
</label>
|
||||
<label>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={zenModeEnabled}
|
||||
onChange={() => setZenModeEnabled(!zenModeEnabled)}
|
||||
/>
|
||||
Zen mode
|
||||
</label>
|
||||
<label>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={gridModeEnabled}
|
||||
onChange={() => setGridModeEnabled(!gridModeEnabled)}
|
||||
/>
|
||||
Grid mode
|
||||
</label>
|
||||
<label>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={theme === "dark"}
|
||||
onChange={() => {
|
||||
let newTheme = "light";
|
||||
if (theme === "light") {
|
||||
newTheme = "dark";
|
||||
}
|
||||
setTheme(newTheme);
|
||||
}}
|
||||
/>
|
||||
Switch to Dark Theme
|
||||
</label>
|
||||
</div>
|
||||
<div className="excalidraw-wrapper">
|
||||
<Excalidraw
|
||||
ref={excalidrawRef}
|
||||
initialData={InitialData}
|
||||
onChange={(elements, state) =>
|
||||
console.info("Elements :", elements, "State : ", state)
|
||||
}
|
||||
onPointerUpdate={(payload) => console.info(payload)}
|
||||
onCollabButtonClick={() =>
|
||||
window.alert("You clicked on collab button")
|
||||
}
|
||||
viewModeEnabled={viewModeEnabled}
|
||||
zenModeEnabled={zenModeEnabled}
|
||||
gridModeEnabled={gridModeEnabled}
|
||||
theme={theme}
|
||||
name="Custom name of drawing"
|
||||
UIOptions={{ canvasActions: { loadScene: false } }}
|
||||
renderTopRightUI={renderTopRightUI}
|
||||
renderFooter={renderFooter}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="export-wrapper button-wrapper">
|
||||
<label className="export-wrapper__checkbox">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={exportWithDarkMode}
|
||||
onChange={() => setExportWithDarkMode(!exportWithDarkMode)}
|
||||
/>
|
||||
Export with dark mode
|
||||
</label>
|
||||
<label className="export-wrapper__checkbox">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={shouldAddWatermark}
|
||||
onChange={() => setShouldAddWatermark(!shouldAddWatermark)}
|
||||
/>
|
||||
Add Watermark
|
||||
</label>
|
||||
<button
|
||||
onClick={async () => {
|
||||
const svg = await exportToSvg({
|
||||
elements: excalidrawRef.current.getSceneElements(),
|
||||
appState: {
|
||||
...initialData.appState,
|
||||
exportWithDarkMode,
|
||||
shouldAddWatermark,
|
||||
width: 300,
|
||||
height: 100,
|
||||
},
|
||||
embedScene: true,
|
||||
});
|
||||
document.querySelector(".export-svg").innerHTML = svg.outerHTML;
|
||||
}}
|
||||
>
|
||||
Export to SVG
|
||||
</button>
|
||||
<div className="export export-svg"></div>
|
||||
|
||||
<button
|
||||
onClick={async () => {
|
||||
const blob = await exportToBlob({
|
||||
elements: excalidrawRef.current.getSceneElements(),
|
||||
mimeType: "image/png",
|
||||
appState: {
|
||||
...initialData.appState,
|
||||
exportWithDarkMode,
|
||||
shouldAddWatermark,
|
||||
},
|
||||
});
|
||||
setBlobUrl(window.URL.createObjectURL(blob));
|
||||
}}
|
||||
>
|
||||
Export to Blob
|
||||
</button>
|
||||
<div className="export export-blob">
|
||||
<img src={blobUrl} alt="" />
|
||||
</div>
|
||||
|
||||
<button
|
||||
onClick={() => {
|
||||
const canvas = exportToCanvas({
|
||||
elements: excalidrawRef.current.getSceneElements(),
|
||||
appState: {
|
||||
...initialData.appState,
|
||||
exportWithDarkMode,
|
||||
shouldAddWatermark,
|
||||
},
|
||||
});
|
||||
const ctx = canvas.getContext("2d");
|
||||
ctx.font = "30px Virgil";
|
||||
ctx.strokeText("My custom text", 50, 60);
|
||||
setCanvasUrl(canvas.toDataURL());
|
||||
}}
|
||||
>
|
||||
Export to Canvas
|
||||
</button>
|
||||
<div className="export export-canvas">
|
||||
<img src={canvasUrl} alt="" />
|
||||
</div>
|
||||
</div>
|
||||
</Sidebar>
|
||||
</div>
|
||||
);
|
||||
}
|
42
src/packages/excalidraw/example/App.scss
Normal file
42
src/packages/excalidraw/example/App.scss
Normal file
@ -0,0 +1,42 @@
|
||||
.App {
|
||||
font-family: sans-serif;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.button-wrapper button {
|
||||
z-index: 1;
|
||||
height: 40px;
|
||||
max-width: 200px;
|
||||
margin: 10px;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.excalidraw .App-menu_top .buttonList {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.excalidraw-wrapper {
|
||||
height: 800px;
|
||||
margin: 50px;
|
||||
}
|
||||
|
||||
:root[dir="ltr"]
|
||||
.excalidraw
|
||||
.layer-ui__wrapper
|
||||
.zen-mode-transition.App-menu_bottom--transition-left {
|
||||
transform: none;
|
||||
}
|
||||
|
||||
.excalidraw .panelColumn {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.export-wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin: 50px;
|
||||
|
||||
&__checkbox {
|
||||
display: flex;
|
||||
}
|
||||
}
|
29
src/packages/excalidraw/example/index.html
Normal file
29
src/packages/excalidraw/example/index.html
Normal file
@ -0,0 +1,29 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta
|
||||
name="viewport"
|
||||
content="width=device-width, initial-scale=1, shrink-to-fit=no"
|
||||
/>
|
||||
<meta name="theme-color" content="#000000" />
|
||||
|
||||
<title>React App</title>
|
||||
<script>
|
||||
window.name = "codesandbox";
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<noscript> You need to enable JavaScript to run this app. </noscript>
|
||||
<div id="root"></div>
|
||||
<script src="https://unpkg.com/react@17.0.2/umd/react.development.js"></script>
|
||||
<script src="https://unpkg.com/react-dom@17.0.2/umd/react-dom.development.js"></script>
|
||||
|
||||
<!-- This is so that we use the bundled excalidraw.developement.js file instead
|
||||
of the actual source code -->
|
||||
<script src="./excalidraw.development.js"></script>
|
||||
|
||||
<script src="./bundle.js"></script>
|
||||
</body>
|
||||
</html>
|
12
src/packages/excalidraw/example/index.js
Normal file
12
src/packages/excalidraw/example/index.js
Normal file
@ -0,0 +1,12 @@
|
||||
import React from "react";
|
||||
import ReactDOM from "react-dom";
|
||||
|
||||
import App from "./App";
|
||||
|
||||
const rootElement = document.getElementById("root");
|
||||
ReactDOM.render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>,
|
||||
rootElement,
|
||||
);
|
1273
src/packages/excalidraw/example/initialData.js
Normal file
1273
src/packages/excalidraw/example/initialData.js
Normal file
File diff suppressed because it is too large
Load Diff
30
src/packages/excalidraw/example/sidebar/Sidebar.js
Normal file
30
src/packages/excalidraw/example/sidebar/Sidebar.js
Normal file
@ -0,0 +1,30 @@
|
||||
import { useState } from "react";
|
||||
import "./Sidebar.scss";
|
||||
export default function Sidebar(props) {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div id="mySidebar" className={`sidebar ${open ? "open" : ""}`}>
|
||||
<button className="closebtn" onClick={() => setOpen(false)}>
|
||||
x
|
||||
</button>
|
||||
<div className="sidebar-links">
|
||||
<button>Dummy Home</button>
|
||||
<button>Dummy About</button>{" "}
|
||||
</div>
|
||||
</div>
|
||||
<div className={`${open ? "sidebar-open" : ""}`}>
|
||||
<button
|
||||
className="openbtn"
|
||||
onClick={() => {
|
||||
setOpen(!open);
|
||||
}}
|
||||
>
|
||||
Open Sidebar
|
||||
</button>
|
||||
{props.children}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
66
src/packages/excalidraw/example/sidebar/Sidebar.scss
Normal file
66
src/packages/excalidraw/example/sidebar/Sidebar.scss
Normal file
@ -0,0 +1,66 @@
|
||||
.sidebar {
|
||||
height: 100%;
|
||||
width: 0;
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
top: 0;
|
||||
left: 0;
|
||||
background-color: #111;
|
||||
overflow-x: hidden;
|
||||
transition: 0.5s;
|
||||
padding-top: 60px;
|
||||
|
||||
&.open {
|
||||
width: 300px;
|
||||
}
|
||||
|
||||
&-links {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 20px;
|
||||
|
||||
button {
|
||||
padding: 10px;
|
||||
margin: 10px;
|
||||
background: #faa2c1;
|
||||
color: #fff;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar a {
|
||||
padding: 8px 8px 8px 32px;
|
||||
text-decoration: none;
|
||||
font-size: 25px;
|
||||
color: #818181;
|
||||
display: block;
|
||||
transition: 0.3s;
|
||||
}
|
||||
|
||||
.sidebar a:hover {
|
||||
color: #f1f1f1;
|
||||
}
|
||||
|
||||
.sidebar .closebtn {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
font-size: 36px;
|
||||
margin-left: 50px;
|
||||
}
|
||||
|
||||
.openbtn {
|
||||
font-size: 20px;
|
||||
cursor: pointer;
|
||||
background-color: #111;
|
||||
color: white;
|
||||
padding: 10px 15px;
|
||||
border: none;
|
||||
display: flex;
|
||||
margin-left: 50px;
|
||||
}
|
||||
.sidebar-open {
|
||||
margin-left: 300px;
|
||||
}
|
@ -65,7 +65,9 @@
|
||||
"typescript": "4.5.3",
|
||||
"webpack": "5.65.0",
|
||||
"webpack-bundle-analyzer": "4.5.0",
|
||||
"webpack-cli": "4.9.1"
|
||||
"webpack-cli": "4.9.1",
|
||||
"webpack-dev-server": "4.7.1",
|
||||
"webpack-merge": "5.8.0"
|
||||
},
|
||||
"bugs": "https://github.com/excalidraw/excalidraw/issues",
|
||||
"homepage": "https://github.com/excalidraw/excalidraw/tree/master/src/packages/excalidraw",
|
||||
@ -73,7 +75,8 @@
|
||||
"gen:types": "tsc --project ../../../tsconfig-types.json",
|
||||
"build:umd": "rm -rf dist && cross-env NODE_ENV=production webpack --config webpack.prod.config.js && cross-env NODE_ENV=development webpack --config webpack.dev.config.js && yarn gen:types",
|
||||
"build:umd:withAnalyzer": "cross-env NODE_ENV=production ANALYZER=true webpack --config webpack.prod.config.js",
|
||||
"pack": "yarn build:umd && yarn pack"
|
||||
"pack": "yarn build:umd && yarn pack",
|
||||
"start": "webpack serve --config webpack.dev-server.config.js "
|
||||
},
|
||||
"dependencies": {
|
||||
"dotenv": "10.0.0"
|
||||
|
28
src/packages/excalidraw/webpack.dev-server.config.js
Normal file
28
src/packages/excalidraw/webpack.dev-server.config.js
Normal file
@ -0,0 +1,28 @@
|
||||
const path = require("path");
|
||||
const { merge } = require("webpack-merge");
|
||||
|
||||
const devConfig = require("./webpack.dev.config");
|
||||
|
||||
const devServerConfig = {
|
||||
entry: {
|
||||
bundle: "./example/index.js",
|
||||
},
|
||||
// Server Configuration options
|
||||
devServer: {
|
||||
port: 3001,
|
||||
host: "localhost",
|
||||
hot: true,
|
||||
compress: true,
|
||||
static: {
|
||||
directory: path.join(__dirname, "example"),
|
||||
},
|
||||
client: {
|
||||
progress: true,
|
||||
logging: "info",
|
||||
overlay: true, //Shows a full-screen overlay in the browser when there are compiler errors or warnings.
|
||||
},
|
||||
open: ["./"],
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = merge(devServerConfig, devConfig);
|
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user