summaryrefslogtreecommitdiffstats
path: root/packages/excalidraw/index.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'packages/excalidraw/index.tsx')
-rw-r--r--packages/excalidraw/index.tsx300
1 files changed, 300 insertions, 0 deletions
diff --git a/packages/excalidraw/index.tsx b/packages/excalidraw/index.tsx
new file mode 100644
index 0000000..b4b8574
--- /dev/null
+++ b/packages/excalidraw/index.tsx
@@ -0,0 +1,300 @@
+import React, { useEffect } from "react";
+import { InitializeApp } from "./components/InitializeApp";
+import App from "./components/App";
+import { isShallowEqual } from "./utils";
+import polyfill from "./polyfill";
+
+import "./css/app.scss";
+import "./css/styles.scss";
+import "./fonts/fonts.css";
+
+import type { AppProps, ExcalidrawProps } from "./types";
+import { defaultLang } from "./i18n";
+import { DEFAULT_UI_OPTIONS } from "./constants";
+import { EditorJotaiProvider, editorJotaiStore } from "./editor-jotai";
+import Footer from "./components/footer/FooterCenter";
+import MainMenu from "./components/main-menu/MainMenu";
+import WelcomeScreen from "./components/welcome-screen/WelcomeScreen";
+import LiveCollaborationTrigger from "./components/live-collaboration/LiveCollaborationTrigger";
+
+polyfill();
+
+const ExcalidrawBase = (props: ExcalidrawProps) => {
+ const {
+ onChange,
+ initialData,
+ excalidrawAPI,
+ isCollaborating = false,
+ onPointerUpdate,
+ renderTopRightUI,
+ langCode = defaultLang.code,
+ viewModeEnabled,
+ zenModeEnabled,
+ gridModeEnabled,
+ libraryReturnUrl,
+ theme,
+ name,
+ renderCustomStats,
+ onPaste,
+ detectScroll = true,
+ handleKeyboardGlobally = false,
+ onLibraryChange,
+ autoFocus = false,
+ generateIdForFile,
+ onLinkOpen,
+ generateLinkForSelection,
+ onPointerDown,
+ onPointerUp,
+ onScrollChange,
+ onDuplicate,
+ children,
+ validateEmbeddable,
+ renderEmbeddable,
+ aiEnabled,
+ showDeprecatedFonts,
+ } = props;
+
+ const canvasActions = props.UIOptions?.canvasActions;
+
+ // FIXME normalize/set defaults in parent component so that the memo resolver
+ // compares the same values
+ const UIOptions: AppProps["UIOptions"] = {
+ ...props.UIOptions,
+ canvasActions: {
+ ...DEFAULT_UI_OPTIONS.canvasActions,
+ ...canvasActions,
+ },
+ tools: {
+ image: props.UIOptions?.tools?.image ?? true,
+ },
+ };
+
+ if (canvasActions?.export) {
+ UIOptions.canvasActions.export.saveFileToDisk =
+ canvasActions.export?.saveFileToDisk ??
+ DEFAULT_UI_OPTIONS.canvasActions.export.saveFileToDisk;
+ }
+
+ if (
+ UIOptions.canvasActions.toggleTheme === null &&
+ typeof theme === "undefined"
+ ) {
+ UIOptions.canvasActions.toggleTheme = true;
+ }
+
+ useEffect(() => {
+ const importPolyfill = async () => {
+ //@ts-ignore
+ await import("canvas-roundrect-polyfill");
+ };
+
+ importPolyfill();
+
+ // Block pinch-zooming on iOS outside of the content area
+ const handleTouchMove = (event: TouchEvent) => {
+ // @ts-ignore
+ if (typeof event.scale === "number" && event.scale !== 1) {
+ event.preventDefault();
+ }
+ };
+
+ document.addEventListener("touchmove", handleTouchMove, {
+ passive: false,
+ });
+
+ return () => {
+ document.removeEventListener("touchmove", handleTouchMove);
+ };
+ }, []);
+
+ return (
+ <EditorJotaiProvider store={editorJotaiStore}>
+ <InitializeApp langCode={langCode} theme={theme}>
+ <App
+ onChange={onChange}
+ initialData={initialData}
+ excalidrawAPI={excalidrawAPI}
+ isCollaborating={isCollaborating}
+ onPointerUpdate={onPointerUpdate}
+ renderTopRightUI={renderTopRightUI}
+ langCode={langCode}
+ viewModeEnabled={viewModeEnabled}
+ zenModeEnabled={zenModeEnabled}
+ gridModeEnabled={gridModeEnabled}
+ libraryReturnUrl={libraryReturnUrl}
+ theme={theme}
+ name={name}
+ renderCustomStats={renderCustomStats}
+ UIOptions={UIOptions}
+ onPaste={onPaste}
+ detectScroll={detectScroll}
+ handleKeyboardGlobally={handleKeyboardGlobally}
+ onLibraryChange={onLibraryChange}
+ autoFocus={autoFocus}
+ generateIdForFile={generateIdForFile}
+ onLinkOpen={onLinkOpen}
+ generateLinkForSelection={generateLinkForSelection}
+ onPointerDown={onPointerDown}
+ onPointerUp={onPointerUp}
+ onScrollChange={onScrollChange}
+ onDuplicate={onDuplicate}
+ validateEmbeddable={validateEmbeddable}
+ renderEmbeddable={renderEmbeddable}
+ aiEnabled={aiEnabled !== false}
+ showDeprecatedFonts={showDeprecatedFonts}
+ >
+ {children}
+ </App>
+ </InitializeApp>
+ </EditorJotaiProvider>
+ );
+};
+
+const areEqual = (prevProps: ExcalidrawProps, nextProps: ExcalidrawProps) => {
+ // short-circuit early
+ if (prevProps.children !== nextProps.children) {
+ return false;
+ }
+
+ const {
+ initialData: prevInitialData,
+ UIOptions: prevUIOptions = {},
+ ...prev
+ } = prevProps;
+ const {
+ initialData: nextInitialData,
+ UIOptions: nextUIOptions = {},
+ ...next
+ } = nextProps;
+
+ // comparing UIOptions
+ const prevUIOptionsKeys = Object.keys(prevUIOptions) as (keyof Partial<
+ typeof DEFAULT_UI_OPTIONS
+ >)[];
+ const nextUIOptionsKeys = Object.keys(nextUIOptions) as (keyof Partial<
+ typeof DEFAULT_UI_OPTIONS
+ >)[];
+
+ if (prevUIOptionsKeys.length !== nextUIOptionsKeys.length) {
+ return false;
+ }
+
+ const isUIOptionsSame = prevUIOptionsKeys.every((key) => {
+ if (key === "canvasActions") {
+ const canvasOptionKeys = Object.keys(
+ prevUIOptions.canvasActions!,
+ ) as (keyof Partial<typeof DEFAULT_UI_OPTIONS.canvasActions>)[];
+ return canvasOptionKeys.every((key) => {
+ if (
+ key === "export" &&
+ prevUIOptions?.canvasActions?.export &&
+ nextUIOptions?.canvasActions?.export
+ ) {
+ return (
+ prevUIOptions.canvasActions.export.saveFileToDisk ===
+ nextUIOptions.canvasActions.export.saveFileToDisk
+ );
+ }
+ return (
+ prevUIOptions?.canvasActions?.[key] ===
+ nextUIOptions?.canvasActions?.[key]
+ );
+ });
+ }
+ return prevUIOptions[key] === nextUIOptions[key];
+ });
+
+ return isUIOptionsSame && isShallowEqual(prev, next);
+};
+
+export const Excalidraw = React.memo(ExcalidrawBase, areEqual);
+Excalidraw.displayName = "Excalidraw";
+
+export {
+ getSceneVersion,
+ hashElementsVersion,
+ hashString,
+ isInvisiblySmallElement,
+ getNonDeletedElements,
+ getTextFromElements,
+} from "./element";
+export { defaultLang, useI18n, languages } from "./i18n";
+export {
+ restore,
+ restoreAppState,
+ restoreElements,
+ restoreLibraryItems,
+} from "./data/restore";
+
+export { reconcileElements } from "./data/reconcile";
+
+export {
+ exportToCanvas,
+ exportToBlob,
+ exportToSvg,
+ exportToClipboard,
+} from "@excalidraw/utils/export";
+
+export { serializeAsJSON, serializeLibraryAsJSON } from "./data/json";
+export {
+ loadFromBlob,
+ loadSceneOrLibraryFromBlob,
+ loadLibraryFromBlob,
+} from "./data/blob";
+export { getFreeDrawSvgPath } from "./renderer/renderElement";
+export { mergeLibraryItems, getLibraryItemsHash } from "./data/library";
+export { isLinearElement } from "./element/typeChecks";
+
+export {
+ FONT_FAMILY,
+ THEME,
+ MIME_TYPES,
+ ROUNDNESS,
+ DEFAULT_LASER_COLOR,
+ UserIdleState,
+} from "./constants";
+
+export {
+ mutateElement,
+ newElementWith,
+ bumpVersion,
+} from "./element/mutateElement";
+
+export { CaptureUpdateAction } from "./store";
+
+export { parseLibraryTokensFromUrl, useHandleLibrary } from "./data/library";
+
+export {
+ sceneCoordsToViewportCoords,
+ viewportCoordsToSceneCoords,
+} from "./utils";
+
+export { Sidebar } from "./components/Sidebar/Sidebar";
+export { Button } from "./components/Button";
+export { Footer };
+export { MainMenu };
+export { useDevice } from "./components/App";
+export { WelcomeScreen };
+export { LiveCollaborationTrigger };
+export { Stats } from "./components/Stats";
+
+export { DefaultSidebar } from "./components/DefaultSidebar";
+export { TTDDialog } from "./components/TTDDialog/TTDDialog";
+export { TTDDialogTrigger } from "./components/TTDDialog/TTDDialogTrigger";
+
+export { normalizeLink } from "./data/url";
+export { zoomToFitBounds } from "./actions/actionCanvas";
+export { convertToExcalidrawElements } from "./data/transform";
+export { getCommonBounds, getVisibleSceneBounds } from "./element/bounds";
+
+export {
+ elementsOverlappingBBox,
+ isElementInsideBBox,
+ elementPartiallyOverlapsWithOrContainsBBox,
+} from "@excalidraw/utils/withinBounds";
+
+export { DiagramToCodePlugin } from "./components/DiagramToCodePlugin/DiagramToCodePlugin";
+export { getDataURL } from "./data/blob";
+export { isElementLink } from "./element/elementLink";
+
+export { setCustomTextMetricsProvider } from "./element/textMeasurements";