123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313 |
- import { useEffect, useState, useRef, useCallback } from "react";
- import InitialData from "./initialData";
- import Sidebar from "./sidebar/Sidebar";
- import "./App.scss";
- import initialData from "./initialData";
- import { MIME_TYPES } from "../../../constants";
- // This is so that we use the bundled excalidraw.development.js file instead
- // of the actual source code
- const { exportToCanvas, exportToSvg, exportToBlob } = window.Excalidraw;
- const Excalidraw = window.Excalidraw.default;
- const resolvablePromise = () => {
- let resolve;
- let reject;
- const promise = new Promise((_resolve, _reject) => {
- resolve = _resolve;
- reject = _reject;
- });
- promise.resolve = resolve;
- promise.reject = reject;
- return promise;
- };
- 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 [theme, setTheme] = useState("light");
- const initialStatePromiseRef = useRef({ promise: null });
- if (!initialStatePromiseRef.current.promise) {
- initialStatePromiseRef.current.promise = resolvablePromise();
- }
- useEffect(() => {
- const fetchData = async () => {
- const res = await fetch("/rocket.jpeg");
- const imageData = await res.blob();
- const reader = new FileReader();
- reader.readAsDataURL(imageData);
- reader.onload = function () {
- const imagesArray = [
- {
- id: "rocket",
- dataURL: reader.result,
- mimeType: MIME_TYPES.jpg,
- created: 1644915140367,
- },
- ];
- initialStatePromiseRef.current.promise.resolve(InitialData);
- excalidrawRef.current.addFiles(imagesArray);
- };
- };
- fetchData();
- 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);
- };
- const onLinkOpen = useCallback((element, event) => {
- const link = element.link;
- const { nativeEvent } = event.detail;
- const isNewTab = nativeEvent.ctrlKey || nativeEvent.metaKey;
- const isNewWindow = nativeEvent.shiftKey;
- const isInternalLink =
- link.startsWith("/") || link.includes(window.location.origin);
- if (isInternalLink && !isNewTab && !isNewWindow) {
- // signal that we're handling the redirect ourselves
- event.preventDefault();
- // do a custom redirect, such as passing to react-router
- // ...
- }
- }, []);
- 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>
- <button
- onClick={() => {
- excalidrawRef.current.updateScene({
- libraryItems: [
- {
- status: "published",
- elements: initialData.libraryItems[0],
- },
- {
- status: "unpublished",
- elements: initialData.libraryItems[1],
- },
- ],
- });
- }}
- >
- Update Library
- </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={initialStatePromiseRef.current.promise}
- 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}
- onLinkOpen={onLinkOpen}
- />
- </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>
- <button
- onClick={async () => {
- const svg = await exportToSvg({
- elements: excalidrawRef.current.getSceneElements(),
- appState: {
- ...initialData.appState,
- exportWithDarkMode,
- width: 300,
- height: 100,
- },
- embedScene: true,
- files: excalidrawRef.current.getFiles(),
- });
- 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,
- },
- files: excalidrawRef.current.getFiles(),
- });
- setBlobUrl(window.URL.createObjectURL(blob));
- }}
- >
- Export to Blob
- </button>
- <div className="export export-blob">
- <img src={blobUrl} alt="" />
- </div>
- <button
- onClick={async () => {
- const canvas = await exportToCanvas({
- elements: excalidrawRef.current.getSceneElements(),
- appState: {
- ...initialData.appState,
- exportWithDarkMode,
- },
- files: excalidrawRef.current.getFiles(),
- });
- 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>
- );
- }
|