@@ -88,17 +50,7 @@ export const Stats = (props: {
{t("stats.height")} |
{Math.round(boundingBox[3]) - Math.round(boundingBox[1])} |
-
- {t("stats.storage")} |
-
-
- {t("stats.scene")} |
- {nFormatter(storageSizes.scene, 1)} |
-
-
- {t("stats.total")} |
- {nFormatter(storageSizes.total, 1)} |
-
+
{selectedElements.length === 1 && (
{t("stats.element")} |
@@ -120,31 +72,17 @@ export const Stats = (props: {
<>
{"x"} |
-
- {Math.round(
- selectedElements.length === 1
- ? selectedElements[0].x
- : selectedBoundingBox[0],
- )}
- |
+ {Math.round(selectedBoundingBox[0])} |
{"y"} |
-
- {Math.round(
- selectedElements.length === 1
- ? selectedElements[0].y
- : selectedBoundingBox[1],
- )}
- |
+ {Math.round(selectedBoundingBox[1])} |
{t("stats.width")} |
{Math.round(
- selectedElements.length === 1
- ? selectedElements[0].width
- : selectedBoundingBox[2] - selectedBoundingBox[0],
+ selectedBoundingBox[2] - selectedBoundingBox[0],
)}
|
@@ -152,9 +90,7 @@ export const Stats = (props: {
{t("stats.height")} |
{Math.round(
- selectedElements.length === 1
- ? selectedElements[0].height
- : selectedBoundingBox[3] - selectedBoundingBox[1],
+ selectedBoundingBox[3] - selectedBoundingBox[1],
)}
|
@@ -170,28 +106,7 @@ export const Stats = (props: {
)}
-
- {t("stats.version")} |
-
-
- {
- try {
- await copyTextToSystemClipboard(getVersion());
- props.setAppState({
- toastMessage: t("toast.copyToClipboard"),
- });
- } catch {}
- }}
- title={t("stats.versionCopy")}
- >
- {timestamp}
-
- {hash}
- |
-
+ {props.renderCustomStats?.(props.elements, props.appState)}
diff --git a/src/constants.ts b/src/constants.ts
index 8142c60d9..c7a58d8db 100644
--- a/src/constants.ts
+++ b/src/constants.ts
@@ -1,5 +1,6 @@
import { FontFamily } from "./element/types";
import cssVariables from "./css/variables.module.scss";
+import { AppProps } from "./types";
export const APP_NAME = "Excalidraw";
@@ -117,3 +118,23 @@ export const MODES = {
};
export const THEME_FILTER = cssVariables.themeFilter;
+
+export const URL_QUERY_KEYS = {
+ addLibrary: "addLibrary",
+} as const;
+
+export const URL_HASH_KEYS = {
+ addLibrary: "addLibrary",
+} as const;
+
+export const DEFAULT_UI_OPTIONS: AppProps["UIOptions"] = {
+ canvasActions: {
+ changeViewBackgroundColor: true,
+ clearCanvas: true,
+ export: true,
+ loadScene: true,
+ saveAsScene: true,
+ saveScene: true,
+ theme: true,
+ },
+};
diff --git a/src/css/styles.scss b/src/css/styles.scss
index 4fd038209..5592eb25e 100644
--- a/src/css/styles.scss
+++ b/src/css/styles.scss
@@ -16,6 +16,8 @@
bottom: 0;
left: 0;
right: 0;
+ height: 100%;
+ width: 100%;
// serves 2 purposes:
// 1. prevent selecting text outside the component when double-clicking or
@@ -47,6 +49,12 @@
z-index: var(--zIndex-canvas);
}
+ #canvas {
+ // Remove the main canvas from document flow to avoid resizeObserver
+ // feedback loop (see https://github.com/excalidraw/excalidraw/pull/3379)
+ position: absolute;
+ }
+
&.theme--dark {
// The percentage is inspired by
// https://material.io/design/color/dark-theme.html#properties, which
@@ -454,6 +462,14 @@
fill: $oc-gray-6;
bottom: 14px;
width: 1.5rem;
+ padding: 0;
+ margin: 0;
+ background: none;
+ color: var(--icon-fill-color);
+
+ &:hover {
+ background: none;
+ }
:root[dir="ltr"] & {
right: 14px;
diff --git a/src/data/blob.ts b/src/data/blob.ts
index 21bb9f208..0702436f4 100644
--- a/src/data/blob.ts
+++ b/src/data/blob.ts
@@ -1,5 +1,5 @@
import { cleanAppStateForExport } from "../appState";
-import { EXPORT_DATA_TYPES, MIME_TYPES } from "../constants";
+import { EXPORT_DATA_TYPES } from "../constants";
import { clearElementsForExport } from "../element";
import { CanvasError } from "../errors";
import { t } from "../i18n";
@@ -95,13 +95,7 @@ export const loadFromBlob = async (
elements: clearElementsForExport(data.elements || []),
appState: {
theme: localAppState?.theme,
- fileHandle:
- blob.handle &&
- ["application/json", MIME_TYPES.excalidraw].includes(
- getMimeType(blob),
- )
- ? blob.handle
- : null,
+ fileHandle: blob.handle ?? null,
...cleanAppStateForExport(data.appState || {}),
...(localAppState
? calculateScrollCenter(data.elements || [], localAppState, null)
diff --git a/src/data/json.ts b/src/data/json.ts
index 4ecaccb3d..59ac5eb3e 100644
--- a/src/data/json.ts
+++ b/src/data/json.ts
@@ -116,5 +116,5 @@ export const importLibraryFromJSON = async () => {
extensions: [".json", ".excalidrawlib"],
*/
});
- Library.importLibrary(blob);
+ await Library.importLibrary(blob);
};
diff --git a/src/data/library.ts b/src/data/library.ts
index 4f5c11a82..665881c34 100644
--- a/src/data/library.ts
+++ b/src/data/library.ts
@@ -4,9 +4,11 @@ import { restoreElements } from "./restore";
import { STORAGE_KEYS } from "../constants";
import { getNonDeletedElements } from "../element";
import { NonDeleted, ExcalidrawElement } from "../element/types";
+import { nanoid } from "nanoid";
export class Library {
private static libraryCache: LibraryItems | null = null;
+ public static csrfToken = nanoid();
static resetLibrary = () => {
Library.libraryCache = null;
diff --git a/src/data/restore.ts b/src/data/restore.ts
index 2a12c639e..172cf58d8 100644
--- a/src/data/restore.ts
+++ b/src/data/restore.ts
@@ -144,7 +144,7 @@ export const restoreElements = (
export const restoreAppState = (
appState: ImportedDataState["appState"],
localAppState: Partial
| null,
-): AppState => {
+): DataState["appState"] => {
appState = appState || {};
const defaultAppState = getDefaultAppState();
@@ -166,8 +166,6 @@ export const restoreAppState = (
return {
...nextAppState,
- offsetLeft: appState.offsetLeft || 0,
- offsetTop: appState.offsetTop || 0,
// Migrates from previous version where appState.zoom was a number
zoom:
typeof appState.zoom === "number"
diff --git a/src/data/types.ts b/src/data/types.ts
index cac2ef64d..e414c7cad 100644
--- a/src/data/types.ts
+++ b/src/data/types.ts
@@ -6,7 +6,7 @@ export interface DataState {
version?: string;
source?: string;
elements: readonly ExcalidrawElement[];
- appState: MarkOptional;
+ appState: Omit;
}
export interface ImportedDataState {
diff --git a/src/element/resizeElements.ts b/src/element/resizeElements.ts
index 1e66550cd..b8a1a65f4 100644
--- a/src/element/resizeElements.ts
+++ b/src/element/resizeElements.ts
@@ -31,7 +31,7 @@ import {
import { PointerDownState } from "../components/App";
import { Point } from "../types";
-const normalizeAngle = (angle: number): number => {
+export const normalizeAngle = (angle: number): number => {
if (angle >= 2 * Math.PI) {
return angle - 2 * Math.PI;
}
@@ -181,7 +181,7 @@ const getPerfectElementSizeWithRotation = (
return rotate(size.width, size.height, 0, 0, -angle);
};
-const reshapeSingleTwoPointElement = (
+export const reshapeSingleTwoPointElement = (
element: NonDeleted,
resizeArrowDirection: "origin" | "end",
isRotateWithDiscreteAngle: boolean,
@@ -378,7 +378,7 @@ const resizeSingleTextElement = (
}
};
-const resizeSingleElement = (
+export const resizeSingleElement = (
stateAtResizeStart: NonDeletedExcalidrawElement,
shouldKeepSidesRatio: boolean,
element: NonDeletedExcalidrawElement,
diff --git a/src/excalidraw-app/CustomStats.tsx b/src/excalidraw-app/CustomStats.tsx
new file mode 100644
index 000000000..2b79ffc1e
--- /dev/null
+++ b/src/excalidraw-app/CustomStats.tsx
@@ -0,0 +1,85 @@
+import React, { useEffect, useState } from "react";
+import { debounce, getVersion, nFormatter } from "../utils";
+import {
+ getElementsStorageSize,
+ getTotalStorageSize,
+} from "./data/localStorage";
+import { DEFAULT_VERSION } from "../constants";
+import { t } from "../i18n";
+import { copyTextToSystemClipboard } from "../clipboard";
+type StorageSizes = { scene: number; total: number };
+
+const STORAGE_SIZE_TIMEOUT = 500;
+
+const getStorageSizes = debounce((cb: (sizes: StorageSizes) => void) => {
+ cb({
+ scene: getElementsStorageSize(),
+ total: getTotalStorageSize(),
+ });
+}, STORAGE_SIZE_TIMEOUT);
+
+type Props = {
+ setToastMessage: (message: string) => void;
+};
+const CustomStats = (props: Props) => {
+ const [storageSizes, setStorageSizes] = useState({
+ scene: 0,
+ total: 0,
+ });
+
+ useEffect(() => {
+ getStorageSizes((sizes) => {
+ setStorageSizes(sizes);
+ });
+ });
+ useEffect(() => () => getStorageSizes.cancel(), []);
+
+ const version = getVersion();
+ let hash;
+ let timestamp;
+
+ if (version !== DEFAULT_VERSION) {
+ timestamp = version.slice(0, 16).replace("T", " ");
+ hash = version.slice(21);
+ } else {
+ timestamp = t("stats.versionNotAvailable");
+ }
+
+ return (
+ <>
+
+ {t("stats.storage")} |
+
+
+ {t("stats.scene")} |
+ {nFormatter(storageSizes.scene, 1)} |
+
+
+ {t("stats.total")} |
+ {nFormatter(storageSizes.total, 1)} |
+
+
+ {t("stats.version")} |
+
+
+ {
+ try {
+ await copyTextToSystemClipboard(getVersion());
+ props.setToastMessage(t("toast.copyToClipboard"));
+ } catch {}
+ }}
+ title={t("stats.versionCopy")}
+ >
+ {timestamp}
+
+ {hash}
+ |
+
+ >
+ );
+};
+
+export default CustomStats;
diff --git a/src/excalidraw-app/collab/CollabWrapper.tsx b/src/excalidraw-app/collab/CollabWrapper.tsx
index 9b353eb68..de87184cc 100644
--- a/src/excalidraw-app/collab/CollabWrapper.tsx
+++ b/src/excalidraw-app/collab/CollabWrapper.tsx
@@ -38,7 +38,7 @@ import Portal from "./Portal";
import RoomDialog from "./RoomDialog";
import { createInverseContext } from "../../createInverseContext";
import { t } from "../../i18n";
-import { UserIdleState } from "./types";
+import { UserIdleState } from "../../types";
import { IDLE_THRESHOLD, ACTIVE_THRESHOLD } from "../../constants";
import { trackEvent } from "../../analytics";
@@ -113,8 +113,8 @@ class CollabWrapper extends PureComponent {
process.env.NODE_ENV === ENV.TEST ||
process.env.NODE_ENV === ENV.DEVELOPMENT
) {
- window.h = window.h || ({} as Window["h"]);
- Object.defineProperties(window.h, {
+ window.collab = window.collab || ({} as Window["collab"]);
+ Object.defineProperties(window, {
collab: {
configurable: true,
value: this,
@@ -658,4 +658,17 @@ class CollabWrapper extends PureComponent {
}
}
+declare global {
+ interface Window {
+ collab: InstanceType;
+ }
+}
+
+if (
+ process.env.NODE_ENV === ENV.TEST ||
+ process.env.NODE_ENV === ENV.DEVELOPMENT
+) {
+ window.collab = window.collab || ({} as Window["collab"]);
+}
+
export default CollabWrapper;
diff --git a/src/excalidraw-app/collab/Portal.tsx b/src/excalidraw-app/collab/Portal.tsx
index b79f20ef9..b58cc2b92 100644
--- a/src/excalidraw-app/collab/Portal.tsx
+++ b/src/excalidraw-app/collab/Portal.tsx
@@ -9,7 +9,7 @@ import CollabWrapper from "./CollabWrapper";
import { getSyncableElements } from "../../packages/excalidraw/index";
import { ExcalidrawElement } from "../../element/types";
import { BROADCAST, SCENE } from "../app_constants";
-import { UserIdleState } from "./types";
+import { UserIdleState } from "../../types";
import { trackEvent } from "../../analytics";
class Portal {
diff --git a/src/excalidraw-app/collab/types.ts b/src/excalidraw-app/collab/types.ts
deleted file mode 100644
index f2b888265..000000000
--- a/src/excalidraw-app/collab/types.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-export enum UserIdleState {
- ACTIVE = "active",
- AWAY = "away",
- IDLE = "idle",
-}
diff --git a/src/excalidraw-app/data/index.ts b/src/excalidraw-app/data/index.ts
index 7d4c6eb35..5769d780e 100644
--- a/src/excalidraw-app/data/index.ts
+++ b/src/excalidraw-app/data/index.ts
@@ -3,8 +3,7 @@ import { restore } from "../../data/restore";
import { ImportedDataState } from "../../data/types";
import { ExcalidrawElement } from "../../element/types";
import { t } from "../../i18n";
-import { AppState } from "../../types";
-import { UserIdleState } from "../collab/types";
+import { AppState, UserIdleState } from "../../types";
const byteToHex = (byte: number): string => `0${byte.toString(16)}`.slice(-2);
diff --git a/src/excalidraw-app/data/localStorage.ts b/src/excalidraw-app/data/localStorage.ts
index 2920b47bf..f4e2b4363 100644
--- a/src/excalidraw-app/data/localStorage.ts
+++ b/src/excalidraw-app/data/localStorage.ts
@@ -101,7 +101,7 @@ export const importFromLocalStorage = () => {
export const getElementsStorageSize = () => {
try {
const elements = localStorage.getItem(STORAGE_KEYS.LOCAL_STORAGE_ELEMENTS);
- const elementsSize = elements ? JSON.stringify(elements).length : 0;
+ const elementsSize = elements?.length || 0;
return elementsSize;
} catch (error) {
console.error(error);
@@ -117,9 +117,9 @@ export const getTotalStorageSize = () => {
APP_STORAGE_KEYS.LOCAL_STORAGE_LIBRARY,
);
- const appStateSize = appState ? JSON.stringify(appState).length : 0;
- const collabSize = collab ? JSON.stringify(collab).length : 0;
- const librarySize = library ? JSON.stringify(library).length : 0;
+ const appStateSize = appState?.length || 0;
+ const collabSize = collab?.length || 0;
+ const librarySize = library?.length || 0;
return appStateSize + collabSize + librarySize + getElementsStorageSize();
} catch (error) {
diff --git a/src/excalidraw-app/index.tsx b/src/excalidraw-app/index.tsx
index be79990d2..f54be2713 100644
--- a/src/excalidraw-app/index.tsx
+++ b/src/excalidraw-app/index.tsx
@@ -3,7 +3,6 @@ import React, {
useCallback,
useContext,
useEffect,
- useLayoutEffect,
useRef,
useState,
} from "react";
@@ -12,7 +11,13 @@ import { getDefaultAppState } from "../appState";
import { ExcalidrawImperativeAPI } from "../components/App";
import { ErrorDialog } from "../components/ErrorDialog";
import { TopErrorBoundary } from "../components/TopErrorBoundary";
-import { APP_NAME, EVENT, TITLE_TIMEOUT, VERSION_TIMEOUT } from "../constants";
+import {
+ APP_NAME,
+ EVENT,
+ TITLE_TIMEOUT,
+ URL_HASH_KEYS,
+ VERSION_TIMEOUT,
+} from "../constants";
import { loadFromBlob } from "../data/blob";
import { DataState, ImportedDataState } from "../data/types";
import {
@@ -44,6 +49,7 @@ import {
importFromLocalStorage,
saveToLocalStorage,
} from "./data/localStorage";
+import CustomStats from "./CustomStats";
const languageDetector = new LanguageDetector();
languageDetector.init({
@@ -155,31 +161,11 @@ const initializeScene = async (opts: {
return null;
};
-function ExcalidrawWrapper() {
- // dimensions
- // ---------------------------------------------------------------------------
-
- const [dimensions, setDimensions] = useState({
- width: window.innerWidth,
- height: window.innerHeight,
- });
+const ExcalidrawWrapper = () => {
const [errorMessage, setErrorMessage] = useState("");
const currentLangCode = languageDetector.detect() || defaultLang.code;
const [langCode, setLangCode] = useState(currentLangCode);
- useLayoutEffect(() => {
- const onResize = () => {
- setDimensions({
- width: window.innerWidth,
- height: window.innerHeight,
- });
- };
-
- window.addEventListener("resize", onResize);
-
- return () => window.removeEventListener("resize", onResize);
- }, []);
-
// initial state
// ---------------------------------------------------------------------------
@@ -213,12 +199,24 @@ function ExcalidrawWrapper() {
initialStatePromiseRef.current.promise.resolve(scene);
});
- const onHashChange = (_: HashChangeEvent) => {
- initializeScene({ collabAPI }).then((scene) => {
- if (scene) {
- excalidrawAPI.updateScene(scene);
- }
- });
+ const onHashChange = (event: HashChangeEvent) => {
+ event.preventDefault();
+ const hash = new URLSearchParams(window.location.hash.slice(1));
+ const libraryUrl = hash.get(URL_HASH_KEYS.addLibrary);
+ if (libraryUrl) {
+ // If hash changed and it contains library url, import it and replace
+ // the url to its previous state (important in case of collaboration
+ // and similar).
+ // Using history API won't trigger another hashchange.
+ window.history.replaceState({}, "", event.oldURL);
+ excalidrawAPI.importLibrary(libraryUrl, hash.get("token"));
+ } else {
+ initializeScene({ collabAPI }).then((scene) => {
+ if (scene) {
+ excalidrawAPI.updateScene(scene);
+ }
+ });
+ }
};
const titleTimeout = setTimeout(
@@ -305,13 +303,19 @@ function ExcalidrawWrapper() {
[langCode],
);
+ const renderCustomStats = () => {
+ return (
+ excalidrawAPI!.setToastMessage(message)}
+ />
+ );
+ };
+
return (
<>
{excalidrawAPI && }
{errorMessage && (
@@ -329,9 +334,9 @@ function ExcalidrawWrapper() {
)}
>
);
-}
+};
-export default function ExcalidrawApp() {
+const ExcalidrawApp = () => {
return (
@@ -339,4 +344,6 @@ export default function ExcalidrawApp() {
);
-}
+};
+
+export default ExcalidrawApp;
diff --git a/src/global.d.ts b/src/global.d.ts
index 2730f5f76..3a329e971 100644
--- a/src/global.d.ts
+++ b/src/global.d.ts
@@ -89,3 +89,5 @@ interface Blob {
handle?: import("browser-fs-acces").FileSystemHandle;
name?: string;
}
+
+declare module "*.scss";
diff --git a/src/index-node.ts b/src/index-node.ts
index 811983aa0..32eb3d37d 100644
--- a/src/index-node.ts
+++ b/src/index-node.ts
@@ -63,6 +63,8 @@ const canvas = exportToCanvas(
...getDefaultAppState(),
offsetTop: 0,
offsetLeft: 0,
+ width: 0,
+ height: 0,
},
{
exportBackground: true,
diff --git a/src/is-mobile.tsx b/src/is-mobile.tsx
index 466fcd0d6..a64942f5f 100644
--- a/src/is-mobile.tsx
+++ b/src/is-mobile.tsx
@@ -34,7 +34,4 @@ export const IsMobileProvider = ({
};
export const isMobile = () => getIsMobileMatcher().matches;
-
-export default function useIsMobile() {
- return useContext(context);
-}
+export const useIsMobile = () => useContext(context);
diff --git a/src/locales/ar-SA.json b/src/locales/ar-SA.json
index d7ecd0cb9..47fb94bd0 100644
--- a/src/locales/ar-SA.json
+++ b/src/locales/ar-SA.json
@@ -61,7 +61,7 @@
"architect": "معماري",
"artist": "رسام",
"cartoonist": "كرتوني",
- "fileTitle": "عنوان الملف",
+ "fileTitle": "",
"colorPicker": "اختيار الألوان",
"canvasBackground": "خلفية اللوحة",
"drawingCanvas": "لوحة الرسم",
@@ -77,7 +77,7 @@
"group": "تحديد مجموعة",
"ungroup": "إلغاء تحديد مجموعة",
"collaborators": "المتعاونون",
- "showGrid": "",
+ "showGrid": "إظهار الشبكة",
"addToLibrary": "أضف إلى المكتبة",
"removeFromLibrary": "حذف من المكتبة",
"libraryLoadingMessage": "جارٍ تحميل المكتبة…",
@@ -92,9 +92,11 @@
"centerHorizontally": "توسيط أفقي",
"distributeHorizontally": "التوزيع الأفقي",
"distributeVertically": "التوزيع عمودياً",
- "viewMode": "",
+ "flipHorizontal": "",
+ "flipVertical": "",
+ "viewMode": "نمط العرض",
"toggleExportColorScheme": "",
- "share": ""
+ "share": "مشاركة"
},
"buttons": {
"clearReset": "إعادة تعيين اللوحة",
@@ -119,7 +121,7 @@
"edit": "تعديل",
"undo": "تراجع",
"redo": "إعادة تنفيذ",
- "resetLibrary": "",
+ "resetLibrary": "إعادة ضبط المكتبة",
"createNewRoom": "إنشاء غرفة جديدة",
"fullScreen": "شاشة كاملة",
"darkMode": "الوضع المظلم",
@@ -138,7 +140,7 @@
"decryptFailed": "تعذر فك تشفير البيانات.",
"uploadedSecurly": "تم تأمين التحميل بتشفير النهاية إلى النهاية، مما يعني أن خادوم Excalidraw والأطراف الثالثة لا يمكنها قراءة المحتوى.",
"loadSceneOverridePrompt": "تحميل الرسم الخارجي سيحل محل المحتوى الموجود لديك. هل ترغب في المتابعة؟",
- "collabStopOverridePrompt": "",
+ "collabStopOverridePrompt": "إيقاف الجلسة سيؤدي إلى الكتابة فوق رسومك السابقة المخزنة داخليا. هل أنت متأكد؟\n\n(إذا كنت ترغب في الاحتفاظ برسمك المخزن داخليا، ببساطة أغلق علامة تبويب المتصفح بدلاً من ذلك.)",
"errorLoadingLibrary": "حصل خطأ أثناء تحميل مكتبة الطرف الثالث.",
"confirmAddLibrary": "هذا سيضيف {{numShapes}} شكل إلى مكتبتك. هل أنت متأكد؟",
"imageDoesNotContainScene": "استيراد الصور غير مدعوم في الوقت الراهن.\n\nهل تريد استيراد مشهد؟ لا يبدو أن هذه الصورة تحتوي على أي بيانات مشهد. هل قمت بسماح هذا أثناء التصدير؟",
@@ -212,9 +214,9 @@
"curvedArrow": "",
"curvedLine": "",
"documentation": "",
- "drag": "",
- "editor": "",
- "github": "",
+ "drag": "اسحب",
+ "editor": "المحرر",
+ "github": "عثرت على مشكلة؟ إرسال",
"howto": "",
"or": "",
"preventBinding": "",
@@ -228,7 +230,8 @@
"zoomToSelection": ""
},
"encrypted": {
- "tooltip": "رسوماتك مشفرة من النهاية إلى النهاية حتى أن خوادم Excalidraw لن تراها أبدا."
+ "tooltip": "رسوماتك مشفرة من النهاية إلى النهاية حتى أن خوادم Excalidraw لن تراها أبدا.",
+ "link": ""
},
"stats": {
"angle": "الزاوية",
diff --git a/src/locales/bg-BG.json b/src/locales/bg-BG.json
index 5e203092b..506e8ff6e 100644
--- a/src/locales/bg-BG.json
+++ b/src/locales/bg-BG.json
@@ -61,7 +61,7 @@
"architect": "Архитект",
"artist": "Художник",
"cartoonist": "Карикатурист",
- "fileTitle": "Заглавие на файл",
+ "fileTitle": "",
"colorPicker": "Избор на цвят",
"canvasBackground": "Фон на платно",
"drawingCanvas": "Платно за рисуване",
@@ -92,6 +92,8 @@
"centerHorizontally": "Центрирай хоризонтално",
"distributeHorizontally": "Разпредели хоризонтално",
"distributeVertically": "Разпредели вертикално",
+ "flipHorizontal": "",
+ "flipVertical": "",
"viewMode": "Изглед",
"toggleExportColorScheme": "",
"share": ""
@@ -228,7 +230,8 @@
"zoomToSelection": "Приближи селекцията"
},
"encrypted": {
- "tooltip": "Вашите рисунки са криптирани от край до край, така че сървърите на Excalidraw няма да могат да ги виждат."
+ "tooltip": "Вашите рисунки са криптирани от край до край, така че сървърите на Excalidraw няма да могат да ги виждат.",
+ "link": ""
},
"stats": {
"angle": "Ъгъл",
diff --git a/src/locales/ca-ES.json b/src/locales/ca-ES.json
index af7dd9398..04a494163 100644
--- a/src/locales/ca-ES.json
+++ b/src/locales/ca-ES.json
@@ -61,7 +61,7 @@
"architect": "Arquitecte",
"artist": "Artista",
"cartoonist": "Dibuixant",
- "fileTitle": "Títol del fitxer",
+ "fileTitle": "",
"colorPicker": "Selector de colors",
"canvasBackground": "Fons del llenç",
"drawingCanvas": "Llenç de dibuix",
@@ -92,6 +92,8 @@
"centerHorizontally": "Centrar horitzontalment",
"distributeHorizontally": "Distribuir horitzontalment",
"distributeVertically": "Distribuir verticalment",
+ "flipHorizontal": "",
+ "flipVertical": "",
"viewMode": "Mode de visualització",
"toggleExportColorScheme": "Canvia l'esquema de colors de l'exportació",
"share": "Compartir"
@@ -228,7 +230,8 @@
"zoomToSelection": "Zoom per veure la selecció"
},
"encrypted": {
- "tooltip": "Els vostres dibuixos estan xifrats de punta a punta de manera que els servidors d’Excalidraw no els veuran mai."
+ "tooltip": "Els vostres dibuixos estan xifrats de punta a punta de manera que els servidors d’Excalidraw no els veuran mai.",
+ "link": ""
},
"stats": {
"angle": "Angle",
diff --git a/src/locales/de-DE.json b/src/locales/de-DE.json
index 9df67751f..13557904f 100644
--- a/src/locales/de-DE.json
+++ b/src/locales/de-DE.json
@@ -92,6 +92,8 @@
"centerHorizontally": "Horizontal zentrieren",
"distributeHorizontally": "Horizontal verteilen",
"distributeVertically": "Vertikal verteilen",
+ "flipHorizontal": "Horizontal spiegeln",
+ "flipVertical": "Vertikal spiegeln",
"viewMode": "Ansichtsmodus",
"toggleExportColorScheme": "Farbschema für Export umschalten",
"share": "Teilen"
@@ -228,7 +230,8 @@
"zoomToSelection": "Auf Auswahl zoomen"
},
"encrypted": {
- "tooltip": "Da deine Zeichnungen Ende-zu-Ende verschlüsselt werden, sehen auch unsere Excalidraw-Server sie niemals."
+ "tooltip": "Da deine Zeichnungen Ende-zu-Ende verschlüsselt werden, sehen auch unsere Excalidraw-Server sie niemals.",
+ "link": "Blogbeitrag über Ende-zu-Ende-Verschlüsselung in Excalidraw"
},
"stats": {
"angle": "Winkel",
diff --git a/src/locales/el-GR.json b/src/locales/el-GR.json
index 7ebc2a7c8..76a3e3eef 100644
--- a/src/locales/el-GR.json
+++ b/src/locales/el-GR.json
@@ -61,7 +61,7 @@
"architect": "Αρχιτέκτονας",
"artist": "Καλλιτέχνης",
"cartoonist": "Σκιτσογράφος",
- "fileTitle": "Τίτλος αρχείου",
+ "fileTitle": "Όνομα αρχείου",
"colorPicker": "Επιλογή Χρώματος",
"canvasBackground": "Φόντο καμβά",
"drawingCanvas": "Σχεδίαση καμβά",
@@ -92,9 +92,11 @@
"centerHorizontally": "Κέντρο οριζόντια",
"distributeHorizontally": "Οριζόντια κατανομή",
"distributeVertically": "Κατακόρυφη κατανομή",
+ "flipHorizontal": "Οριζόντια αναστροφή",
+ "flipVertical": "Κατακόρυφη αναστροφή",
"viewMode": "Λειτουργία προβολής",
"toggleExportColorScheme": "Εναλλαγή εξαγωγής θέματος χρωμάτων",
- "share": ""
+ "share": "Κοινοποίηση"
},
"buttons": {
"clearReset": "Επαναφορά του καμβά",
@@ -228,7 +230,8 @@
"zoomToSelection": "Ζουμ στην επιλογή"
},
"encrypted": {
- "tooltip": "Τα σχέδιά σου είναι κρυπτογραφημένα από άκρο σε άκρο, έτσι δεν θα είναι ποτέ ορατά μέσα από τους διακομιστές του Excalidraw."
+ "tooltip": "Τα σχέδιά σου είναι κρυπτογραφημένα από άκρο σε άκρο, έτσι δεν θα είναι ποτέ ορατά μέσα από τους διακομιστές του Excalidraw.",
+ "link": ""
},
"stats": {
"angle": "Γωνία",
diff --git a/src/locales/en.json b/src/locales/en.json
index 0b27e40b1..87ff17f65 100644
--- a/src/locales/en.json
+++ b/src/locales/en.json
@@ -94,6 +94,8 @@
"centerHorizontally": "Center horizontally",
"distributeHorizontally": "Distribute horizontally",
"distributeVertically": "Distribute vertically",
+ "flipHorizontal": "Flip horizontal",
+ "flipVertical": "Flip vertical",
"viewMode": "View mode",
"toggleExportColorScheme": "Toggle export color scheme",
"share": "Share"
@@ -230,7 +232,8 @@
"zoomToSelection": "Zoom to selection"
},
"encrypted": {
- "tooltip": "Your drawings are end-to-end encrypted so Excalidraw's servers will never see them."
+ "tooltip": "Your drawings are end-to-end encrypted so Excalidraw's servers will never see them.",
+ "link": "Blog post on end-to-end encryption in Excalidraw"
},
"stats": {
"angle": "Angle",
diff --git a/src/locales/es-ES.json b/src/locales/es-ES.json
index 574d42ed5..3b0574dc9 100644
--- a/src/locales/es-ES.json
+++ b/src/locales/es-ES.json
@@ -61,7 +61,7 @@
"architect": "Arquitecto",
"artist": "Artista",
"cartoonist": "Caricatura",
- "fileTitle": "Título del archivo",
+ "fileTitle": "Nombre del archivo",
"colorPicker": "Selector de color",
"canvasBackground": "Fondo del lienzo",
"drawingCanvas": "Lienzo de dibujo",
@@ -92,6 +92,8 @@
"centerHorizontally": "Centrar horizontalmente",
"distributeHorizontally": "Distribuir horizontalmente",
"distributeVertically": "Distribuir verticalmente",
+ "flipHorizontal": "Girar horizontalmente",
+ "flipVertical": "Girar verticalmente",
"viewMode": "Modo presentación",
"toggleExportColorScheme": "Cambiar el esquema de colores de exportación",
"share": "Compartir"
@@ -228,7 +230,8 @@
"zoomToSelection": "Hacer zoom a la selección"
},
"encrypted": {
- "tooltip": "Tus dibujos están cifrados de punto a punto, por lo que los servidores de Excalidraw nunca los verán."
+ "tooltip": "Tus dibujos están cifrados de punto a punto, por lo que los servidores de Excalidraw nunca los verán.",
+ "link": ""
},
"stats": {
"angle": "Ángulo",
diff --git a/src/locales/fa-IR.json b/src/locales/fa-IR.json
index a397ff8f6..f6d539897 100644
--- a/src/locales/fa-IR.json
+++ b/src/locales/fa-IR.json
@@ -61,7 +61,7 @@
"architect": "معمار",
"artist": "هنرمند",
"cartoonist": "کارتونیست",
- "fileTitle": "عنوان فایل",
+ "fileTitle": "",
"colorPicker": "انتخابگر رنگ",
"canvasBackground": "بوم",
"drawingCanvas": "بوم نقاشی",
@@ -92,6 +92,8 @@
"centerHorizontally": "وسط قرار دادن به صورت افقی",
"distributeHorizontally": "توزیع کردن به صورت افقی",
"distributeVertically": "توزیع کردن به صورت عمودی",
+ "flipHorizontal": "",
+ "flipVertical": "",
"viewMode": "",
"toggleExportColorScheme": "",
"share": ""
@@ -228,7 +230,8 @@
"zoomToSelection": "بزرگنمایی قسمت انتخاب شده"
},
"encrypted": {
- "tooltip": "شما در یک محیط رمزگزاری شده دو طرفه در حال طراحی هستید پس Excalidraw هرگز طرح های شما را نمیبند."
+ "tooltip": "شما در یک محیط رمزگزاری شده دو طرفه در حال طراحی هستید پس Excalidraw هرگز طرح های شما را نمیبند.",
+ "link": ""
},
"stats": {
"angle": "زاویه",
diff --git a/src/locales/fi-FI.json b/src/locales/fi-FI.json
index b64df1c1f..50aaf0bca 100644
--- a/src/locales/fi-FI.json
+++ b/src/locales/fi-FI.json
@@ -61,7 +61,7 @@
"architect": "Arkkitehti",
"artist": "Taiteilija",
"cartoonist": "Sarjakuva",
- "fileTitle": "Tiedoston otsikko",
+ "fileTitle": "Tiedostonimi",
"colorPicker": "Värin valinta",
"canvasBackground": "Piirtoalueen tausta",
"drawingCanvas": "Piirtoalue",
@@ -92,6 +92,8 @@
"centerHorizontally": "Keskitä vaakasuunnassa",
"distributeHorizontally": "Jaa vaakasuunnassa",
"distributeVertically": "Jaa pystysuunnassa",
+ "flipHorizontal": "Käännä vaakasuunnassa",
+ "flipVertical": "Käännä pystysuunnassa",
"viewMode": "Katselutila",
"toggleExportColorScheme": "Vaihda viennin väriteema",
"share": "Jaa"
@@ -228,7 +230,8 @@
"zoomToSelection": "Näytä valinta"
},
"encrypted": {
- "tooltip": "Piirroksesi ovat päästä-päähän-salattuja, joten Excalidrawin palvelimet eivät koskaan näe niitä."
+ "tooltip": "Piirroksesi ovat päästä-päähän-salattuja, joten Excalidrawin palvelimet eivät koskaan näe niitä.",
+ "link": ""
},
"stats": {
"angle": "Kulma",
diff --git a/src/locales/fr-FR.json b/src/locales/fr-FR.json
index 3feaabbf1..fdd502ec9 100644
--- a/src/locales/fr-FR.json
+++ b/src/locales/fr-FR.json
@@ -61,7 +61,7 @@
"architect": "Architecte",
"artist": "Artiste",
"cartoonist": "Caricaturiste",
- "fileTitle": "Titre du fichier",
+ "fileTitle": "Nom du fichier",
"colorPicker": "Sélecteur de couleur",
"canvasBackground": "Arrière-plan du canevas",
"drawingCanvas": "Zone de dessin",
@@ -92,6 +92,8 @@
"centerHorizontally": "Centrer horizontalement",
"distributeHorizontally": "Distribuer horizontalement",
"distributeVertically": "Distribuer verticalement",
+ "flipHorizontal": "Retourner horizontalement",
+ "flipVertical": "Retourner verticalement",
"viewMode": "Mode présentation",
"toggleExportColorScheme": "Activer/Désactiver l'export du thème de couleur",
"share": "Partager"
@@ -228,7 +230,8 @@
"zoomToSelection": "Zoomer sur la sélection"
},
"encrypted": {
- "tooltip": "Vos dessins sont chiffrés de bout en bout, les serveurs d'Excalidraw ne les verront jamais."
+ "tooltip": "Vos dessins sont chiffrés de bout en bout, les serveurs d'Excalidraw ne les verront jamais.",
+ "link": "Article de blog sur le chiffrement de bout en bout dans Excalidraw"
},
"stats": {
"angle": "Angle",
diff --git a/src/locales/he-IL.json b/src/locales/he-IL.json
index 6667b7618..2a16f6807 100644
--- a/src/locales/he-IL.json
+++ b/src/locales/he-IL.json
@@ -61,7 +61,7 @@
"architect": "ארכיטקט",
"artist": "אמן",
"cartoonist": "קריקטוריסט",
- "fileTitle": "כותרת הקובץ",
+ "fileTitle": "",
"colorPicker": "בחירת צבע",
"canvasBackground": "רקע הלוח",
"drawingCanvas": "לוח ציור",
@@ -92,6 +92,8 @@
"centerHorizontally": "מרכז אופקית",
"distributeHorizontally": "חלוקה אופקית",
"distributeVertically": "חלוקה אנכית",
+ "flipHorizontal": "",
+ "flipVertical": "",
"viewMode": "מצב תצוגה",
"toggleExportColorScheme": "",
"share": ""
@@ -228,7 +230,8 @@
"zoomToSelection": "התמקד בבחירה"
},
"encrypted": {
- "tooltip": "הרישומים שלך מוצפנים מקצה לקצה כך שהשרתים של Excalidraw לא יראו אותם לעולם."
+ "tooltip": "הרישומים שלך מוצפנים מקצה לקצה כך שהשרתים של Excalidraw לא יראו אותם לעולם.",
+ "link": ""
},
"stats": {
"angle": "זווית",
diff --git a/src/locales/hi-IN.json b/src/locales/hi-IN.json
index a315c2589..8f5a47cad 100644
--- a/src/locales/hi-IN.json
+++ b/src/locales/hi-IN.json
@@ -61,7 +61,7 @@
"architect": "वास्तुकार",
"artist": "कलाकार",
"cartoonist": "व्यंग्य चित्रकार",
- "fileTitle": "फ़ाइल का शीर्षक",
+ "fileTitle": "फ़ाइल का नाम",
"colorPicker": "रंग चयन",
"canvasBackground": "कैनवास बैकग्राउंड",
"drawingCanvas": "कैनवास बना रहे हैं",
@@ -92,6 +92,8 @@
"centerHorizontally": "क्षैतिज केन्द्रित",
"distributeHorizontally": "क्षैतिज रूप से वितरित करें",
"distributeVertically": "खड़ी रूप से वितरित करें",
+ "flipHorizontal": "",
+ "flipVertical": "",
"viewMode": "",
"toggleExportColorScheme": "",
"share": ""
@@ -228,7 +230,8 @@
"zoomToSelection": "चयन तक ज़ूम करे"
},
"encrypted": {
- "tooltip": "आपके चित्र अंत-से-अंत एन्क्रिप्टेड हैं, इसलिए एक्सक्लूसिव्रॉव के सर्वर उन्हें कभी नहीं देखेंगे।"
+ "tooltip": "आपके चित्र अंत-से-अंत एन्क्रिप्टेड हैं, इसलिए एक्सक्लूसिव्रॉव के सर्वर उन्हें कभी नहीं देखेंगे।",
+ "link": ""
},
"stats": {
"angle": "कोण",
diff --git a/src/locales/hu-HU.json b/src/locales/hu-HU.json
index 058a19bde..e33fa14ad 100644
--- a/src/locales/hu-HU.json
+++ b/src/locales/hu-HU.json
@@ -61,7 +61,7 @@
"architect": "Tervezői",
"artist": "Művészi",
"cartoonist": "Karikatúrás",
- "fileTitle": "Fájl címe",
+ "fileTitle": "",
"colorPicker": "Színválasztó",
"canvasBackground": "Vászon háttérszíne",
"drawingCanvas": "Rajzvászon",
@@ -92,6 +92,8 @@
"centerHorizontally": "Vízszintesen középre igazított",
"distributeHorizontally": "Vízszintes elosztás",
"distributeVertically": "Függőleges elosztás",
+ "flipHorizontal": "",
+ "flipVertical": "",
"viewMode": "",
"toggleExportColorScheme": "",
"share": ""
@@ -228,7 +230,8 @@
"zoomToSelection": ""
},
"encrypted": {
- "tooltip": "A rajzaidat végpontok közötti titkosítással tároljuk, tehát az Excalidraw szervereiről se tud más belenézni."
+ "tooltip": "A rajzaidat végpontok közötti titkosítással tároljuk, tehát az Excalidraw szervereiről se tud más belenézni.",
+ "link": ""
},
"stats": {
"angle": "Szög",
diff --git a/src/locales/id-ID.json b/src/locales/id-ID.json
index 73e8cc694..284b436bf 100644
--- a/src/locales/id-ID.json
+++ b/src/locales/id-ID.json
@@ -61,7 +61,7 @@
"architect": "Arsitek",
"artist": "Artis",
"cartoonist": "Kartunis",
- "fileTitle": "Judul file",
+ "fileTitle": "Nama file",
"colorPicker": "Pilihan Warna",
"canvasBackground": "Latar Kanvas",
"drawingCanvas": "Kanvas",
@@ -92,6 +92,8 @@
"centerHorizontally": "Pusatkan secara horizontal",
"distributeHorizontally": "Distribusikan horizontal",
"distributeVertically": "Distribusikan vertikal",
+ "flipHorizontal": "Balikkan horizontal",
+ "flipVertical": "Balikkan vertikal",
"viewMode": "Mode tampilan",
"toggleExportColorScheme": "Ubah skema warna ekspor",
"share": "Bagikan"
@@ -143,7 +145,7 @@
"confirmAddLibrary": "Ini akan menambahkan {{numShapes}} bentuk ke pustaka Anda. Anda yakin?",
"imageDoesNotContainScene": "Mengimpor gambar tidak didukung saat ini.\n\nApakah Anda ingin impor pemandangan? Gambar ini tidak berisi data pemandangan. Sudah ka Anda aktifkan ini ketika ekspor?",
"cannotRestoreFromImage": "Pemandangan tidak dapat dipulihkan dari file gambar ini",
- "invalidSceneUrl": "",
+ "invalidSceneUrl": "Tidak dapat impor pemandangan dari URL. Kemungkinan URL itu rusak atau tidak berisi data JSON Excalidraw yang valid.",
"resetLibrary": "Ini akan menghapus pustaka Anda. Anda yakin?"
},
"toolBar": {
@@ -228,7 +230,8 @@
"zoomToSelection": "Perbesar ke seleksi"
},
"encrypted": {
- "tooltip": "Gambar anda terenkripsi end-to-end sehingga server Excalidraw tidak akan pernah dapat melihatnya."
+ "tooltip": "Gambar anda terenkripsi end-to-end sehingga server Excalidraw tidak akan pernah dapat melihatnya.",
+ "link": "Pos blog tentang enkripsi ujung ke ujung di Excalidraw"
},
"stats": {
"angle": "Sudut",
diff --git a/src/locales/it-IT.json b/src/locales/it-IT.json
index 33d581a67..2d35caf6d 100644
--- a/src/locales/it-IT.json
+++ b/src/locales/it-IT.json
@@ -61,7 +61,7 @@
"architect": "Architetto",
"artist": "Artista",
"cartoonist": "Fumettista",
- "fileTitle": "Titolo del file",
+ "fileTitle": "Nome del file",
"colorPicker": "Selettore colore",
"canvasBackground": "Sfondo tela",
"drawingCanvas": "Area di disegno",
@@ -92,6 +92,8 @@
"centerHorizontally": "Centra orizzontalmente",
"distributeHorizontally": "Distribuisci orizzontalmente",
"distributeVertically": "Distribuisci verticalmente",
+ "flipHorizontal": "Capovolgi orizzontalmente",
+ "flipVertical": "Capovolgi verticalmente",
"viewMode": "Modalità visualizzazione",
"toggleExportColorScheme": "Cambia lo schema di colori in esportazione",
"share": "Condividi"
@@ -228,7 +230,8 @@
"zoomToSelection": "Zoom alla selezione"
},
"encrypted": {
- "tooltip": "I tuoi disegni sono crittografati end-to-end in modo che i server di Excalidraw non li possano mai vedere."
+ "tooltip": "I tuoi disegni sono crittografati end-to-end in modo che i server di Excalidraw non li possano mai vedere.",
+ "link": "Articolo del blog sulla crittografia end-to-end di Excalidraw"
},
"stats": {
"angle": "Angolo",
diff --git a/src/locales/ja-JP.json b/src/locales/ja-JP.json
index b63bb7657..530e89379 100644
--- a/src/locales/ja-JP.json
+++ b/src/locales/ja-JP.json
@@ -92,6 +92,8 @@
"centerHorizontally": "横方向に中央揃え",
"distributeHorizontally": "水平方向に分散配置",
"distributeVertically": "垂直方向に分散配置",
+ "flipHorizontal": "水平方向に反転",
+ "flipVertical": "垂直方向に反転",
"viewMode": "閲覧モード",
"toggleExportColorScheme": "",
"share": ""
@@ -201,7 +203,7 @@
"desc_inProgressIntro": "共同編集セッションが有効になっています。",
"desc_shareLink": "下記URLを共同編集したい人に共有してください:",
"desc_exitSession": "セッションを終了すると部屋から切断されますが、手元の環境で編集を続けることができます。変更内容は他の人には反映されません。",
- "shareTitle": ""
+ "shareTitle": "Excalidrawのライブコラボレーションセッションに参加する"
},
"errorDialog": {
"title": "エラー"
@@ -228,7 +230,8 @@
"zoomToSelection": "選択要素にズーム"
},
"encrypted": {
- "tooltip": "描画内容はエンドツーエンド暗号化が施されており、Excalidrawサーバーが内容を見ることはできません。"
+ "tooltip": "描画内容はエンドツーエンド暗号化が施されており、Excalidrawサーバーが内容を見ることはできません。",
+ "link": "Excalidrawのエンドツーエンド暗号化に関するブログ記事"
},
"stats": {
"angle": "角度",
@@ -251,7 +254,7 @@
"copyToClipboardAsPng": "",
"fileSaved": "ファイルを保存しました",
"fileSavedToFilename": "{filename} に保存しました",
- "canvas": "",
+ "canvas": "キャンバス",
"selection": ""
}
}
diff --git a/src/locales/kab-KAB.json b/src/locales/kab-KAB.json
index cef9fad1c..9c6978057 100644
--- a/src/locales/kab-KAB.json
+++ b/src/locales/kab-KAB.json
@@ -61,7 +61,7 @@
"architect": "Amasdag",
"artist": "Anaẓur",
"cartoonist": "",
- "fileTitle": "Azwel n ufaylu",
+ "fileTitle": "Isem n ufaylu",
"colorPicker": "Amafran n yini",
"canvasBackground": "Agilal n teɣzut n usuneɣ",
"drawingCanvas": "Taɣzut n usuneɣ",
@@ -92,6 +92,8 @@
"centerHorizontally": "Di tlemmast s uglawi",
"distributeHorizontally": "Freq s uglawi",
"distributeVertically": "Freq s yibeddi",
+ "flipHorizontal": "Tuttya taglawant",
+ "flipVertical": "Tuttya tubdidt",
"viewMode": "Askar n tmuɣli",
"toggleExportColorScheme": "Sermed/sens asifeḍ usentel n yini",
"share": "Bḍu"
@@ -228,7 +230,8 @@
"zoomToSelection": "Simɣur ɣer tefrayt"
},
"encrypted": {
- "tooltip": "Unuɣen-inek (m) ttuwgelhnen seg yixef s ixef dɣa iqeddacen n Excalidraw werǧin ad ten-walin. "
+ "tooltip": "Unuɣen-inek (m) ttuwgelhnen seg yixef s ixef dɣa iqeddacen n Excalidraw werǧin ad ten-walin. ",
+ "link": "Amagrad ɣef uwgelhen ixef s ixef di Excalidraw"
},
"stats": {
"angle": "Tiɣmeṛt",
diff --git a/src/locales/ko-KR.json b/src/locales/ko-KR.json
index b8c827260..ab769eae5 100644
--- a/src/locales/ko-KR.json
+++ b/src/locales/ko-KR.json
@@ -61,7 +61,7 @@
"architect": "건축가",
"artist": "예술가",
"cartoonist": "만화가",
- "fileTitle": "파일명",
+ "fileTitle": "",
"colorPicker": "색상 선택기",
"canvasBackground": "캔버스 배경",
"drawingCanvas": "캔버스 그리기",
@@ -92,6 +92,8 @@
"centerHorizontally": "수평으로 중앙 정렬",
"distributeHorizontally": "수평으로 분배",
"distributeVertically": "수직으로 분배",
+ "flipHorizontal": "",
+ "flipVertical": "",
"viewMode": "보기 모드",
"toggleExportColorScheme": "",
"share": ""
@@ -228,7 +230,8 @@
"zoomToSelection": "선택 영역으로 확대/축소"
},
"encrypted": {
- "tooltip": "그림은 종단 간 암호화되므로 Excalidraw의 서버는 절대로 내용을 알 수 없습니다."
+ "tooltip": "그림은 종단 간 암호화되므로 Excalidraw의 서버는 절대로 내용을 알 수 없습니다.",
+ "link": ""
},
"stats": {
"angle": "각도",
diff --git a/src/locales/my-MM.json b/src/locales/my-MM.json
index 4a10e86fe..02bb64ec4 100644
--- a/src/locales/my-MM.json
+++ b/src/locales/my-MM.json
@@ -61,7 +61,7 @@
"architect": "ဗိသုကာ",
"artist": "ပန်းချီ",
"cartoonist": "ကာတွန်း",
- "fileTitle": "ခေါင်းစဉ်",
+ "fileTitle": "",
"colorPicker": "အရောင်ရွေး",
"canvasBackground": "ကားချပ်နောက်ခံ",
"drawingCanvas": "ပုံဆွဲကားချပ်",
@@ -92,6 +92,8 @@
"centerHorizontally": "အလျားလိုက်အလယ်ညှိ",
"distributeHorizontally": "အလျားလိုက်",
"distributeVertically": "ထောင်လိုက်",
+ "flipHorizontal": "",
+ "flipVertical": "",
"viewMode": "",
"toggleExportColorScheme": "",
"share": ""
@@ -228,7 +230,8 @@
"zoomToSelection": ""
},
"encrypted": {
- "tooltip": "ရေးဆွဲထားသောပုံများအား နှစ်ဘက်စွန်းတိုင်လျှို့ဝှက်ထားသဖြင့် Excalidraw ၏ဆာဗာများပင်လျှင်မြင်တွေ့ရမည်မဟုတ်ပါ။"
+ "tooltip": "ရေးဆွဲထားသောပုံများအား နှစ်ဘက်စွန်းတိုင်လျှို့ဝှက်ထားသဖြင့် Excalidraw ၏ဆာဗာများပင်လျှင်မြင်တွေ့ရမည်မဟုတ်ပါ။",
+ "link": ""
},
"stats": {
"angle": "ထောင့်",
diff --git a/src/locales/nb-NO.json b/src/locales/nb-NO.json
index 03fd5af00..0a4398b58 100644
--- a/src/locales/nb-NO.json
+++ b/src/locales/nb-NO.json
@@ -92,6 +92,8 @@
"centerHorizontally": "Midtstill horisontalt",
"distributeHorizontally": "Distribuer horisontalt",
"distributeVertically": "Distribuer vertikalt",
+ "flipHorizontal": "Snu horisontalt",
+ "flipVertical": "Snu vertikalt",
"viewMode": "Visningsmodus",
"toggleExportColorScheme": "Veksle eksport av fargepalett",
"share": "Del"
@@ -228,7 +230,8 @@
"zoomToSelection": "Zoom til utvalg"
},
"encrypted": {
- "tooltip": "Dine tegninger er ende-til-ende-krypterte slik at Excalidraw sine servere aldri vil se dem."
+ "tooltip": "Dine tegninger er ende-til-ende-krypterte slik at Excalidraw sine servere aldri vil se dem.",
+ "link": "Blogginnlegg om ende-til-ende-kryptering i Excalidraw"
},
"stats": {
"angle": "Vinkel",
diff --git a/src/locales/nl-NL.json b/src/locales/nl-NL.json
index eac1738c3..28971d47d 100644
--- a/src/locales/nl-NL.json
+++ b/src/locales/nl-NL.json
@@ -92,6 +92,8 @@
"centerHorizontally": "Horizontaal Centreren",
"distributeHorizontally": "Horizontaal verspreiden",
"distributeVertically": "Verticaal distribueren",
+ "flipHorizontal": "Horizontaal spiegelen",
+ "flipVertical": "Verticaal spiegelen",
"viewMode": "Weergavemodus",
"toggleExportColorScheme": "Kleurenschema exporteren aan/uit",
"share": "Deel"
@@ -138,7 +140,7 @@
"decryptFailed": "Kan gegevens niet decoderen.",
"uploadedSecurly": "De upload is beveiligd met end-to-end encryptie, wat betekent dat de Excalidraw server en derden de inhoud niet kunnen lezen.",
"loadSceneOverridePrompt": "Het laden van externe tekening zal uw bestaande inhoud vervangen. Wil je doorgaan?",
- "collabStopOverridePrompt": "",
+ "collabStopOverridePrompt": "Wanneer de sessie wordt gestopt, overschrijft u de eerdere, lokaal opgeslagen tekening. Weet je het zeker?\n\n(Als je de lokale tekening wilt behouden, sluit je in plaats daarvan het browsertabblad)",
"errorLoadingLibrary": "Bij het laden van de externe bibliotheek is een fout opgetreden.",
"confirmAddLibrary": "Hiermee worden {{numShapes}} vorm(n) aan uw bibliotheek toegevoegd. Ben je het zeker?",
"imageDoesNotContainScene": "Afbeeldingen importeren wordt op dit moment niet ondersteund.\n\nWil je een scène importeren? Deze afbeelding lijkt geen scène gegevens te bevatten. Heb je dit geactiveerd tijdens het exporteren?",
@@ -228,7 +230,8 @@
"zoomToSelection": "Inzoomen op selectie"
},
"encrypted": {
- "tooltip": "Je tekeningen zijn beveiligd met end-to-end encryptie, dus Excalidraw's servers zullen nooit zien wat je tekent."
+ "tooltip": "Je tekeningen zijn beveiligd met end-to-end encryptie, dus Excalidraw's servers zullen nooit zien wat je tekent.",
+ "link": "Blog post over end-to-end versleuteling in Excalidraw"
},
"stats": {
"angle": "Hoek",
diff --git a/src/locales/nn-NO.json b/src/locales/nn-NO.json
index 5c2fa1df9..4364aec98 100644
--- a/src/locales/nn-NO.json
+++ b/src/locales/nn-NO.json
@@ -61,7 +61,7 @@
"architect": "Arkitekt",
"artist": "Kunstnar",
"cartoonist": "Teiknar",
- "fileTitle": "Filnamn",
+ "fileTitle": "",
"colorPicker": "Fargeveljar",
"canvasBackground": "Lerretsbakgrunn",
"drawingCanvas": "Lerret",
@@ -92,6 +92,8 @@
"centerHorizontally": "Midtstill horisontalt",
"distributeHorizontally": "Sprei horisontalt",
"distributeVertically": "Sprei vertikalt",
+ "flipHorizontal": "",
+ "flipVertical": "",
"viewMode": "",
"toggleExportColorScheme": "",
"share": ""
@@ -228,7 +230,8 @@
"zoomToSelection": ""
},
"encrypted": {
- "tooltip": "Teikningane dine er ende-til-ende-krypterte slik at Excalidraw sine serverar aldri får sjå dei."
+ "tooltip": "Teikningane dine er ende-til-ende-krypterte slik at Excalidraw sine serverar aldri får sjå dei.",
+ "link": ""
},
"stats": {
"angle": "Vinkel",
diff --git a/src/locales/oc-FR.json b/src/locales/oc-FR.json
index eb6ec841f..c192cefbc 100644
--- a/src/locales/oc-FR.json
+++ b/src/locales/oc-FR.json
@@ -38,7 +38,7 @@
"fontSize": "Talha poliça",
"fontFamily": "Familha de poliça",
"onlySelected": "Seleccion sonque",
- "withBackground": "Inclure rèireplan",
+ "withBackground": "Inclure lo rèireplan",
"exportEmbedScene": "Integrar la scèna al fichièr d’expo",
"exportEmbedScene_details": "Las donadas de scèna seràn enregistradas dins lo fichièr PNG/SVG exportat, per que la scèna pòsca èsser restaurada a partir d’aqueste fichièr.\nAumentarà la talha del fichièr exportat.",
"addWatermark": "Apondre « Fabricat amb Excalidraw »",
@@ -61,7 +61,7 @@
"architect": "Arquitècte",
"artist": "Artista",
"cartoonist": "Dessenhaire",
- "fileTitle": "Títol del fichièr",
+ "fileTitle": "Nom del fichièr",
"colorPicker": "Selector de color",
"canvasBackground": "Rèireplan del canabàs",
"drawingCanvas": "Zòna de dessenh",
@@ -92,6 +92,8 @@
"centerHorizontally": "Centrar orizontalament",
"distributeHorizontally": "Distribuir orizontalament",
"distributeVertically": "Distribuir verticalament",
+ "flipHorizontal": "Virar orizontalament",
+ "flipVertical": "Virar verticalament",
"viewMode": "Mòde de vista",
"toggleExportColorScheme": "Alternar l’esquèma de color d’expòrt",
"share": "Partejar"
@@ -99,8 +101,8 @@
"buttons": {
"clearReset": "Reïnicializar lo canabàs",
"export": "Exportar",
- "exportToPng": "Export en PNG",
- "exportToSvg": "Export en SVG",
+ "exportToPng": "Exportar en PNG",
+ "exportToSvg": "Exportar en SVG",
"copyToClipboard": "Copiar al quichapapièrs",
"copyPngToClipboard": "Copiar PNG al quichapapièrs",
"scale": "Escala",
@@ -228,7 +230,8 @@
"zoomToSelection": "Zoomar la seleccion"
},
"encrypted": {
- "tooltip": "Vòstres dessenhs son chifrats del cap a la fin en consequéncia los servidors d’Excalidraw los veiràn pas jamai."
+ "tooltip": "Vòstres dessenhs son chifrats del cap a la fin en consequéncia los servidors d’Excalidraw los veiràn pas jamai.",
+ "link": "Article de blòg sul chiframent del cap a la fin dins Excalidraw"
},
"stats": {
"angle": "Angle",
diff --git a/src/locales/pa-IN.json b/src/locales/pa-IN.json
index f437cd40d..3b335cbff 100644
--- a/src/locales/pa-IN.json
+++ b/src/locales/pa-IN.json
@@ -61,7 +61,7 @@
"architect": "ਭਵਨ ਨਿਰਮਾਣਕਾਰੀ",
"artist": "ਕਲਾਕਾਰ",
"cartoonist": "ਕਾਰਟੂਨਿਸਟ",
- "fileTitle": "ਫਾਈਲ ਦਾ ਸਿਰਨਾਵਾਂ",
+ "fileTitle": "",
"colorPicker": "ਰੰਗ ਚੋਣਕਾਰ",
"canvasBackground": "ਕੈਨਵਸ ਦਾ ਬੈਕਗਰਾਉਂਡ",
"drawingCanvas": "ਡਰਾਇੰਗ ਕੈਨਵਸ",
@@ -92,6 +92,8 @@
"centerHorizontally": "ਖੜ੍ਹਵੇਂ ਵਿਚਕਾਰ ਕਰੋ",
"distributeHorizontally": "ਖੜ੍ਹਵੇਂ ਇਕਸਾਰ ਵੰਡੋ",
"distributeVertically": "ਲੇਟਵੇਂ ਇਕਸਾਰ ਵੰਡੋ",
+ "flipHorizontal": "",
+ "flipVertical": "",
"viewMode": "ਦੇਖੋ ਮੋਡ",
"toggleExportColorScheme": "",
"share": ""
@@ -228,7 +230,8 @@
"zoomToSelection": "ਚੋਣ ਤੱਕ ਜ਼ੂਮ ਕਰੋ"
},
"encrypted": {
- "tooltip": "ਤੁਹਾਡੀ ਡਰਾਇੰਗਾਂ ਸਿਰੇ-ਤੋਂ-ਸਿਰੇ ਤੱਕ ਇਨਕਰਿਪਟ ਕੀਤੀਆਂ ਹੋਈਆਂ ਹਨ, ਇਸ ਲਈ Excalidraw ਦੇ ਸਰਵਰ ਉਹਨਾਂ ਨੂੰ ਕਦੇ ਵੀ ਨਹੀਂ ਦੇਖਣਗੇ।"
+ "tooltip": "ਤੁਹਾਡੀ ਡਰਾਇੰਗਾਂ ਸਿਰੇ-ਤੋਂ-ਸਿਰੇ ਤੱਕ ਇਨਕਰਿਪਟ ਕੀਤੀਆਂ ਹੋਈਆਂ ਹਨ, ਇਸ ਲਈ Excalidraw ਦੇ ਸਰਵਰ ਉਹਨਾਂ ਨੂੰ ਕਦੇ ਵੀ ਨਹੀਂ ਦੇਖਣਗੇ।",
+ "link": ""
},
"stats": {
"angle": "ਕੋਣ",
diff --git a/src/locales/percentages.json b/src/locales/percentages.json
index 6752bbafd..684345115 100644
--- a/src/locales/percentages.json
+++ b/src/locales/percentages.json
@@ -1,37 +1,37 @@
{
- "ar-SA": 83,
- "bg-BG": 94,
- "ca-ES": 100,
+ "ar-SA": 85,
+ "bg-BG": 93,
+ "ca-ES": 98,
"de-DE": 100,
"el-GR": 98,
"en": 100,
- "es-ES": 100,
- "fa-IR": 90,
- "fi-FI": 100,
+ "es-ES": 99,
+ "fa-IR": 88,
+ "fi-FI": 99,
"fr-FR": 100,
- "he-IL": 91,
- "hi-IN": 93,
- "hu-HU": 83,
- "id-ID": 99,
+ "he-IL": 89,
+ "hi-IN": 91,
+ "hu-HU": 81,
+ "id-ID": 100,
"it-IT": 100,
- "ja-JP": 96,
+ "ja-JP": 97,
"kab-KAB": 99,
- "ko-KR": 94,
- "my-MM": 77,
+ "ko-KR": 92,
+ "my-MM": 76,
"nb-NO": 100,
- "nl-NL": 99,
- "nn-NO": 85,
+ "nl-NL": 100,
+ "nn-NO": 83,
"oc-FR": 100,
- "pa-IN": 95,
- "pl-PL": 96,
+ "pa-IN": 94,
+ "pl-PL": 95,
"pt-BR": 100,
- "pt-PT": 97,
+ "pt-PT": 95,
"ro-RO": 100,
- "ru-RU": 100,
- "sk-SK": 100,
+ "ru-RU": 98,
+ "sk-SK": 99,
"sv-SE": 100,
- "tr-TR": 83,
- "uk-UA": 95,
- "zh-CN": 100,
- "zh-TW": 100
+ "tr-TR": 99,
+ "uk-UA": 99,
+ "zh-CN": 99,
+ "zh-TW": 99
}
diff --git a/src/locales/pl-PL.json b/src/locales/pl-PL.json
index 10fe86d7b..2e810c884 100644
--- a/src/locales/pl-PL.json
+++ b/src/locales/pl-PL.json
@@ -61,7 +61,7 @@
"architect": "Dokładny",
"artist": "Artystyczny",
"cartoonist": "Rysunkowy",
- "fileTitle": "Tytuł pliku",
+ "fileTitle": "",
"colorPicker": "Paleta kolorów",
"canvasBackground": "Kolor dokumentu",
"drawingCanvas": "Obszar roboczy",
@@ -92,6 +92,8 @@
"centerHorizontally": "Wyśrodkuj w poziomie",
"distributeHorizontally": "Rozłóż poziomo",
"distributeVertically": "Rozłóż pionowo",
+ "flipHorizontal": "",
+ "flipVertical": "",
"viewMode": "Tryb widoku",
"toggleExportColorScheme": "",
"share": ""
@@ -228,7 +230,8 @@
"zoomToSelection": "Przybliż do zaznaczenia"
},
"encrypted": {
- "tooltip": "Twoje rysunki są zabezpieczone szyfrowaniem end-to-end, tak więc nawet w Excalidraw nie jesteśmy w stanie zobaczyć tego co tworzysz."
+ "tooltip": "Twoje rysunki są zabezpieczone szyfrowaniem end-to-end, tak więc nawet w Excalidraw nie jesteśmy w stanie zobaczyć tego co tworzysz.",
+ "link": ""
},
"stats": {
"angle": "Kąt",
diff --git a/src/locales/pt-BR.json b/src/locales/pt-BR.json
index 0c90435e0..1bf8d3cbf 100644
--- a/src/locales/pt-BR.json
+++ b/src/locales/pt-BR.json
@@ -61,7 +61,7 @@
"architect": "Arquiteto",
"artist": "Artista",
"cartoonist": "Cartunista",
- "fileTitle": "Título do arquivo",
+ "fileTitle": "Nome do arquivo",
"colorPicker": "Seletor de cores",
"canvasBackground": "Fundo da tela",
"drawingCanvas": "Tela de desenho",
@@ -92,6 +92,8 @@
"centerHorizontally": "Centralizar horizontalmente",
"distributeHorizontally": "Distribuir horizontalmente",
"distributeVertically": "Distribuir verticalmente",
+ "flipHorizontal": "Inverter horizontalmente",
+ "flipVertical": "Inverter verticalmente",
"viewMode": "Modo de visualização",
"toggleExportColorScheme": "Alternar esquema de cores de exportação",
"share": "Compartilhar"
@@ -228,7 +230,8 @@
"zoomToSelection": "Ampliar a seleção"
},
"encrypted": {
- "tooltip": "Seus desenhos são criptografados de ponta a ponta, então os servidores do Excalidraw nunca os verão."
+ "tooltip": "Seus desenhos são criptografados de ponta a ponta, então os servidores do Excalidraw nunca os verão.",
+ "link": "Postagem de blog com uma criptografia de ponta a ponta no Excalidraw"
},
"stats": {
"angle": "Ângulo",
diff --git a/src/locales/pt-PT.json b/src/locales/pt-PT.json
index 47bdd9496..8994d1fc5 100644
--- a/src/locales/pt-PT.json
+++ b/src/locales/pt-PT.json
@@ -61,7 +61,7 @@
"architect": "Arquitecto",
"artist": "Artista",
"cartoonist": "Caricaturista",
- "fileTitle": "Título do ficheiro",
+ "fileTitle": "",
"colorPicker": "Seletor de cores",
"canvasBackground": "Fundo da tela",
"drawingCanvas": "Tela de desenho",
@@ -92,6 +92,8 @@
"centerHorizontally": "Centralizar horizontalmente",
"distributeHorizontally": "Distribuir horizontalmente",
"distributeVertically": "Distribuir verticalmente",
+ "flipHorizontal": "",
+ "flipVertical": "",
"viewMode": "Modo de visualização",
"toggleExportColorScheme": "Alternar esquema de cores de exportação",
"share": ""
@@ -228,7 +230,8 @@
"zoomToSelection": "Ampliar a seleção"
},
"encrypted": {
- "tooltip": "Seus desenhos são criptografados de ponta a ponta, então os servidores do Excalidraw nunca os verão."
+ "tooltip": "Seus desenhos são criptografados de ponta a ponta, então os servidores do Excalidraw nunca os verão.",
+ "link": ""
},
"stats": {
"angle": "Ângulo",
diff --git a/src/locales/ro-RO.json b/src/locales/ro-RO.json
index 4092c40f6..e07913560 100644
--- a/src/locales/ro-RO.json
+++ b/src/locales/ro-RO.json
@@ -61,7 +61,7 @@
"architect": "Arhitect",
"artist": "Artist",
"cartoonist": "Caricaturist",
- "fileTitle": "Denumirea fișierului",
+ "fileTitle": "Nume de fișier",
"colorPicker": "Selector de culoare",
"canvasBackground": "Fundalul pânzei",
"drawingCanvas": "Pânză pentru desenat",
@@ -92,6 +92,8 @@
"centerHorizontally": "Centrare orizontală",
"distributeHorizontally": "Distribuie orizontal",
"distributeVertically": "Distribuie vertical",
+ "flipHorizontal": "Răsturnare orizontală",
+ "flipVertical": "Răsturnare verticală",
"viewMode": "Mod de vizualizare",
"toggleExportColorScheme": "Comutare schemă de culori de export",
"share": "Distribuie"
@@ -228,7 +230,8 @@
"zoomToSelection": "Panoramare la selecție"
},
"encrypted": {
- "tooltip": "Desenele tale sunt criptate integral, astfel că serverele Excalidraw nu le vor vedea niciodată."
+ "tooltip": "Desenele tale sunt criptate integral, astfel că serverele Excalidraw nu le vor vedea niciodată.",
+ "link": "Articol de blog pe criptarea integrală din Excalidraw"
},
"stats": {
"angle": "Unghi",
diff --git a/src/locales/ru-RU.json b/src/locales/ru-RU.json
index 9135ebbdd..bcda30d47 100644
--- a/src/locales/ru-RU.json
+++ b/src/locales/ru-RU.json
@@ -61,7 +61,7 @@
"architect": "Архитектор",
"artist": "Художник",
"cartoonist": "Карикатурист",
- "fileTitle": "Название файла",
+ "fileTitle": "Имя файла",
"colorPicker": "Выбор цвета",
"canvasBackground": "Фон холста",
"drawingCanvas": "Полотно",
@@ -92,6 +92,8 @@
"centerHorizontally": "Центрировать по горизонтали",
"distributeHorizontally": "Распределить по горизонтали",
"distributeVertically": "Распределить по вертикали",
+ "flipHorizontal": "",
+ "flipVertical": "",
"viewMode": "Вид",
"toggleExportColorScheme": "Экспортировать цветовую схему",
"share": "Поделиться"
@@ -228,7 +230,8 @@
"zoomToSelection": "Увеличить до выделенного"
},
"encrypted": {
- "tooltip": "Ваши данные защищены сквозным (End-to-end) шифрованием. Серверы Excalidraw никогда не получат доступ к ним."
+ "tooltip": "Ваши данные защищены сквозным (End-to-end) шифрованием. Серверы Excalidraw никогда не получат доступ к ним.",
+ "link": ""
},
"stats": {
"angle": "Угол",
diff --git a/src/locales/sk-SK.json b/src/locales/sk-SK.json
index b0b95e132..4865b5837 100644
--- a/src/locales/sk-SK.json
+++ b/src/locales/sk-SK.json
@@ -92,6 +92,8 @@
"centerHorizontally": "Zarovnať vodorovne na stred",
"distributeHorizontally": "Rozmiestniť vodorovne",
"distributeVertically": "Rozmiestniť zvisle",
+ "flipHorizontal": "Prevrátiť vodorovne",
+ "flipVertical": "Prevrátiť zvislo",
"viewMode": "Režim zobrazenia",
"toggleExportColorScheme": "Prepnúť exportovanie farebnej schémy",
"share": "Zdieľať"
@@ -228,7 +230,8 @@
"zoomToSelection": "Priblížiť na výber"
},
"encrypted": {
- "tooltip": "Vaše kresby používajú end-to-end šifrovanie, takže ich Excalidraw server nedokáže prečítať."
+ "tooltip": "Vaše kresby používajú end-to-end šifrovanie, takže ich Excalidraw server nedokáže prečítať.",
+ "link": ""
},
"stats": {
"angle": "Uhol",
diff --git a/src/locales/sv-SE.json b/src/locales/sv-SE.json
index 0d338d462..fea3064c6 100644
--- a/src/locales/sv-SE.json
+++ b/src/locales/sv-SE.json
@@ -61,7 +61,7 @@
"architect": "Arkitekt",
"artist": "Artist",
"cartoonist": "Serietecknare",
- "fileTitle": "Filtitel",
+ "fileTitle": "Filnamn",
"colorPicker": "Färgväljare",
"canvasBackground": "Canvas-bakgrund",
"drawingCanvas": "Ritar canvas",
@@ -92,6 +92,8 @@
"centerHorizontally": "Centrera horisontellt",
"distributeHorizontally": "Fördela horisontellt",
"distributeVertically": "Fördela vertikalt",
+ "flipHorizontal": "Vänd horisontellt",
+ "flipVertical": "Vänd vertikalt",
"viewMode": "Visningsläge",
"toggleExportColorScheme": "Växla färgschema för export",
"share": "Dela"
@@ -228,7 +230,8 @@
"zoomToSelection": "Zooma till markering"
},
"encrypted": {
- "tooltip": "Dina skisser är krypterade från ände till ände så Excalidraws servrar kommer aldrig att se dem."
+ "tooltip": "Dina skisser är krypterade från ände till ände så Excalidraws servrar kommer aldrig att se dem.",
+ "link": "Blogginlägg om kryptering från ände till ände i Excalidraw"
},
"stats": {
"angle": "Vinkel",
diff --git a/src/locales/tr-TR.json b/src/locales/tr-TR.json
index 38d03f770..42a6a925f 100644
--- a/src/locales/tr-TR.json
+++ b/src/locales/tr-TR.json
@@ -1,7 +1,7 @@
{
"labels": {
"paste": "Yapıştır",
- "pasteCharts": "Dairesel grafik",
+ "pasteCharts": "Grafikleri yapıştır",
"selectAll": "Tümünü seç",
"multiSelect": "Seçime öge ekle",
"moveCanvas": "Tuvali taşı",
@@ -68,7 +68,7 @@
"layers": "Katmanlar",
"actions": "Eylemler",
"language": "Dil",
- "liveCollaboration": "",
+ "liveCollaboration": "Canlı ortak çalışma alanı",
"duplicateSelection": "Çoğalt",
"untitled": "Adsız",
"name": "İsim",
@@ -77,7 +77,7 @@
"group": "Seçimi grup yap",
"ungroup": "Seçilen grubu dağıt",
"collaborators": "Ortaklar",
- "showGrid": "",
+ "showGrid": "Izgarayı göster",
"addToLibrary": "Kütüphaneye ekle",
"removeFromLibrary": "Kütüphaneden kaldır",
"libraryLoadingMessage": "Kütüphane yükleniyor…",
@@ -92,9 +92,11 @@
"centerHorizontally": "Yatayda ortala",
"distributeHorizontally": "Yatay dağıt",
"distributeVertically": "Dikey dağıt",
- "viewMode": "",
- "toggleExportColorScheme": "",
- "share": ""
+ "flipHorizontal": "Yatay döndür",
+ "flipVertical": "Dikey döndür",
+ "viewMode": "Görünüm modu",
+ "toggleExportColorScheme": "Renk şemasını dışa aktar/aktarma",
+ "share": "Paylaş"
},
"buttons": {
"clearReset": "Tuvali sıfırla",
@@ -119,7 +121,7 @@
"edit": "Düzenle",
"undo": "Geri Al",
"redo": "Yeniden yap",
- "resetLibrary": "",
+ "resetLibrary": "Kütüphaneyi sıfırla",
"createNewRoom": "Yeni oda oluştur",
"fullScreen": "Tam ekran",
"darkMode": "Koyu tema",
@@ -138,13 +140,13 @@
"decryptFailed": "Şifrelenmiş veri çözümlenemedi.",
"uploadedSecurly": "Yükleme uçtan uca şifreleme ile korunmaktadır. Excalidraw sunucusu ve üçüncül şahıslar içeriği okuyamayacaktır.",
"loadSceneOverridePrompt": "Harici çizimler yüklemek mevcut olan içeriği değiştirecektir. Devam etmek istiyor musunuz?",
- "collabStopOverridePrompt": "",
+ "collabStopOverridePrompt": "Oturumu sonlandırmak daha önceki, yerel olarak kaydedilmiş çizimin üzerine kaydedilmesine sebep olacak. Emin misiniz?\n\n(Yerel çiziminizi kaybetmemek için tarayıcı sekmesini kapatabilirsiniz.)",
"errorLoadingLibrary": "Üçüncü taraf kitaplığı yüklerken bir hata oluştu.",
"confirmAddLibrary": "Bu, kitaplığınıza {{numShapes}} tane şekil ekleyecek. Emin misiniz?",
"imageDoesNotContainScene": "Resim ekleme şuan için desteklenmiyor.\nBir sahneyi içeri aktarmak mı istediniz? Bu dosya herhangi bir sahne içeriyor gibi durmuyor. Çıktı alırken sahneyi dahil ettiniz mi?",
"cannotRestoreFromImage": "Sahne bu dosyadan oluşturulamıyor",
- "invalidSceneUrl": "",
- "resetLibrary": ""
+ "invalidSceneUrl": "Verilen URL'den çalışma alanı yüklenemedi. Dosya bozuk olabilir veya geçerli bir Excalidraw JSON verisi bulundurmuyor olabilir.",
+ "resetLibrary": "Bu işlem kütüphanenizi sıfırlayacak. Emin misiniz?"
},
"toolBar": {
"selection": "Seçme",
@@ -201,34 +203,35 @@
"desc_inProgressIntro": "Ortak çalışma ortamı oluşturuldu.",
"desc_shareLink": "Bu bağlantıyı birlikte çalışacağınız kişilerle paylaşabilirsiniz:",
"desc_exitSession": "Çalışma ortamını kapattığınızda ortak çalışmadan ayrılmış olursunuz ancak kendi versiyonunuzda çalışmaya devam edebilirsiniz. Bu durumda ortak çalıştığınız diğer kişiler etkilenmeyecek, çalışma ortamındaki versiyon üzerinden çalışmaya devam edebilecekler.",
- "shareTitle": ""
+ "shareTitle": "Excalidraw'da canlı ortak calışma oturumuna katıl"
},
"errorDialog": {
"title": "Hata"
},
"helpDialog": {
- "blog": "",
- "click": "",
- "curvedArrow": "",
- "curvedLine": "",
- "documentation": "",
- "drag": "",
- "editor": "",
- "github": "",
- "howto": "",
- "or": "",
- "preventBinding": "",
- "shapes": "",
- "shortcuts": "",
- "textFinish": "",
- "textNewLine": "",
- "title": "",
- "view": "",
- "zoomToFit": "",
- "zoomToSelection": ""
+ "blog": "Blog'umuzu okuyun",
+ "click": "tıkla",
+ "curvedArrow": "Eğri ok",
+ "curvedLine": "Eğri çizgi",
+ "documentation": "Dokümantasyon",
+ "drag": "sürükle",
+ "editor": "Düzenleyici",
+ "github": "Bir hata mı buldun? Bildir",
+ "howto": "Rehberlerimizi takip edin",
+ "or": "veya",
+ "preventBinding": "Ok bağlamayı önleyin",
+ "shapes": "Şekiller",
+ "shortcuts": "Klavye kısayolları",
+ "textFinish": "(Metin) düzenlemeyi bitir",
+ "textNewLine": "Yeni satır ekle (metin)",
+ "title": "Yardım",
+ "view": "Görünüm",
+ "zoomToFit": "Tüm öğeleri sığdırmak için yakınlaştır",
+ "zoomToSelection": "Seçime yakınlaş"
},
"encrypted": {
- "tooltip": "Çizimleriniz uçtan-uca şifrelenmiştir, Excalidraw'ın sunucuları bile onları göremez."
+ "tooltip": "Çizimleriniz uçtan-uca şifrelenmiştir, Excalidraw'ın sunucuları bile onları göremez.",
+ "link": ""
},
"stats": {
"angle": "Açı",
@@ -240,18 +243,18 @@
"storage": "Depolama",
"title": "İnekler için istatistikler",
"total": "Toplam",
- "version": "",
- "versionCopy": "",
- "versionNotAvailable": "",
+ "version": "Sürüm",
+ "versionCopy": "Kopyalamak için tıkla",
+ "versionNotAvailable": "Sürüm mevcut değil",
"width": "Genişlik"
},
"toast": {
- "copyStyles": "",
- "copyToClipboard": "",
- "copyToClipboardAsPng": "",
- "fileSaved": "",
- "fileSavedToFilename": "",
- "canvas": "",
- "selection": ""
+ "copyStyles": "Stiller kopyalandı.",
+ "copyToClipboard": "Panoya kopyalandı.",
+ "copyToClipboardAsPng": "{{exportSelection}} panoya PNG olarak\n({{exportColorScheme}}) kopyalandı",
+ "fileSaved": "Dosya kaydedildi.",
+ "fileSavedToFilename": "{filename} kaydedildi",
+ "canvas": "tuval",
+ "selection": "seçim"
}
}
diff --git a/src/locales/uk-UA.json b/src/locales/uk-UA.json
index 03f5fe677..122313e19 100644
--- a/src/locales/uk-UA.json
+++ b/src/locales/uk-UA.json
@@ -68,7 +68,7 @@
"layers": "Шари",
"actions": "Дії",
"language": "Мова",
- "liveCollaboration": "",
+ "liveCollaboration": "Спільна співпраця",
"duplicateSelection": "Дублювати",
"untitled": "Без назви",
"name": "Ім’я",
@@ -92,9 +92,11 @@
"centerHorizontally": "Центрувати по горизонталі",
"distributeHorizontally": "Розподілити по горизонталі",
"distributeVertically": "Розподілити вертикально",
+ "flipHorizontal": "Віддзеркалити горизонтально",
+ "flipVertical": "Віддзеркалити вертикально",
"viewMode": "Режим перегляду",
- "toggleExportColorScheme": "",
- "share": ""
+ "toggleExportColorScheme": "Переключити колірну схему експорту",
+ "share": "Поділитися"
},
"buttons": {
"clearReset": "Очистити полотно",
@@ -119,7 +121,7 @@
"edit": "Редагувати",
"undo": "Відмінити",
"redo": "Повторити",
- "resetLibrary": "",
+ "resetLibrary": "Очистити бібліотеку",
"createNewRoom": "Створити нову кімнату",
"fullScreen": "Повноекранний режим",
"darkMode": "Темний режим",
@@ -143,8 +145,8 @@
"confirmAddLibrary": "Це призведе до додавання {{numShapes}} фігур до вашої бібліотеки. Ви впевнені?",
"imageDoesNotContainScene": "Імпортування зображень на даний момент не підтримується.\n\nЧи хочете ви імпортувати сцену? Це зображення не містить ніяких даних сцен. Ви увімкнули це під час експорту?",
"cannotRestoreFromImage": "Сцена не може бути відновлена з цього файлу зображення",
- "invalidSceneUrl": "",
- "resetLibrary": ""
+ "invalidSceneUrl": "Не вдалося імпортувати сцену з наданого URL. Він або недоформований, або не містить дійсних даних Excalidraw JSON.",
+ "resetLibrary": "Це призведе до очищення бібліотеки. Ви впевнені?"
},
"toolBar": {
"selection": "Виділення",
@@ -201,7 +203,7 @@
"desc_inProgressIntro": "Сесія спільної роботи над кресленням триває.",
"desc_shareLink": "Поділіться цим посиланням з будь-ким для спільної роботи:",
"desc_exitSession": "Зупинка сесії відключить вас від кімнати, але ви зможете продовжити роботу з полотном локально. Зверніть увагу, що це не вплине на інших людей, і вони все одно зможуть працювати над їх версією.",
- "shareTitle": ""
+ "shareTitle": "Приєднатися до сеансу спільної роботи на Excalidraw"
},
"errorDialog": {
"title": "Помилка"
@@ -228,7 +230,8 @@
"zoomToSelection": "Наблизити вибране"
},
"encrypted": {
- "tooltip": "Ваші креслення захищені наскрізним шифруванням — сервери Excalidraw ніколи їх не побачать."
+ "tooltip": "Ваші креслення захищені наскрізним шифруванням — сервери Excalidraw ніколи їх не побачать.",
+ "link": ""
},
"stats": {
"angle": "Кут",
@@ -248,10 +251,10 @@
"toast": {
"copyStyles": "Скопійовані стилі.",
"copyToClipboard": "Скопіювати до буферу обміну.",
- "copyToClipboardAsPng": "",
+ "copyToClipboardAsPng": "Скопійовано {{exportSelection}} до буфера обміну як PNG\n({{exportColorScheme}})",
"fileSaved": "Файл збережено.",
"fileSavedToFilename": "Збережено в {filename}",
- "canvas": "",
- "selection": ""
+ "canvas": "полотно",
+ "selection": "виділення"
}
}
diff --git a/src/locales/zh-CN.json b/src/locales/zh-CN.json
index 04ad518cc..81ddcc8e9 100644
--- a/src/locales/zh-CN.json
+++ b/src/locales/zh-CN.json
@@ -25,7 +25,7 @@
"strokeStyle_dashed": "虚线",
"strokeStyle_dotted": "点虚线",
"sloppiness": "边框",
- "opacity": "透明度",
+ "opacity": "不透明度",
"textAlign": "文本对齐",
"edges": "边角",
"sharp": "尖锐",
@@ -38,7 +38,7 @@
"fontSize": "字体大小",
"fontFamily": "字体",
"onlySelected": "仅被选中",
- "withBackground": "使用背景",
+ "withBackground": "包括背景",
"exportEmbedScene": "将画布数据嵌入到导出的文件",
"exportEmbedScene_details": "画布数据将被保存到导出的 PNG/SVG 文件,以便恢复。\n将会增加导出的文件大小。",
"addWatermark": "添加 “使用 Excalidraw 创建” 水印",
@@ -61,7 +61,7 @@
"architect": "朴素",
"artist": "艺术",
"cartoonist": "漫画家",
- "fileTitle": "文件标题",
+ "fileTitle": "文件名",
"colorPicker": "调色盘",
"canvasBackground": "画布背景",
"drawingCanvas": "绘制 Canvas",
@@ -92,6 +92,8 @@
"centerHorizontally": "水平居中",
"distributeHorizontally": "水平等距分布",
"distributeVertically": "垂直等距分布",
+ "flipHorizontal": "水平翻转",
+ "flipVertical": "垂直翻转",
"viewMode": "查看模式",
"toggleExportColorScheme": "切换导出配色方案",
"share": "分享"
@@ -128,7 +130,7 @@
"exitZenMode": "退出禅模式"
},
"alerts": {
- "clearReset": "这将会清除整个 画板。您是否要继续?",
+ "clearReset": "这将会清除整个画布。您是否要继续?",
"couldNotCreateShareableLink": "无法创建共享链接",
"couldNotCreateShareableLinkTooBig": "无法创建可共享链接:画布过大",
"couldNotLoadInvalidFile": "无法加载无效的文件",
@@ -228,7 +230,8 @@
"zoomToSelection": "缩放到选区"
},
"encrypted": {
- "tooltip": "您的绘图采用的端到端加密,其内容对于Excalidraw服务器是不可见的。"
+ "tooltip": "您的绘图采用的端到端加密,其内容对于Excalidraw服务器是不可见的。",
+ "link": ""
},
"stats": {
"angle": "角度",
diff --git a/src/locales/zh-TW.json b/src/locales/zh-TW.json
index a44b4cdad..a17826f2d 100644
--- a/src/locales/zh-TW.json
+++ b/src/locales/zh-TW.json
@@ -61,7 +61,7 @@
"architect": "精確",
"artist": "藝術",
"cartoonist": "卡通",
- "fileTitle": "檔案標題",
+ "fileTitle": "檔案名稱",
"colorPicker": "色彩選擇工具",
"canvasBackground": "Canvas 背景",
"drawingCanvas": "繪圖 canvas",
@@ -92,6 +92,8 @@
"centerHorizontally": "水平置中",
"distributeHorizontally": "水平分布",
"distributeVertically": "垂直分布",
+ "flipHorizontal": "水平翻轉",
+ "flipVertical": "垂直翻轉",
"viewMode": "檢視模式",
"toggleExportColorScheme": "切換輸出配色",
"share": "共享"
@@ -228,7 +230,8 @@
"zoomToSelection": "縮放至選取區"
},
"encrypted": {
- "tooltip": "你的作品已使用 end-to-end 方式加密,Excalidraw 的伺服器也無法取得其內容。"
+ "tooltip": "你的作品已使用 end-to-end 方式加密,Excalidraw 的伺服器也無法取得其內容。",
+ "link": ""
},
"stats": {
"angle": "角度",
diff --git a/src/packages/excalidraw/CHANGELOG.md b/src/packages/excalidraw/CHANGELOG.md
index 1e4b54f53..dbd3437c9 100644
--- a/src/packages/excalidraw/CHANGELOG.md
+++ b/src/packages/excalidraw/CHANGELOG.md
@@ -12,6 +12,32 @@ The change should be grouped under one of the below section and must contain PR
Please add the latest change on the top under the correct section.
-->
+## Unreleased
+
+## Excalidraw API
+
+### Features
+
+- Add `UIOptions` prop to customise `canvas actions` which includes customising `background color picker`, `clear canvas`, `export`, `load`, `save`, `save as` & `theme toggle` [#3364](https://github.com/excalidraw/excalidraw/pull/3364).
+- Calculate `width/height` of canvas based on excalidraw component (".excalidraw" selector) & also resize and update offsets whenever the dimensions of excalidraw component gets updated [#3379](https://github.com/excalidraw/excalidraw/pull/3379). You also don't need to add a resize handler anymore for excalidraw as its handled now in excalidraw itself.
+ #### BREAKING CHANGE
+ - `width/height` props have been removed. Instead now it takes `100%` of `width` and `height` of the container so you need to make sure the container in which you are rendering Excalidraw has non zero dimensions (It should have non zero width and height so Excalidraw can match the dimensions of containing block)
+- Calculate offsets when excalidraw container resizes using resize observer api [#3374](https://github.com/excalidraw/excalidraw/pull/3374).
+- Export types for the package so now it can be used with typescript [#3337](https://github.com/excalidraw/excalidraw/pull/3337). The types are available at `@excalidraw/excalirdraw/types`.
+- Add `renderCustomStats` prop to render extra stats on host, and expose `setToastMessage` API via refs which can be used to show toast with custom message [#3360](https://github.com/excalidraw/excalidraw/pull/3360).
+- Support passing a CSRF token when importing libraries to prevent prompting before installation. The token is passed from [https://libraries.excalidraw.com](https://libraries.excalidraw.com/) using the `token` URL key [#3329](https://github.com/excalidraw/excalidraw/pull/3329).
+- #### BREAKING CHANGE
+ Use `location.hash` when importing libraries to fix installation issues. This will require host apps to add a `hashchange` listener and call the newly exposed `excalidrawAPI.importLibrary(url)` API when applicable [#3320](https://github.com/excalidraw/excalidraw/pull/3320).
+- Append `location.pathname` to `libraryReturnUrl` default url [#3325](https://github.com/excalidraw/excalidraw/pull/3325).
+
+### Build
+
+- Expose separate builds for dev and prod and support source maps in dev build [#3330](https://github.com/excalidraw/excalidraw/pull/3330).
+ #### BREAKING CHANGE
+ - If you are using script tag to embed excalidraw then the name of the file will have to be updated to `excalidraw.production.min.js` instead of `excalidraw.min.js`. If you want to use dev build you can use `excalidraw.development.js`
+
+---
+
## 0.5.0 (2021-03-21)
## Excalidraw API
diff --git a/src/packages/excalidraw/README.md b/src/packages/excalidraw/README.md
index 59c046715..f8c81fc6e 100644
--- a/src/packages/excalidraw/README.md
+++ b/src/packages/excalidraw/README.md
@@ -1,6 +1,6 @@
### Excalidraw
-Excalidraw exported as a component to directly embed in your projects
+Excalidraw exported as a component to directly embed in your projects.
### Installation
@@ -28,10 +28,13 @@ If you want to load assets from a different path you can set a variable `window.
[Try here](https://codesandbox.io/s/excalidraw-ehlz3).
-
-Usage
+### Usage
-1. If you are using a Web bundler (for instance, Webpack), you can import it as an ES6 module as shown below
+#### Using Web Bundler
+
+If you are using a Web bundler (for instance, Webpack), you can import it as an ES6 module as shown below
+
+View Example
```js
import React, { useEffect, useState, useRef } from "react";
@@ -168,10 +171,29 @@ To view the full example visit :point_down:
[](https://codesandbox.io/s/excalidraw-ehlz3?fontsize=14&hidenavigation=1&theme=dark)
-2. To use it in a browser directly:
+Since Excalidraw doesn't support server side rendering yet so you will have to make sure the component is rendered once host is mounted.
+
+```js
+import { useState, useEffect } from "react";
+export default function IndexPage() {
+ const [Comp, setComp] = useState(null);
+ useEffect(() => {
+ import("@excalidraw/excalidraw").then((comp) => setComp(comp.default));
+ });
+ return <>{Comp && }>;
+}
+```
+
+
+
+#### In Browser
+
+To use it in a browser directly:
You will need to make sure `react`, `react-dom` is available as shown below.
+View Example
+
```html
@@ -183,7 +205,7 @@ You will need to make sure `react`, `react-dom` is available as shown below.
@@ -348,23 +370,9 @@ To view the full example visit :point_down:
[](https://codesandbox.io/s/excalidraw-in-browser-tlqom?fontsize=14&hidenavigation=1&theme=dark)
-Since Excalidraw doesn't support server side rendering yet so you will have to make sure the component is rendered once host is mounted.
-
-```js
-import { useState, useEffect } from "react";
-export default function IndexPage() {
- const [Comp, setComp] = useState(null);
- useEffect(() => {
- import("@excalidraw/excalidraw").then((comp) => setComp(comp.default));
- });
- return <>{Comp && }>;
-}
-```
-
-
-Props
+### Props
| Name | Type | Default | Description |
| --- | --- | --- | --- |
@@ -537,7 +545,7 @@ This prop indicates whether the shows the grid. When supplied, the value takes p
#### `libraryReturnUrl`
-If supplied, this URL will be used when user tries to install a library from [libraries.excalidraw.com](https://libraries.excalidraw.com). Default to `window.location.origin`. To install the libraries in the same tab from which it was opened, you need to set `window.name` (to any alphanumeric string) — if it's not set it will open in a new tab.
+If supplied, this URL will be used when user tries to install a library from [libraries.excalidraw.com](https://libraries.excalidraw.com). Defaults to `window.location.origin`. To install the libraries in the same tab from which it was opened, you need to set `window.name` (to any alphanumeric string) — if it's not set it will open in a new tab.
#### `theme`
@@ -547,10 +555,11 @@ This prop controls Excalidraw's theme. When supplied, the value takes precedence
This prop sets the name of the drawing which will be used when exporting the drawing. When supplied, the value takes precedence over `intialData.appState.name`, the `name` will be fully controlled by host app and the users won't be able to edit from within Excalidraw.
-
+### Does it support collaboration ?
-
diff --git a/src/packages/excalidraw/README_NEXT.md b/src/packages/excalidraw/README_NEXT.md
new file mode 100644
index 000000000..6a3ed9ba4
--- /dev/null
+++ b/src/packages/excalidraw/README_NEXT.md
@@ -0,0 +1,734 @@
+### Excalidraw
+
+Excalidraw exported as a component to directly embed in your projects.
+
+### Installation
+
+You can use npm
+
+```
+npm install react react-dom @excalidraw/excalidraw
+```
+
+or via yarn
+
+```
+yarn add react react-dom @excalidraw/excalidraw
+```
+
+After installation you will see a folder `excalidraw-assets` and `excalidraw-dev-assets` in `dist` directory which contains the assets needed for this app in prod and dev mode respectively.
+
+Move the folder `excalidraw-assets` and `excalidraw-dev-assets` to the path where your assets are served.
+
+By default it will try to load the files from `https://unpkg.com/@excalidraw/excalidraw/{currentVersion}/dist/`
+
+If you want to load assets from a different path you can set a variable `window.EXCALIDRAW_ASSET_PATH` depending on environment (for example if you have different URL's for dev and prod) to the url from where you want to load the assets.
+
+### Demo
+
+[Try here](https://codesandbox.io/s/excalidraw-ehlz3).
+
+### Usage
+
+#### Using Web Bundler
+
+If you are using a Web bundler (for instance, Webpack), you can import it as an ES6 module as shown below
+
+View Example
+
+```js
+import React, { useEffect, useState, useRef } from "react";
+import Excalidraw from "@excalidraw/excalidraw";
+import InitialData from "./initialData";
+
+import "./styles.scss";
+
+export default function App() {
+ const excalidrawRef = useRef(null);
+
+ const [viewModeEnabled, setViewModeEnabled] = useState(false);
+ const [zenModeEnabled, setZenModeEnabled] = useState(false);
+ const [gridModeEnabled, setGridModeEnabled] = useState(false);
+
+
+ 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 (
+
+ );
+}
+```
+
+To view the full example visit :point_down:
+
+[](https://codesandbox.io/s/excalidraw-ehlz3?fontsize=14&hidenavigation=1&theme=dark)
+
+
+
+Since Excalidraw doesn't support server side rendering yet so you will have to make sure the component is rendered once host is mounted.
+
+```js
+import { useState, useEffect } from "react";
+export default function IndexPage() {
+ const [Comp, setComp] = useState(null);
+ useEffect(() => {
+ import("@excalidraw/excalidraw").then((comp) => setComp(comp.default));
+ });
+ return <>{Comp && }>;
+}
+```
+
+The `types` are available at `@excalidraw/excalidraw/types`, you can view [example for typescript](https://codesandbox.io/s/excalidraw-types-9h2dm)
+
+#### In Browser
+
+To use it in a browser directly:
+
+For development use :point_down:
+
+```js
+
+```
+
+For production use :point_down:
+
+```js
+
+```
+
+You will need to make sure `react`, `react-dom` is available as shown in the below example. For prod please use the production versions of `react`, `react-dom`.
+
+View Example
+
+```html
+
+
+
+ Excalidraw in browser
+
+
+
+
+
+
+
+
+
+
Excalidraw Embed Example
+
+
+
+
+
+```
+
+```js
+/*eslint-disable */
+import "./styles.css";
+import InitialData from "./initialData";
+
+const App = () => {
+ const excalidrawRef = React.useRef(null);
+
+ const [viewModeEnabled, setViewModeEnabled] = React.useState(false);
+ const [zenModeEnabled, setZenModeEnabled] = React.useState(false);
+ const [gridModeEnabled, setGridModeEnabled] = React.useState(false);
+
+ 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 React.createElement(
+ React.Fragment,
+ null,
+ React.createElement(
+ "div",
+ { className: "button-wrapper" },
+ React.createElement(
+ "button",
+ {
+ className: "update-scene",
+ onClick: updateScene,
+ },
+ "Update Scene",
+ ),
+ React.createElement(
+ "button",
+ {
+ className: "reset-scene",
+ onClick: () => excalidrawRef.current.resetScene(),
+ },
+ "Reset Scene",
+ ),
+ React.createElement(
+ "label",
+ null,
+ React.createElement("input", {
+ type: "checkbox",
+ checked: viewModeEnabled,
+ onChange: () => setViewModeEnabled(!viewModeEnabled),
+ }),
+ "View mode",
+ ),
+ React.createElement(
+ "label",
+ null,
+ React.createElement("input", {
+ type: "checkbox",
+ checked: zenModeEnabled,
+ onChange: () => setZenModeEnabled(!zenModeEnabled),
+ }),
+ "Zen mode",
+ ),
+ React.createElement(
+ "label",
+ null,
+ React.createElement("input", {
+ type: "checkbox",
+ checked: gridModeEnabled,
+ onChange: () => setGridModeEnabled(!gridModeEnabled),
+ }),
+ "Grid mode",
+ ),
+ ),
+ React.createElement(
+ "div",
+ {
+ className: "excalidraw-wrapper",
+ ref: excalidrawWrapperRef,
+ },
+ React.createElement(Excalidraw.default, {
+ initialData: InitialData,
+ onChange: (elements, state) =>
+ console.log("Elements :", elements, "State : ", state),
+ onPointerUpdate: (payload) => console.log(payload),
+ onCollabButtonClick: () => window.alert("You clicked on collab button"),
+ viewModeEnabled: viewModeEnabled,
+ zenModeEnabled: zenModeEnabled,
+ gridModeEnabled: gridModeEnabled,
+ }),
+ ),
+ );
+};
+
+const excalidrawWrapper = document.getElementById("app");
+
+ReactDOM.render(React.createElement(App), excalidrawWrapper);
+```
+
+To view the full example visit :point_down:
+
+[](https://codesandbox.io/s/excalidraw-in-browser-tlqom?fontsize=14&hidenavigation=1&theme=dark)
+
+
+
+### Props
+
+| Name | Type | Default | Description |
+| --- | --- | --- | --- |
+| [`onChange`](#onChange) | Function | | This callback is triggered whenever the component updates due to any change. This callback will receive the excalidraw elements and the current app state. |
+| [`initialData`](#initialData) | {elements?: ExcalidrawElement[], appState?: AppState }
| null | The initial data with which app loads. |
+| [`ref`](#ref) | [`createRef`](https://reactjs.org/docs/refs-and-the-dom.html#creating-refs) or [`callbackRef`](https://reactjs.org/docs/refs-and-the-dom.html#callback-refs) or { current: { readyPromise: resolvablePromise } }
| | Ref to be passed to Excalidraw |
+| [`onCollabButtonClick`](#onCollabButtonClick) | Function | | Callback to be triggered when the collab button is clicked |
+| [`isCollaborating`](#isCollaborating) | `boolean` | | This implies if the app is in collaboration mode |
+| [`onPointerUpdate`](#onPointerUpdate) | Function | | Callback triggered when mouse pointer is updated. |
+| [`onExportToBackend`](#onExportToBackend) | Function | | Callback triggered when link button is clicked on export dialog |
+| [`langCode`](#langCode) | string | `en` | Language code string |
+| [`renderFooter `](#renderFooter) | Function | | Function that renders custom UI footer |
+| [`renderCustomStats`](#renderCustomStats) | Function | | Function that can be used to render custom stats on the stats dialog. |
+| [`viewModeEnabled`](#viewModeEnabled) | boolean | | This implies if the app is in view mode. |
+| [`zenModeEnabled`](#zenModeEnabled) | boolean | | This implies if the zen mode is enabled |
+| [`gridModeEnabled`](#gridModeEnabled) | boolean | | This implies if the grid mode is enabled |
+| [`libraryReturnUrl`](#libraryReturnUrl) | string | | What URL should [libraries.excalidraw.com](https://libraries.excalidraw.com) be installed to |
+| [`theme`](#theme) | `light` or `dark` | | The theme of the Excalidraw component |
+| [`name`](#name) | string | | Name of the drawing |
+| [`UIOptions`](#UIOptions) | { canvasActions: CanvasActions }
| [DEFAULT UI OPTIONS](https://github.com/excalidraw/excalidraw/blob/master/src/constants.ts#L129) | To customise UI options. Currently we support customising [`canvas actions`](#canvasActions) |
+
+### Dimensions of Excalidraw
+
+Excalidraw takes `100%` of `width` and `height` of the containing block so you need to make sure the container in which you are rendering Excalidraw has non zero dimensions (It should have non zero width and height so Excalidraw can match the dimensions of the containing block). This is to make sure you don't have to worry about updating the offsets of dimensions when resizing Excalidraw.
+
+#### `onChange`
+
+Every time component updates, this callback if passed will get triggered and has the below signature.
+
+```js
+(excalidrawElements, appState) => void;
+```
+
+1.`excalidrawElements`: Array of [excalidrawElements](https://github.com/excalidraw/excalidraw/blob/master/src/element/types.ts#L78) in the scene.
+
+2.`appState`: [AppState](https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L37) of the scene
+
+Here you can try saving the data to your backend or local storage for example.
+
+#### `initialData`
+
+This helps to load Excalidraw with `initialData`. It must be an object or a [promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/Promise) which resolves to an object containing the below optional fields.
+
+| Name | Type | Descrption |
+| --- | --- | --- |
+| `elements` | [ExcalidrawElement[]](https://github.com/excalidraw/excalidraw/blob/master/src/element/types.ts#L78) | The elements with which Excalidraw should be mounted. |
+| `appState` | [AppState](https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L37) | The App state with which Excalidraw should be mounted. |
+| `scrollToContent` | boolean | This attribute implies whether to scroll to the nearest element to center once Excalidraw is mounted. By default, it will not scroll the nearest element to the center. Make sure you pass `initialData.appState.scrollX` and `initialData.appState.scrollY` when `scrollToContent` is false so that scroll positions are retained |
+
+```json
+{
+ "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": "#000000",
+ "backgroundColor": "transparent",
+ "width": 186.47265625,
+ "height": 141.9765625,
+ "seed": 1968410350,
+ "groupIds": []
+ }
+ ],
+ "appState": { "zenModeEnabled": true, "viewBackgroundColor": "#AFEEEE" }
+}
+```
+
+You might want to use this when you want to load excalidraw with some initial elements and app state.
+
+#### `ref`
+
+You can pass a `ref` when you want to access some excalidraw APIs. We expose the below APIs:
+
+| API | signature | Usage |
+| --- | --- | --- |
+| ready | `boolean` | This is set to true once Excalidraw is rendered |
+| readyPromise | [resolvablePromise](https://github.com/excalidraw/excalidraw/blob/master/src/utils.ts#L317) | This promise will be resolved with the api once excalidraw has rendered. This will be helpful when you want do some action on the host app once this promise resolves. For this to work you will have to pass ref as shown [here](#readyPromise) |
+| updateScene | (sceneData)) => void
| updates the scene with the sceneData |
+| resetScene | `({ resetLoadingState: boolean }) => void` | Resets the scene. If `resetLoadingState` is passed as true then it will also force set the loading state to false. |
+| getSceneElementsIncludingDeleted | () => ExcalidrawElement[]
| Returns all the elements including the deleted in the scene |
+| getSceneElements | () => ExcalidrawElement[]
| Returns all the elements excluding the deleted in the scene |
+| getAppState | () => AppState
| Returns current appState |
+| history | `{ clear: () => void }` | This is the history API. `history.clear()` will clear the history |
+| setScrollToContent | (ExcalidrawElement[]) => void
| Scroll to the nearest element to center |
+| setCanvasOffsets | `() => void` | Updates the offsets for the Excalidraw component so that the coordinates are computed correctly (for example the cursor position). You don't have to call this when the position is changed on page scroll or when the excalidraw container resizes (we handle that ourselves). For any other cases if the position of excalidraw is updated (example due to scroll on parent container and not page scroll) you should call this API. |
+| importLibrary | `(url: string, token?: string) => void` | Imports library from given URL. You should call this on `hashchange`, passing the `addLibrary` value if you detect it. Optionally pass a CSRF `token` to skip prompting during installation (retrievable via `token` key from the url coming from [https://libraries.excalidraw.com](https://libraries.excalidraw.com/)). |
+| setToastMessage | `(message: string) => void` | This API can be used to show the toast with custom message. |
+
+#### `readyPromise`
+
+
+const excalidrawRef = { current: { readyPromise: resolvablePromise}}
+
+
+#### `onCollabButtonClick`
+
+This callback is triggered when clicked on the collab button in excalidraw. If not supplied, the collab dialog button is not rendered.
+
+#### `isCollaborating`
+
+This prop indicates if the app is in collaboration mode.
+
+#### `onPointerUpdate`
+
+This callback is triggered when mouse pointer is updated.
+
+```js
+({ x, y }, button, pointersMap}) => void;
+```
+
+1.`{x, y}`: Pointer coordinates
+
+2.`button`: The position of the button. This will be one of `["down", "up"]`
+
+3.`pointersMap`: [`pointers map`](https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L131) of the scene
+
+#### `onExportToBackend`
+
+This callback is triggered when the shareable-link button is clicked in the export dialog. The link button will only be shown if this callback is passed.
+
+```js
+(exportedElements, appState, canvas) => void
+```
+
+1. `exportedElements`: An array of [non deleted elements](https://github.com/excalidraw/excalidraw/blob/master/src/element/types.ts#L87) which needs to be exported.
+2. `appState`: [AppState](https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L37) of the scene.
+3. `canvas`: The `HTMLCanvasElement` of the scene.
+
+#### `langCode`
+
+Determines the language of the UI. It should be one of the [available language codes](https://github.com/excalidraw/excalidraw/blob/master/src/i18n.ts#L14). Defaults to `en` (English). We also export default language and supported languages which you can import as shown below.
+
+```js
+import { defaultLang, languages } from "@excalidraw/excalidraw";
+```
+
+| name | type |
+| --- | --- |
+| defaultLang | string |
+| languages | [Language[]](https://github.com/excalidraw/excalidraw/blob/master/src/i18n.ts#L8) |
+
+#### `renderFooter`
+
+A function that renders (returns JSX) custom UI footer. For example, you can use this to render a language picker that was previously being rendered by Excalidraw itself (for now, you'll need to implement your own language picker).
+
+#### `renderCustomStats`
+
+A function that can be used to render custom stats (returns JSX) in the nerd stats dialog. For example you can use this prop to render the size of the elements in the storage.
+
+#### `viewModeEnabled`
+
+This prop indicates whether the app is in `view mode`. When supplied, the value takes precedence over `intialData.appState.viewModeEnabled`, the `view mode` will be fully controlled by the host app, and users won't be able to toggle it from within the app.
+
+#### `zenModeEnabled`
+
+This prop indicates whether the app is in `zen mode`. When supplied, the value takes precedence over `intialData.appState.zenModeEnabled`, the `zen mode` will be fully controlled by the host app, and users won't be able to toggle it from within the app.
+
+#### `gridModeEnabled`
+
+This prop indicates whether the shows the grid. When supplied, the value takes precedence over `intialData.appState.gridModeEnabled`, the grid will be fully controlled by the host app, and users won't be able to toggle it from within the app.
+
+#### `libraryReturnUrl`
+
+If supplied, this URL will be used when user tries to install a library from [libraries.excalidraw.com](https://libraries.excalidraw.com). Defaults to `window.location.origin + window.location.pathname`. To install the libraries in the same tab from which it was opened, you need to set `window.name` (to any alphanumeric string) — if it's not set it will open in a new tab.
+
+#### `theme`
+
+This prop controls Excalidraw's theme. When supplied, the value takes precedence over `intialData.appState.theme`, the theme will be fully controlled by the host app, and users won't be able to toggle it from within the app.
+
+#### `name`
+
+This prop sets the name of the drawing which will be used when exporting the drawing. When supplied, the value takes precedence over `intialData.appState.name`, the `name` will be fully controlled by host app and the users won't be able to edit from within Excalidraw.
+
+### `UIOptions`
+
+This prop can be used to customise UI of Excalidraw. Currently we support customising only [`canvasActions`](#canvasActions). It accepts the below parameters
+
+
+{ canvasActions: CanvasActions }
+
+
+#### canvasActions
+
+| Attribute | Type | Default | Description |
+| --- | --- | --- | --- |
+| `changeViewBackgroundColor` | boolean | true | Implies whether to show `Background color picker` |
+| `clearCanvas` | boolean | true | Implies whether to show `Clear canvas button` |
+| `export` | boolean | true | Implies whether to show `Export button` |
+| `loadScene` | boolean | true | Implies whether to show `Load button` |
+| `saveAsScene` | boolean | true | Implies whether to show `Save as button` |
+| `saveScene` | boolean | true | Implies whether to show `Save button` |
+| `theme` | boolean | true | Implies whether to show `Theme toggle` |
+
+### Does it support collaboration ?
+
+No Excalidraw package doesn't come with collaboration, since this would have different implementations on the consumer so we expose the API's which you can use to communicate with Excalidraw as mentioned above. If you are interested in understanding how Excalidraw does it you can check it [here](https://github.com/excalidraw/excalidraw/blob/master/src/excalidraw-app/index.tsx).
+
+### Extra API's
+
+#### `getSceneVersion`
+
+**How to use**
+
+
+import { getSceneVersion } from "@excalidraw/excalidraw";
+getSceneVersion(elements: ExcalidrawElement[])
+
+
+This function returns the current scene version.
+
+#### `getSyncableElements`
+
+**_Signature_**
+
+
+getSyncableElements(elements: ExcalidrawElement[]):ExcalidrawElement[]
+
+
+**How to use**
+
+```js
+import { getSyncableElements } from "@excalidraw/excalidraw";
+```
+
+This function returns all the deleted elements of the scene.
+
+#### `getElementMap`
+
+**_Signature_**
+
+
+getElementsMap(elements: ExcalidrawElement[]): {[id: string]: ExcalidrawElement}
+
+
+**How to use**
+
+```js
+import { getElementsMap } from "@excalidraw/excalidraw";
+```
+
+This function returns an object where each element is mapped to its id.
+
+### Restore utilities
+
+#### `restoreAppState`
+
+**_Signature_**
+
+
+restoreAppState(appState: ImportedDataState["appState"], localAppState: Partial<AppState> | null): AppState
+
+
+**_How to use_**
+
+```js
+import { restoreAppState } from "@excalidraw/excalidraw";
+```
+
+This function will make sure all the keys have appropriate values in [appState](https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L37) and if any key is missing, it will be set to default value. If you pass `localAppState`, `localAppState` value will be preferred over the `appState` passed in params.
+
+#### `restoreElements`
+
+**_Signature_**
+
+
+restoreElements(elements: ImportedDataState["elements"]): ExcalidrawElement[]
+
+
+**_How to use_**
+
+```js
+import { restoreElements } from "@excalidraw/excalidraw";
+```
+
+This function will make sure all properties of element is correctly set and if any attribute is missing, it will be set to default value.
+
+#### `restore`
+
+**_Signature_**
+
+
+restoreElements(data: ImportedDataState): DataState
+
+
+**_How to use_**
+
+```js
+import { restore } from "@excalidraw/excalidraw";
+```
+
+This function makes sure elements and state is set to appropriate values and set to default value if not present. It is combination of [restoreElements](#restoreElements) and [restoreAppState](#restoreAppState)
+
+### Export utilities
+
+#### `exportToCanvas`
+
+**_Signature_**
+
+exportToCanvas({
+ elements,
+ appState
+ getDimensions,
+}: ExportOpts
+
+
+| Name | Type | Default | Description |
+| --- | --- | --- | --- |
+| elements | [Excalidraw Element []](https://github.com/excalidraw/excalidraw/blob/master/src/element/types) | | The elements to be exported to canvas |
+| appState | [AppState](https://github.com/excalidraw/excalidraw/blob/master/src/packages/utils.ts#L12) | [defaultAppState](https://github.com/excalidraw/excalidraw/blob/master/src/appState.ts#L11) | The app state of the scene |
+| getDimensions | `(width: number, height: number) => {width: number, height: number, scale: number)` | `(width, height) => ({ width, height, scale: 1 })` | A function which returns the width, height and scale with which canvas is to be exported. |
+
+**How to use**
+
+```js
+import { exportToCanvas } from "@excalidraw/excalidraw";
+```
+
+This function returns the canvas with the exported elements, appState and dimensions.
+
+#### `exportToBlob`
+
+**_Signature_**
+
+
+exportToBlob(
+ opts: ExportOpts & {
+ mimeType?: string,
+ quality?: number;
+})
+
+
+| Name | Type | Default | Description |
+| --- | --- | --- | --- |
+| 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. |
+
+**How to use**
+
+```js
+import { exportToBlob } from "@excalidraw/excalidraw";
+```
+
+Returns a promise which resolves with a [blob](https://developer.mozilla.org/en-US/docs/Web/API/Blob). It internally uses [canvas.ToBlob](https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlob).
+
+#### `exportToSvg`
+
+**_Signature_**
+
+
+exportToSvg({
+ elements: ExcalidrawElement[],
+ appState: AppState,
+ exportPadding?: number,
+ metadata?: string,
+}
+
+
+| Name | Type | Default | Description |
+| --- | --- | --- | --- |
+| elements | [Excalidraw Element []](https://github.com/excalidraw/excalidraw/blob/master/src/element/types.ts#L78) | | The elements to exported as svg |
+| appState | [AppState](https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L42) | [defaultAppState](https://github.com/excalidraw/excalidraw/blob/master/src/appState.ts#L11) | The app state of the scene |
+| exportPadding | number | 10 | The padding to be added on canvas |
+| metadata | string | '' | The metadata to be embedded in svg |
+
+This function returns a svg with the exported elements.
+
+##### Additional attributes of appState for `export\*` APIs
+
+| Name | Type | Default | Description |
+| --- | --- | --- | --- |
+| exportBackground | boolean | true | Indicates whether background should be exported |
+| viewBackgroundColor | string | #fff | The default background color |
+| shouldAddWatermark | boolean | false | Indicates whether watermark should be exported |
+| exportWithDarkMode | boolean | false | Indicates whether to export with dark mode |
diff --git a/src/packages/excalidraw/index.tsx b/src/packages/excalidraw/index.tsx
index 93cf7404d..2a9f041ad 100644
--- a/src/packages/excalidraw/index.tsx
+++ b/src/packages/excalidraw/index.tsx
@@ -10,11 +10,10 @@ import "../../css/styles.scss";
import { ExcalidrawAPIRefValue, ExcalidrawProps } from "../../types";
import { IsMobileProvider } from "../../is-mobile";
import { defaultLang } from "../../i18n";
+import { DEFAULT_UI_OPTIONS } from "../../constants";
const Excalidraw = (props: ExcalidrawProps) => {
const {
- width,
- height,
onChange,
initialData,
excalidrawRef,
@@ -30,8 +29,18 @@ const Excalidraw = (props: ExcalidrawProps) => {
libraryReturnUrl,
theme,
name,
+ renderCustomStats,
} = props;
+ const canvasActions = props.UIOptions?.canvasActions;
+
+ const UIOptions = {
+ canvasActions: {
+ ...DEFAULT_UI_OPTIONS.canvasActions,
+ ...canvasActions,
+ },
+ };
+
useEffect(() => {
// Block pinch-zooming on iOS outside of the content area
const handleTouchMove = (event: TouchEvent) => {
@@ -54,8 +63,6 @@ const Excalidraw = (props: ExcalidrawProps) => {
{
libraryReturnUrl={libraryReturnUrl}
theme={theme}
name={name}
+ renderCustomStats={renderCustomStats}
+ UIOptions={UIOptions}
/>
@@ -96,6 +105,7 @@ const areEqual = (
Excalidraw.defaultProps = {
lanCode: defaultLang.code,
+ UIOptions: DEFAULT_UI_OPTIONS,
};
const forwardedRefComp = forwardRef<
diff --git a/src/packages/excalidraw/main.js b/src/packages/excalidraw/main.js
new file mode 100644
index 000000000..b1668faf7
--- /dev/null
+++ b/src/packages/excalidraw/main.js
@@ -0,0 +1,5 @@
+if (process.env.NODE_ENV === "production") {
+ module.exports = require("./dist/excalidraw.production.min.js");
+} else {
+ module.exports = require("./dist/excalidraw.development.js");
+}
diff --git a/src/packages/excalidraw/package.json b/src/packages/excalidraw/package.json
index af76511de..d44ce1491 100644
--- a/src/packages/excalidraw/package.json
+++ b/src/packages/excalidraw/package.json
@@ -1,9 +1,11 @@
{
"name": "@excalidraw/excalidraw",
"version": "0.5.0",
- "main": "dist/excalidraw.min.js",
+ "main": "main.js",
+ "types": "types/packages/excalidraw/index.d.ts",
"files": [
- "dist/*"
+ "dist/*",
+ "types/*"
],
"publishConfig": {
"access": "public"
@@ -41,32 +43,34 @@
"react-dom": "^17.0.1"
},
"devDependencies": {
- "@babel/core": "7.13.10",
+ "@babel/core": "7.13.14",
"@babel/plugin-transform-arrow-functions": "7.13.0",
"@babel/plugin-transform-async-to-generator": "7.13.0",
"@babel/plugin-transform-runtime": "7.13.10",
"@babel/plugin-transform-typescript": "7.13.0",
- "@babel/preset-env": "7.13.10",
- "@babel/preset-react": "7.12.13",
+ "@babel/preset-env": "7.13.12",
+ "@babel/preset-react": "7.13.13",
"@babel/preset-typescript": "7.13.0",
"babel-loader": "8.2.2",
"babel-plugin-transform-class-properties": "6.24.1",
"cross-env": "7.0.3",
- "css-loader": "5.1.3",
+ "css-loader": "5.2.0",
"file-loader": "6.2.0",
- "mini-css-extract-plugin": "1.3.9",
+ "mini-css-extract-plugin": "1.4.0",
"sass-loader": "11.0.1",
"terser-webpack-plugin": "5.1.1",
- "ts-loader": "8.0.18",
- "webpack": "5.27.1",
+ "ts-loader": "8.1.0",
+ "typescript": "4.2.3",
+ "webpack": "5.30.0",
"webpack-bundle-analyzer": "4.4.0",
- "webpack-cli": "4.5.0"
+ "webpack-cli": "4.6.0"
},
"bugs": "https://github.com/excalidraw/excalidraw/issues",
"repository": "https://github.com/excalidraw/excalidraw",
"homepage": "https://github.com/excalidraw/excalidraw/tree/master/src/packages/excalidraw",
"scripts": {
- "build:umd": "cross-env NODE_ENV=production webpack --config webpack.prod.config.js",
+ "gen:types": "tsc --project ../../../tsconfig-types.json",
+ "build:umd": "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"
}
diff --git a/src/packages/excalidraw/webpack.dev.config.js b/src/packages/excalidraw/webpack.dev.config.js
new file mode 100644
index 000000000..70eec5119
--- /dev/null
+++ b/src/packages/excalidraw/webpack.dev.config.js
@@ -0,0 +1,81 @@
+const path = require("path");
+const webpack = require("webpack");
+
+module.exports = {
+ mode: "development",
+ devtool: false,
+ entry: {
+ "excalidraw.development": "./entry.js",
+ },
+ output: {
+ path: path.resolve(__dirname, "dist"),
+ library: "Excalidraw",
+ libraryTarget: "umd",
+ filename: "[name].js",
+ chunkFilename: "excalidraw-assets-dev/[name]-[contenthash].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" }, "sass-loader"],
+ },
+ {
+ test: /\.(ts|tsx|js|jsx|mjs)$/,
+ exclude: /node_modules/,
+ use: [
+ {
+ loader: "ts-loader",
+ options: {
+ transpileOnly: true,
+ configFile: path.resolve(__dirname, "../tsconfig.dev.json"),
+ },
+ },
+ ],
+ },
+ {
+ test: /\.(woff|woff2|eot|ttf|otf)$/,
+ use: [
+ {
+ loader: "file-loader",
+ options: {
+ name: "[name].[ext]",
+ outputPath: "excalidraw-assets-dev",
+ },
+ },
+ ],
+ },
+ ],
+ },
+ optimization: {
+ splitChunks: {
+ chunks: "async",
+ cacheGroups: {
+ vendors: {
+ test: /[\\/]node_modules[\\/]/,
+ name: "vendor",
+ },
+ },
+ },
+ },
+ plugins: [new webpack.EvalSourceMapDevToolPlugin({ exclude: /vendor/ })],
+ externals: {
+ react: {
+ root: "React",
+ commonjs2: "react",
+ commonjs: "react",
+ amd: "react",
+ },
+ "react-dom": {
+ root: "ReactDOM",
+ commonjs2: "react-dom",
+ commonjs: "react-dom",
+ amd: "react-dom",
+ },
+ },
+};
diff --git a/src/packages/excalidraw/webpack.prod.config.js b/src/packages/excalidraw/webpack.prod.config.js
index b1e808cc4..fa3eaefb8 100644
--- a/src/packages/excalidraw/webpack.prod.config.js
+++ b/src/packages/excalidraw/webpack.prod.config.js
@@ -6,7 +6,7 @@ const BundleAnalyzerPlugin = require("webpack-bundle-analyzer")
module.exports = {
mode: "production",
entry: {
- "excalidraw.min": "./entry.js",
+ "excalidraw.production.min": "./entry.js",
},
output: {
path: path.resolve(__dirname, "dist"),
@@ -24,7 +24,13 @@ module.exports = {
{
test: /\.(sa|sc|c)ss$/,
exclude: /node_modules/,
- use: ["style-loader", { loader: "css-loader" }, "sass-loader"],
+ use: [
+ "style-loader",
+ {
+ loader: "css-loader",
+ },
+ "sass-loader",
+ ],
},
{
test: /\.(ts|tsx|js|jsx|mjs)$/,
diff --git a/src/packages/excalidraw/yarn.lock b/src/packages/excalidraw/yarn.lock
index 5cd67bff7..2f479c681 100644
--- a/src/packages/excalidraw/yarn.lock
+++ b/src/packages/excalidraw/yarn.lock
@@ -9,34 +9,33 @@
dependencies:
"@babel/highlight" "^7.12.13"
-"@babel/compat-data@^7.13.0", "@babel/compat-data@^7.13.8":
- version "7.13.8"
- resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.13.8.tgz#5b783b9808f15cef71547f1b691f34f8ff6003a6"
- integrity sha512-EaI33z19T4qN3xLXsGf48M2cDqa6ei9tPZlfLdb2HC+e/cFtREiRd8hdSqDbwdLB0/+gLwqJmCYASH0z2bUdog==
+"@babel/compat-data@^7.13.0", "@babel/compat-data@^7.13.12", "@babel/compat-data@^7.13.8":
+ version "7.13.12"
+ resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.13.12.tgz#a8a5ccac19c200f9dd49624cac6e19d7be1236a1"
+ integrity sha512-3eJJ841uKxeV8dcN/2yGEUy+RfgQspPEgQat85umsE1rotuquQ2AbIub4S6j7c50a2d+4myc+zSlnXeIHrOnhQ==
-"@babel/core@7.13.10":
- version "7.13.10"
- resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.13.10.tgz#07de050bbd8193fcd8a3c27918c0890613a94559"
- integrity sha512-bfIYcT0BdKeAZrovpMqX2Mx5NrgAckGbwT982AkdS5GNfn3KMGiprlBAtmBcFZRUmpaufS6WZFP8trvx8ptFDw==
+"@babel/core@7.13.14":
+ version "7.13.14"
+ resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.13.14.tgz#8e46ebbaca460a63497c797e574038ab04ae6d06"
+ integrity sha512-wZso/vyF4ki0l0znlgM4inxbdrUvCb+cVz8grxDq+6C9k6qbqoIJteQOKicaKjCipU3ISV+XedCqpL2RJJVehA==
dependencies:
"@babel/code-frame" "^7.12.13"
"@babel/generator" "^7.13.9"
- "@babel/helper-compilation-targets" "^7.13.10"
- "@babel/helper-module-transforms" "^7.13.0"
+ "@babel/helper-compilation-targets" "^7.13.13"
+ "@babel/helper-module-transforms" "^7.13.14"
"@babel/helpers" "^7.13.10"
- "@babel/parser" "^7.13.10"
+ "@babel/parser" "^7.13.13"
"@babel/template" "^7.12.13"
- "@babel/traverse" "^7.13.0"
- "@babel/types" "^7.13.0"
+ "@babel/traverse" "^7.13.13"
+ "@babel/types" "^7.13.14"
convert-source-map "^1.7.0"
debug "^4.1.0"
gensync "^1.0.0-beta.2"
json5 "^2.1.2"
- lodash "^4.17.19"
semver "^6.3.0"
source-map "^0.5.0"
-"@babel/generator@^7.13.0", "@babel/generator@^7.13.9":
+"@babel/generator@^7.13.9":
version "7.13.9"
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.13.9.tgz#3a7aa96f9efb8e2be42d38d80e2ceb4c64d8de39"
integrity sha512-mHOOmY0Axl/JCTkxTU6Lf5sWOg/v8nUa+Xkt4zMTftX0wqmb6Sh7J8gvcehBw7q0AhrhAR+FDacKjCZ2X8K+Sw==
@@ -60,12 +59,12 @@
"@babel/helper-explode-assignable-expression" "^7.12.13"
"@babel/types" "^7.12.13"
-"@babel/helper-compilation-targets@^7.13.0", "@babel/helper-compilation-targets@^7.13.10", "@babel/helper-compilation-targets@^7.13.8":
- version "7.13.10"
- resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.13.10.tgz#1310a1678cb8427c07a753750da4f8ce442bdd0c"
- integrity sha512-/Xju7Qg1GQO4mHZ/Kcs6Au7gfafgZnwm+a7sy/ow/tV1sHeraRUHbjdat8/UvDor4Tez+siGKDk6zIKtCPKVJA==
+"@babel/helper-compilation-targets@^7.13.0", "@babel/helper-compilation-targets@^7.13.10", "@babel/helper-compilation-targets@^7.13.13", "@babel/helper-compilation-targets@^7.13.8":
+ version "7.13.13"
+ resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.13.13.tgz#2b2972a0926474853f41e4adbc69338f520600e5"
+ integrity sha512-q1kcdHNZehBwD9jYPh3WyXcsFERi39X4I59I3NadciWtNDyZ6x+GboOxncFK0kXlKIv6BJm5acncehXWUjWQMQ==
dependencies:
- "@babel/compat-data" "^7.13.8"
+ "@babel/compat-data" "^7.13.12"
"@babel/helper-validator-option" "^7.12.17"
browserslist "^4.14.5"
semver "^6.3.0"
@@ -148,6 +147,13 @@
dependencies:
"@babel/types" "^7.13.0"
+"@babel/helper-member-expression-to-functions@^7.13.12":
+ version "7.13.12"
+ resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.13.12.tgz#dfe368f26d426a07299d8d6513821768216e6d72"
+ integrity sha512-48ql1CLL59aKbU94Y88Xgb2VFy7a95ykGRbJJaaVv+LX5U8wFpLfiGXJJGUozsmA1oEh/o5Bp60Voq7ACyA/Sw==
+ dependencies:
+ "@babel/types" "^7.13.12"
+
"@babel/helper-module-imports@^7.12.13":
version "7.12.13"
resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.12.13.tgz#ec67e4404f41750463e455cc3203f6a32e93fcb0"
@@ -155,20 +161,26 @@
dependencies:
"@babel/types" "^7.12.13"
-"@babel/helper-module-transforms@^7.13.0":
- version "7.13.0"
- resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.13.0.tgz#42eb4bd8eea68bab46751212c357bfed8b40f6f1"
- integrity sha512-Ls8/VBwH577+pw7Ku1QkUWIyRRNHpYlts7+qSqBBFCW3I8QteB9DxfcZ5YJpOwH6Ihe/wn8ch7fMGOP1OhEIvw==
+"@babel/helper-module-imports@^7.13.12":
+ version "7.13.12"
+ resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.13.12.tgz#c6a369a6f3621cb25da014078684da9196b61977"
+ integrity sha512-4cVvR2/1B693IuOvSI20xqqa/+bl7lqAMR59R4iu39R9aOX8/JoYY1sFaNvUMyMBGnHdwvJgUrzNLoUZxXypxA==
dependencies:
- "@babel/helper-module-imports" "^7.12.13"
- "@babel/helper-replace-supers" "^7.13.0"
- "@babel/helper-simple-access" "^7.12.13"
+ "@babel/types" "^7.13.12"
+
+"@babel/helper-module-transforms@^7.13.0", "@babel/helper-module-transforms@^7.13.14":
+ version "7.13.14"
+ resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.13.14.tgz#e600652ba48ccb1641775413cb32cfa4e8b495ef"
+ integrity sha512-QuU/OJ0iAOSIatyVZmfqB0lbkVP0kDRiKj34xy+QNsnVZi/PA6BoSoreeqnxxa9EHFAIL0R9XOaAR/G9WlIy5g==
+ dependencies:
+ "@babel/helper-module-imports" "^7.13.12"
+ "@babel/helper-replace-supers" "^7.13.12"
+ "@babel/helper-simple-access" "^7.13.12"
"@babel/helper-split-export-declaration" "^7.12.13"
"@babel/helper-validator-identifier" "^7.12.11"
"@babel/template" "^7.12.13"
- "@babel/traverse" "^7.13.0"
- "@babel/types" "^7.13.0"
- lodash "^4.17.19"
+ "@babel/traverse" "^7.13.13"
+ "@babel/types" "^7.13.14"
"@babel/helper-optimise-call-expression@^7.12.13":
version "7.12.13"
@@ -211,6 +223,16 @@
"@babel/traverse" "^7.13.0"
"@babel/types" "^7.13.0"
+"@babel/helper-replace-supers@^7.13.12":
+ version "7.13.12"
+ resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.13.12.tgz#6442f4c1ad912502481a564a7386de0c77ff3804"
+ integrity sha512-Gz1eiX+4yDO8mT+heB94aLVNCL+rbuT2xy4YfyNqu8F+OI6vMvJK891qGBTqL9Uc8wxEvRW92Id6G7sDen3fFw==
+ dependencies:
+ "@babel/helper-member-expression-to-functions" "^7.13.12"
+ "@babel/helper-optimise-call-expression" "^7.12.13"
+ "@babel/traverse" "^7.13.0"
+ "@babel/types" "^7.13.12"
+
"@babel/helper-simple-access@^7.12.13":
version "7.12.13"
resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.12.13.tgz#8478bcc5cacf6aa1672b251c1d2dde5ccd61a6c4"
@@ -218,6 +240,13 @@
dependencies:
"@babel/types" "^7.12.13"
+"@babel/helper-simple-access@^7.13.12":
+ version "7.13.12"
+ resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.13.12.tgz#dd6c538afb61819d205a012c31792a39c7a5eaf6"
+ integrity sha512-7FEjbrx5SL9cWvXioDbnlYTppcZGuCY6ow3/D5vMggb2Ywgu4dMrpTJX0JdQAIcRRUElOIxF3yEooa9gUb9ZbA==
+ dependencies:
+ "@babel/types" "^7.13.12"
+
"@babel/helper-skip-transparent-expression-wrappers@^7.12.1":
version "7.12.1"
resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.12.1.tgz#462dc63a7e435ade8468385c63d2b84cce4b3cbf"
@@ -270,10 +299,19 @@
chalk "^2.0.0"
js-tokens "^4.0.0"
-"@babel/parser@^7.12.13", "@babel/parser@^7.13.0", "@babel/parser@^7.13.10":
- version "7.13.10"
- resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.13.10.tgz#8f8f9bf7b3afa3eabd061f7a5bcdf4fec3c48409"
- integrity sha512-0s7Mlrw9uTWkYua7xWr99Wpk2bnGa0ANleKfksYAES8LpWH4gW1OUr42vqKNf0us5UQNfru2wPqMqRITzq/SIQ==
+"@babel/parser@^7.12.13", "@babel/parser@^7.13.13":
+ version "7.13.13"
+ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.13.13.tgz#42f03862f4aed50461e543270916b47dd501f0df"
+ integrity sha512-OhsyMrqygfk5v8HmWwOzlYjJrtLaFhF34MrfG/Z73DgYCI6ojNUTUp2TYbtnjo8PegeJp12eamsNettCQjKjVw==
+
+"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.13.12":
+ version "7.13.12"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.13.12.tgz#a3484d84d0b549f3fc916b99ee4783f26fabad2a"
+ integrity sha512-d0u3zWKcoZf379fOeJdr1a5WPDny4aOFZ6hlfKivgK0LY7ZxNfoaHL2fWwdGtHyVvra38FC+HVYkO+byfSA8AQ==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.13.0"
+ "@babel/helper-skip-transparent-expression-wrappers" "^7.12.1"
+ "@babel/plugin-proposal-optional-chaining" "^7.13.12"
"@babel/plugin-proposal-async-generator-functions@^7.13.8":
version "7.13.8"
@@ -359,10 +397,10 @@
"@babel/helper-plugin-utils" "^7.13.0"
"@babel/plugin-syntax-optional-catch-binding" "^7.8.3"
-"@babel/plugin-proposal-optional-chaining@^7.13.8":
- version "7.13.8"
- resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.13.8.tgz#e39df93efe7e7e621841babc197982e140e90756"
- integrity sha512-hpbBwbTgd7Cz1QryvwJZRo1U0k1q8uyBmeXOSQUjdg/A2TASkhR/rz7AyqZ/kS8kbpsNA80rOYbxySBJAqmhhQ==
+"@babel/plugin-proposal-optional-chaining@^7.13.12":
+ version "7.13.12"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.13.12.tgz#ba9feb601d422e0adea6760c2bd6bbb7bfec4866"
+ integrity sha512-fcEdKOkIB7Tf4IxrgEVeFC4zeJSTr78no9wTdBuZZbqF64kzllU0ybo2zrzm7gUQfxGhBgq4E39oRs8Zx/RMYQ==
dependencies:
"@babel/helper-plugin-utils" "^7.13.0"
"@babel/helper-skip-transparent-expression-wrappers" "^7.12.1"
@@ -672,23 +710,23 @@
dependencies:
"@babel/helper-plugin-utils" "^7.12.13"
-"@babel/plugin-transform-react-jsx-development@^7.12.12":
- version "7.12.16"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.12.16.tgz#af187e749d123b54ae49bc7e034057a0c1d4d568"
- integrity sha512-GOp5SkMC4zhHwLbOSYhF+WpIZSf5bGzaKQTT9jWkemJRDM/CE6FtPydXjEYO3pHcna2Zjvg4mQ1lfjOR/4jsaQ==
+"@babel/plugin-transform-react-jsx-development@^7.12.17":
+ version "7.12.17"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.12.17.tgz#f510c0fa7cd7234153539f9a362ced41a5ca1447"
+ integrity sha512-BPjYV86SVuOaudFhsJR1zjgxxOhJDt6JHNoD48DxWEIxUCAMjV1ys6DYw4SDYZh0b1QsS2vfIA9t/ZsQGsDOUQ==
dependencies:
- "@babel/plugin-transform-react-jsx" "^7.12.16"
+ "@babel/plugin-transform-react-jsx" "^7.12.17"
-"@babel/plugin-transform-react-jsx@^7.12.13", "@babel/plugin-transform-react-jsx@^7.12.16":
- version "7.12.16"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.12.16.tgz#07c341e02a3e4066b00413534f30c42519923230"
- integrity sha512-dNu0vAbIk8OkqJfGtYF6ADk6jagoyAl+Ks5aoltbAlfoKv8d6yooi3j+kObeSQaCj9PgN6KMZPB90wWyek5TmQ==
+"@babel/plugin-transform-react-jsx@^7.12.17", "@babel/plugin-transform-react-jsx@^7.13.12":
+ version "7.13.12"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.13.12.tgz#1df5dfaf0f4b784b43e96da6f28d630e775f68b3"
+ integrity sha512-jcEI2UqIcpCqB5U5DRxIl0tQEProI2gcu+g8VTIqxLO5Iidojb4d77q+fwGseCvd8af/lJ9masp4QWzBXFE2xA==
dependencies:
"@babel/helper-annotate-as-pure" "^7.12.13"
- "@babel/helper-module-imports" "^7.12.13"
- "@babel/helper-plugin-utils" "^7.12.13"
+ "@babel/helper-module-imports" "^7.13.12"
+ "@babel/helper-plugin-utils" "^7.13.0"
"@babel/plugin-syntax-jsx" "^7.12.13"
- "@babel/types" "^7.12.13"
+ "@babel/types" "^7.13.12"
"@babel/plugin-transform-react-pure-annotations@^7.12.1":
version "7.12.1"
@@ -784,15 +822,16 @@
"@babel/helper-create-regexp-features-plugin" "^7.12.13"
"@babel/helper-plugin-utils" "^7.12.13"
-"@babel/preset-env@7.13.10":
- version "7.13.10"
- resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.13.10.tgz#b5cde31d5fe77ab2a6ab3d453b59041a1b3a5252"
- integrity sha512-nOsTScuoRghRtUsRr/c69d042ysfPHcu+KOB4A9aAO9eJYqrkat+LF8G1yp1HD18QiwixT2CisZTr/0b3YZPXQ==
+"@babel/preset-env@7.13.12":
+ version "7.13.12"
+ resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.13.12.tgz#6dff470478290582ac282fb77780eadf32480237"
+ integrity sha512-JzElc6jk3Ko6zuZgBtjOd01pf9yYDEIH8BcqVuYIuOkzOwDesoa/Nz4gIo4lBG6K861KTV9TvIgmFuT6ytOaAA==
dependencies:
- "@babel/compat-data" "^7.13.8"
+ "@babel/compat-data" "^7.13.12"
"@babel/helper-compilation-targets" "^7.13.10"
"@babel/helper-plugin-utils" "^7.13.0"
"@babel/helper-validator-option" "^7.12.17"
+ "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.13.12"
"@babel/plugin-proposal-async-generator-functions" "^7.13.8"
"@babel/plugin-proposal-class-properties" "^7.13.0"
"@babel/plugin-proposal-dynamic-import" "^7.13.8"
@@ -803,7 +842,7 @@
"@babel/plugin-proposal-numeric-separator" "^7.12.13"
"@babel/plugin-proposal-object-rest-spread" "^7.13.8"
"@babel/plugin-proposal-optional-catch-binding" "^7.13.8"
- "@babel/plugin-proposal-optional-chaining" "^7.13.8"
+ "@babel/plugin-proposal-optional-chaining" "^7.13.12"
"@babel/plugin-proposal-private-methods" "^7.13.0"
"@babel/plugin-proposal-unicode-property-regex" "^7.12.13"
"@babel/plugin-syntax-async-generators" "^7.8.4"
@@ -851,7 +890,7 @@
"@babel/plugin-transform-unicode-escapes" "^7.12.13"
"@babel/plugin-transform-unicode-regex" "^7.12.13"
"@babel/preset-modules" "^0.1.4"
- "@babel/types" "^7.13.0"
+ "@babel/types" "^7.13.12"
babel-plugin-polyfill-corejs2 "^0.1.4"
babel-plugin-polyfill-corejs3 "^0.1.3"
babel-plugin-polyfill-regenerator "^0.1.2"
@@ -869,15 +908,16 @@
"@babel/types" "^7.4.4"
esutils "^2.0.2"
-"@babel/preset-react@7.12.13":
- version "7.12.13"
- resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.12.13.tgz#5f911b2eb24277fa686820d5bd81cad9a0602a0a"
- integrity sha512-TYM0V9z6Abb6dj1K7i5NrEhA13oS5ujUYQYDfqIBXYHOc2c2VkFgc+q9kyssIyUfy4/hEwqrgSlJ/Qgv8zJLsA==
+"@babel/preset-react@7.13.13":
+ version "7.13.13"
+ resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.13.13.tgz#fa6895a96c50763fe693f9148568458d5a839761"
+ integrity sha512-gx+tDLIE06sRjKJkVtpZ/t3mzCDOnPG+ggHZG9lffUbX8+wC739x20YQc9V35Do6ZAxaUc/HhVHIiOzz5MvDmA==
dependencies:
- "@babel/helper-plugin-utils" "^7.12.13"
+ "@babel/helper-plugin-utils" "^7.13.0"
+ "@babel/helper-validator-option" "^7.12.17"
"@babel/plugin-transform-react-display-name" "^7.12.13"
- "@babel/plugin-transform-react-jsx" "^7.12.13"
- "@babel/plugin-transform-react-jsx-development" "^7.12.12"
+ "@babel/plugin-transform-react-jsx" "^7.13.12"
+ "@babel/plugin-transform-react-jsx-development" "^7.12.17"
"@babel/plugin-transform-react-pure-annotations" "^7.12.1"
"@babel/preset-typescript@7.13.0":
@@ -905,25 +945,24 @@
"@babel/parser" "^7.12.13"
"@babel/types" "^7.12.13"
-"@babel/traverse@^7.12.13", "@babel/traverse@^7.13.0":
- version "7.13.0"
- resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.13.0.tgz#6d95752475f86ee7ded06536de309a65fc8966cc"
- integrity sha512-xys5xi5JEhzC3RzEmSGrs/b3pJW/o87SypZ+G/PhaE7uqVQNv/jlmVIBXuoh5atqQ434LfXV+sf23Oxj0bchJQ==
+"@babel/traverse@^7.12.13", "@babel/traverse@^7.13.0", "@babel/traverse@^7.13.13":
+ version "7.13.13"
+ resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.13.13.tgz#39aa9c21aab69f74d948a486dd28a2dbdbf5114d"
+ integrity sha512-CblEcwmXKR6eP43oQGG++0QMTtCjAsa3frUuzHoiIJWpaIIi8dwMyEFUJoXRLxagGqCK+jALRwIO+o3R9p/uUg==
dependencies:
"@babel/code-frame" "^7.12.13"
- "@babel/generator" "^7.13.0"
+ "@babel/generator" "^7.13.9"
"@babel/helper-function-name" "^7.12.13"
"@babel/helper-split-export-declaration" "^7.12.13"
- "@babel/parser" "^7.13.0"
- "@babel/types" "^7.13.0"
+ "@babel/parser" "^7.13.13"
+ "@babel/types" "^7.13.13"
debug "^4.1.0"
globals "^11.1.0"
- lodash "^4.17.19"
-"@babel/types@^7.12.1", "@babel/types@^7.12.13", "@babel/types@^7.13.0", "@babel/types@^7.4.4":
- version "7.13.0"
- resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.13.0.tgz#74424d2816f0171b4100f0ab34e9a374efdf7f80"
- integrity sha512-hE+HE8rnG1Z6Wzo+MhaKE5lM5eMx71T4EHJgku2E3xIfaULhDcxiiRxUYgwX8qwP1BBSlag+TdGOt6JAidIZTA==
+"@babel/types@^7.12.1", "@babel/types@^7.12.13", "@babel/types@^7.13.0", "@babel/types@^7.13.12", "@babel/types@^7.13.13", "@babel/types@^7.13.14", "@babel/types@^7.4.4":
+ version "7.13.14"
+ resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.13.14.tgz#c35a4abb15c7cd45a2746d78ab328e362cbace0d"
+ integrity sha512-A2aa3QTkWoyqsZZFl56MLUsfmh7O0gN41IPvXAE/++8ojpbz12SszD7JEGYVdn4f9Kt4amIei07swF1h4AqmmQ==
dependencies:
"@babel/helper-validator-identifier" "^7.12.11"
lodash "^4.17.19"
@@ -1091,22 +1130,22 @@
"@webassemblyjs/ast" "1.11.0"
"@xtuc/long" "4.2.2"
-"@webpack-cli/configtest@^1.0.1":
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/@webpack-cli/configtest/-/configtest-1.0.1.tgz#241aecfbdc715eee96bed447ed402e12ec171935"
- integrity sha512-B+4uBUYhpzDXmwuo3V9yBH6cISwxEI4J+NO5ggDaGEEHb0osY/R7MzeKc0bHURXQuZjMM4qD+bSJCKIuI3eNBQ==
+"@webpack-cli/configtest@^1.0.2":
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/@webpack-cli/configtest/-/configtest-1.0.2.tgz#2a20812bfb3a2ebb0b27ee26a52eeb3e3f000836"
+ integrity sha512-3OBzV2fBGZ5TBfdW50cha1lHDVf9vlvRXnjpVbJBa20pSZQaSkMJZiwA8V2vD9ogyeXn8nU5s5A6mHyf5jhMzA==
-"@webpack-cli/info@^1.2.2":
- version "1.2.2"
- resolved "https://registry.yarnpkg.com/@webpack-cli/info/-/info-1.2.2.tgz#ef3c0cd947a1fa083e174a59cb74e0b6195c236c"
- integrity sha512-5U9kUJHnwU+FhKH4PWGZuBC1hTEPYyxGSL5jjoBI96Gx8qcYJGOikpiIpFoTq8mmgX3im2zAo2wanv/alD74KQ==
+"@webpack-cli/info@^1.2.3":
+ version "1.2.3"
+ resolved "https://registry.yarnpkg.com/@webpack-cli/info/-/info-1.2.3.tgz#ef819d10ace2976b6d134c7c823a3e79ee31a92c"
+ integrity sha512-lLek3/T7u40lTqzCGpC6CAbY6+vXhdhmwFRxZLMnRm6/sIF/7qMpT8MocXCRQfz0JAh63wpbXLMnsQ5162WS7Q==
dependencies:
envinfo "^7.7.3"
-"@webpack-cli/serve@^1.3.0":
- version "1.3.0"
- resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-1.3.0.tgz#2730c770f5f1f132767c63dcaaa4ec28f8c56a6c"
- integrity sha512-k2p2VrONcYVX1wRRrf0f3X2VGltLWcv+JzXRBDmvCxGlCeESx4OXw91TsWeKOkp784uNoVQo313vxJFHXPPwfw==
+"@webpack-cli/serve@^1.3.1":
+ version "1.3.1"
+ resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-1.3.1.tgz#911d1b3ff4a843304b9c3bacf67bb34672418441"
+ integrity sha512-0qXvpeYO6vaNoRBI52/UsbcaBydJCggoBBnIo/ovQQdn6fug0BgwsjorV1hVS7fMqGVTZGcVxv8334gjmbj5hw==
"@xtuc/ieee754@^1.2.0":
version "1.2.0"
@@ -1426,12 +1465,7 @@ color-name@~1.1.4:
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
-colorette@^1.2.1:
- version "1.2.1"
- resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.1.tgz#4d0b921325c14faf92633086a536db6e89564b1b"
- integrity sha512-puCDz0CzydiSYOrnXpz/PKd69zRrribezjtE9yd4zvytoRc8+RY/KJPvtPFKZS3E3wP6neGyMe0vOTlHO5L3Pw==
-
-colorette@^1.2.2:
+colorette@^1.2.1, colorette@^1.2.2:
version "1.2.2"
resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.2.tgz#cbcc79d5e99caea2dbf10eb3a26fd8b3e6acfa94"
integrity sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==
@@ -1497,10 +1531,10 @@ cross-spawn@^7.0.1, cross-spawn@^7.0.3:
shebang-command "^2.0.0"
which "^2.0.1"
-css-loader@5.1.3:
- version "5.1.3"
- resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-5.1.3.tgz#87f6fc96816b20debe3cf682f85c7e56a963d0d1"
- integrity sha512-CoPZvyh8sLiGARK3gqczpfdedbM74klGWurF2CsNZ2lhNaXdLIUks+3Mfax3WBeRuHoglU+m7KG/+7gY6G4aag==
+css-loader@5.2.0:
+ version "5.2.0"
+ resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-5.2.0.tgz#a9ecda190500863673ce4434033710404efbff00"
+ integrity sha512-MfRo2MjEeLXMlUkeUwN71Vx5oc6EJnx5UQ4Yi9iUtYQvrPtwLUucYptz0hc6n++kdNcyF5olYBS4vPjJDAcLkw==
dependencies:
camelcase "^6.2.0"
cssesc "^3.0.0"
@@ -2030,10 +2064,10 @@ mimic-fn@^2.1.0:
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==
-mini-css-extract-plugin@1.3.9:
- version "1.3.9"
- resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-1.3.9.tgz#47a32132b0fd97a119acd530e8421e8f6ab16d5e"
- integrity sha512-Ac4s+xhVbqlyhXS5J/Vh/QXUz3ycXlCqoCPpg0vdfhsIBH9eg/It/9L1r1XhSCH737M1lqcWnMuWL13zcygn5A==
+mini-css-extract-plugin@1.4.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-1.4.0.tgz#c8e571c4b6d63afa56c47260343adf623349c473"
+ integrity sha512-DyQr5DhXXARKZoc4kwvCvD95kh69dUupfuKOmBUqZ4kBTmRaRZcU32lYu3cLd6nEGXhQ1l7LzZ3F/CjItaY6VQ==
dependencies:
loader-utils "^2.0.0"
schema-utils "^3.0.0"
@@ -2535,10 +2569,10 @@ totalist@^1.0.0:
resolved "https://registry.yarnpkg.com/totalist/-/totalist-1.1.0.tgz#a4d65a3e546517701e3e5c37a47a70ac97fe56df"
integrity sha512-gduQwd1rOdDMGxFG1gEvhV88Oirdo2p+KjoYFU7k2g+i7n6AFFbDQ5kMPUsW0pNbfQsB/cwXvT1i4Bue0s9g5g==
-ts-loader@8.0.18:
- version "8.0.18"
- resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-8.0.18.tgz#b2385cbe81c34ad9f997915129cdde3ad92a61ea"
- integrity sha512-hRZzkydPX30XkLaQwJTDcWDoxZHK6IrEMDQpNd7tgcakFruFkeUp/aY+9hBb7BUGb+ZWKI0jiOGMo0MckwzdDQ==
+ts-loader@8.1.0:
+ version "8.1.0"
+ resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-8.1.0.tgz#d6292487df279c7cc79b6d3b70bb9d31682b693e"
+ integrity sha512-YiQipGGAFj2zBfqLhp28yUvPP9jUGqHxRzrGYuc82Z2wM27YIHbElXiaZDc93c3x0mz4zvBmS6q/DgExpdj37A==
dependencies:
chalk "^4.1.0"
enhanced-resolve "^4.0.0"
@@ -2551,6 +2585,11 @@ tslib@^1.9.0:
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
+typescript@4.2.3:
+ version "4.2.3"
+ resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.2.3.tgz#39062d8019912d43726298f09493d598048c1ce3"
+ integrity sha512-qOcYwxaByStAWrBf4x0fibwZvMRG+r4cQoTjbPtUlrWjBHbmCAww1i448U0GJ+3cNNEtebDteo/cHOR3xJ4wEw==
+
unicode-canonical-property-names-ecmascript@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818"
@@ -2619,15 +2658,15 @@ webpack-bundle-analyzer@4.4.0:
sirv "^1.0.7"
ws "^7.3.1"
-webpack-cli@4.5.0:
- version "4.5.0"
- resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-4.5.0.tgz#b5213b84adf6e1f5de6391334c9fa53a48850466"
- integrity sha512-wXg/ef6Ibstl2f50mnkcHblRPN/P9J4Nlod5Hg9HGFgSeF8rsqDGHJeVe4aR26q9l62TUJi6vmvC2Qz96YJw1Q==
+webpack-cli@4.6.0:
+ version "4.6.0"
+ resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-4.6.0.tgz#27ae86bfaec0cf393fcfd58abdc5a229ad32fd16"
+ integrity sha512-9YV+qTcGMjQFiY7Nb1kmnupvb1x40lfpj8pwdO/bom+sQiP4OBMKjHq29YQrlDWDPZO9r/qWaRRywKaRDKqBTA==
dependencies:
"@discoveryjs/json-ext" "^0.5.0"
- "@webpack-cli/configtest" "^1.0.1"
- "@webpack-cli/info" "^1.2.2"
- "@webpack-cli/serve" "^1.3.0"
+ "@webpack-cli/configtest" "^1.0.2"
+ "@webpack-cli/info" "^1.2.3"
+ "@webpack-cli/serve" "^1.3.1"
colorette "^1.2.1"
commander "^7.0.0"
enquirer "^2.3.6"
@@ -2663,10 +2702,10 @@ webpack-sources@^2.1.1:
source-list-map "^2.0.1"
source-map "^0.6.1"
-webpack@5.27.1:
- version "5.27.1"
- resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.27.1.tgz#6808fb6e45e35290cdb8ae43c7a10884839a3079"
- integrity sha512-rxIDsPZ3Apl3JcqiemiLmWH+hAq04YeOXqvCxNZOnTp8ZgM9NEPtbu4CaMfMEf9KShnx/Ym8uLGmM6P4XnwCoA==
+webpack@5.30.0:
+ version "5.30.0"
+ resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.30.0.tgz#07d87c182a060e0c2491062f3dc0edc85a29d884"
+ integrity sha512-Zr9NIri5yzpfmaMea2lSMV1UygbW0zQsSlGLMgKUm63ACXg6alhd1u4v5UBSBjzYKXJN6BNMGVM7w165e7NxYA==
dependencies:
"@types/eslint-scope" "^3.7.0"
"@types/estree" "^0.0.46"
diff --git a/src/packages/tsconfig.dev.json b/src/packages/tsconfig.dev.json
new file mode 100644
index 000000000..6462aaba3
--- /dev/null
+++ b/src/packages/tsconfig.dev.json
@@ -0,0 +1,11 @@
+{
+ "compilerOptions": {
+ "target": "es6",
+ "module": "esNext",
+ "moduleResolution": "node",
+ "resolveJsonModule": true,
+ "jsx": "react-jsx",
+ "sourceMap": true,
+ "allowJs": true
+ }
+}
diff --git a/src/packages/utils.ts b/src/packages/utils.ts
index 0f4c1c402..9749ee79f 100644
--- a/src/packages/utils.ts
+++ b/src/packages/utils.ts
@@ -11,7 +11,7 @@ import { restore } from "../data/restore";
type ExportOpts = {
elements: readonly ExcalidrawElement[];
appState?: Partial>;
- getDimensions: (
+ getDimensions?: (
width: number,
height: number,
) => { width: number; height: number; scale: number };
@@ -33,7 +33,7 @@ export const exportToCanvas = ({
} = restoredAppState;
return _exportToCanvas(
getNonDeletedElements(restoredElements),
- { ...restoredAppState, offsetTop: 0, offsetLeft: 0 },
+ { ...restoredAppState, offsetTop: 0, offsetLeft: 0, width: 0, height: 0 },
{ exportBackground, viewBackgroundColor, shouldAddWatermark },
(width: number, height: number) => {
const canvas = document.createElement("canvas");
@@ -83,7 +83,7 @@ export const exportToSvg = ({
appState = getDefaultAppState(),
exportPadding,
metadata,
-}: ExportOpts & {
+}: Omit & {
exportPadding?: number;
metadata?: string;
}): SVGSVGElement => {
diff --git a/src/packages/utils/CHANGELOG.md b/src/packages/utils/CHANGELOG.md
index 395b77e7e..875d8e82f 100644
--- a/src/packages/utils/CHANGELOG.md
+++ b/src/packages/utils/CHANGELOG.md
@@ -5,3 +5,7 @@
First release of `@excalidraw/utils` to provide utilities functions.
- Added `exportToBlob` and `exportToSvg` to export an Excalidraw diagram definition, respectively, to a [Blob](https://developer.mozilla.org/en-US/docs/Web/API/Blob) and to a [SVGElement](https://developer.mozilla.org/en-US/docs/Web/API/SVGElement) ([#2246](https://github.com/excalidraw/excalidraw/pull/2246))
+
+### Features
+
+- Flip single elements horizontally or vertically [#2520](https://github.com/excalidraw/excalidraw/pull/2520)
diff --git a/src/packages/utils/package.json b/src/packages/utils/package.json
index 583c758a2..0ac78693e 100644
--- a/src/packages/utils/package.json
+++ b/src/packages/utils/package.json
@@ -34,23 +34,23 @@
]
},
"devDependencies": {
- "@babel/core": "7.13.10",
+ "@babel/core": "7.13.14",
"@babel/plugin-transform-arrow-functions": "7.13.0",
"@babel/plugin-transform-async-to-generator": "7.13.0",
"@babel/plugin-transform-runtime": "^7.12.10",
"@babel/plugin-transform-typescript": "7.13.0",
- "@babel/preset-env": "7.13.10",
+ "@babel/preset-env": "7.13.12",
"@babel/preset-typescript": "7.13.0",
"babel-loader": "8.2.2",
"babel-plugin-transform-class-properties": "6.24.1",
"cross-env": "7.0.3",
- "css-loader": "5.1.3",
+ "css-loader": "5.2.0",
"file-loader": "6.2.0",
"sass-loader": "11.0.1",
- "ts-loader": "8.0.18",
- "webpack": "5.27.1",
+ "ts-loader": "8.1.0",
+ "webpack": "5.30.0",
"webpack-bundle-analyzer": "4.4.0",
- "webpack-cli": "4.5.0"
+ "webpack-cli": "4.6.0"
},
"bugs": "https://github.com/excalidraw/excalidraw/issues",
"repository": "https://github.com/excalidraw/excalidraw",
diff --git a/src/packages/utils/yarn.lock b/src/packages/utils/yarn.lock
index fc6b29ad3..34f59f9ed 100644
--- a/src/packages/utils/yarn.lock
+++ b/src/packages/utils/yarn.lock
@@ -9,34 +9,33 @@
dependencies:
"@babel/highlight" "^7.12.13"
-"@babel/compat-data@^7.13.0", "@babel/compat-data@^7.13.8":
- version "7.13.8"
- resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.13.8.tgz#5b783b9808f15cef71547f1b691f34f8ff6003a6"
- integrity sha512-EaI33z19T4qN3xLXsGf48M2cDqa6ei9tPZlfLdb2HC+e/cFtREiRd8hdSqDbwdLB0/+gLwqJmCYASH0z2bUdog==
+"@babel/compat-data@^7.13.0", "@babel/compat-data@^7.13.12", "@babel/compat-data@^7.13.8":
+ version "7.13.12"
+ resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.13.12.tgz#a8a5ccac19c200f9dd49624cac6e19d7be1236a1"
+ integrity sha512-3eJJ841uKxeV8dcN/2yGEUy+RfgQspPEgQat85umsE1rotuquQ2AbIub4S6j7c50a2d+4myc+zSlnXeIHrOnhQ==
-"@babel/core@7.13.10":
- version "7.13.10"
- resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.13.10.tgz#07de050bbd8193fcd8a3c27918c0890613a94559"
- integrity sha512-bfIYcT0BdKeAZrovpMqX2Mx5NrgAckGbwT982AkdS5GNfn3KMGiprlBAtmBcFZRUmpaufS6WZFP8trvx8ptFDw==
+"@babel/core@7.13.14":
+ version "7.13.14"
+ resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.13.14.tgz#8e46ebbaca460a63497c797e574038ab04ae6d06"
+ integrity sha512-wZso/vyF4ki0l0znlgM4inxbdrUvCb+cVz8grxDq+6C9k6qbqoIJteQOKicaKjCipU3ISV+XedCqpL2RJJVehA==
dependencies:
"@babel/code-frame" "^7.12.13"
"@babel/generator" "^7.13.9"
- "@babel/helper-compilation-targets" "^7.13.10"
- "@babel/helper-module-transforms" "^7.13.0"
+ "@babel/helper-compilation-targets" "^7.13.13"
+ "@babel/helper-module-transforms" "^7.13.14"
"@babel/helpers" "^7.13.10"
- "@babel/parser" "^7.13.10"
+ "@babel/parser" "^7.13.13"
"@babel/template" "^7.12.13"
- "@babel/traverse" "^7.13.0"
- "@babel/types" "^7.13.0"
+ "@babel/traverse" "^7.13.13"
+ "@babel/types" "^7.13.14"
convert-source-map "^1.7.0"
debug "^4.1.0"
gensync "^1.0.0-beta.2"
json5 "^2.1.2"
- lodash "^4.17.19"
semver "^6.3.0"
source-map "^0.5.0"
-"@babel/generator@^7.13.0", "@babel/generator@^7.13.9":
+"@babel/generator@^7.13.9":
version "7.13.9"
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.13.9.tgz#3a7aa96f9efb8e2be42d38d80e2ceb4c64d8de39"
integrity sha512-mHOOmY0Axl/JCTkxTU6Lf5sWOg/v8nUa+Xkt4zMTftX0wqmb6Sh7J8gvcehBw7q0AhrhAR+FDacKjCZ2X8K+Sw==
@@ -60,12 +59,12 @@
"@babel/helper-explode-assignable-expression" "^7.12.13"
"@babel/types" "^7.12.13"
-"@babel/helper-compilation-targets@^7.13.0", "@babel/helper-compilation-targets@^7.13.10", "@babel/helper-compilation-targets@^7.13.8":
- version "7.13.10"
- resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.13.10.tgz#1310a1678cb8427c07a753750da4f8ce442bdd0c"
- integrity sha512-/Xju7Qg1GQO4mHZ/Kcs6Au7gfafgZnwm+a7sy/ow/tV1sHeraRUHbjdat8/UvDor4Tez+siGKDk6zIKtCPKVJA==
+"@babel/helper-compilation-targets@^7.13.0", "@babel/helper-compilation-targets@^7.13.10", "@babel/helper-compilation-targets@^7.13.13", "@babel/helper-compilation-targets@^7.13.8":
+ version "7.13.13"
+ resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.13.13.tgz#2b2972a0926474853f41e4adbc69338f520600e5"
+ integrity sha512-q1kcdHNZehBwD9jYPh3WyXcsFERi39X4I59I3NadciWtNDyZ6x+GboOxncFK0kXlKIv6BJm5acncehXWUjWQMQ==
dependencies:
- "@babel/compat-data" "^7.13.8"
+ "@babel/compat-data" "^7.13.12"
"@babel/helper-validator-option" "^7.12.17"
browserslist "^4.14.5"
semver "^6.3.0"
@@ -148,6 +147,13 @@
dependencies:
"@babel/types" "^7.13.0"
+"@babel/helper-member-expression-to-functions@^7.13.12":
+ version "7.13.12"
+ resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.13.12.tgz#dfe368f26d426a07299d8d6513821768216e6d72"
+ integrity sha512-48ql1CLL59aKbU94Y88Xgb2VFy7a95ykGRbJJaaVv+LX5U8wFpLfiGXJJGUozsmA1oEh/o5Bp60Voq7ACyA/Sw==
+ dependencies:
+ "@babel/types" "^7.13.12"
+
"@babel/helper-module-imports@^7.12.13":
version "7.12.13"
resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.12.13.tgz#ec67e4404f41750463e455cc3203f6a32e93fcb0"
@@ -155,20 +161,26 @@
dependencies:
"@babel/types" "^7.12.13"
-"@babel/helper-module-transforms@^7.13.0":
- version "7.13.0"
- resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.13.0.tgz#42eb4bd8eea68bab46751212c357bfed8b40f6f1"
- integrity sha512-Ls8/VBwH577+pw7Ku1QkUWIyRRNHpYlts7+qSqBBFCW3I8QteB9DxfcZ5YJpOwH6Ihe/wn8ch7fMGOP1OhEIvw==
+"@babel/helper-module-imports@^7.13.12":
+ version "7.13.12"
+ resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.13.12.tgz#c6a369a6f3621cb25da014078684da9196b61977"
+ integrity sha512-4cVvR2/1B693IuOvSI20xqqa/+bl7lqAMR59R4iu39R9aOX8/JoYY1sFaNvUMyMBGnHdwvJgUrzNLoUZxXypxA==
dependencies:
- "@babel/helper-module-imports" "^7.12.13"
- "@babel/helper-replace-supers" "^7.13.0"
- "@babel/helper-simple-access" "^7.12.13"
+ "@babel/types" "^7.13.12"
+
+"@babel/helper-module-transforms@^7.13.0", "@babel/helper-module-transforms@^7.13.14":
+ version "7.13.14"
+ resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.13.14.tgz#e600652ba48ccb1641775413cb32cfa4e8b495ef"
+ integrity sha512-QuU/OJ0iAOSIatyVZmfqB0lbkVP0kDRiKj34xy+QNsnVZi/PA6BoSoreeqnxxa9EHFAIL0R9XOaAR/G9WlIy5g==
+ dependencies:
+ "@babel/helper-module-imports" "^7.13.12"
+ "@babel/helper-replace-supers" "^7.13.12"
+ "@babel/helper-simple-access" "^7.13.12"
"@babel/helper-split-export-declaration" "^7.12.13"
"@babel/helper-validator-identifier" "^7.12.11"
"@babel/template" "^7.12.13"
- "@babel/traverse" "^7.13.0"
- "@babel/types" "^7.13.0"
- lodash "^4.17.19"
+ "@babel/traverse" "^7.13.13"
+ "@babel/types" "^7.13.14"
"@babel/helper-optimise-call-expression@^7.12.13":
version "7.12.13"
@@ -211,6 +223,16 @@
"@babel/traverse" "^7.13.0"
"@babel/types" "^7.13.0"
+"@babel/helper-replace-supers@^7.13.12":
+ version "7.13.12"
+ resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.13.12.tgz#6442f4c1ad912502481a564a7386de0c77ff3804"
+ integrity sha512-Gz1eiX+4yDO8mT+heB94aLVNCL+rbuT2xy4YfyNqu8F+OI6vMvJK891qGBTqL9Uc8wxEvRW92Id6G7sDen3fFw==
+ dependencies:
+ "@babel/helper-member-expression-to-functions" "^7.13.12"
+ "@babel/helper-optimise-call-expression" "^7.12.13"
+ "@babel/traverse" "^7.13.0"
+ "@babel/types" "^7.13.12"
+
"@babel/helper-simple-access@^7.12.13":
version "7.12.13"
resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.12.13.tgz#8478bcc5cacf6aa1672b251c1d2dde5ccd61a6c4"
@@ -218,6 +240,13 @@
dependencies:
"@babel/types" "^7.12.13"
+"@babel/helper-simple-access@^7.13.12":
+ version "7.13.12"
+ resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.13.12.tgz#dd6c538afb61819d205a012c31792a39c7a5eaf6"
+ integrity sha512-7FEjbrx5SL9cWvXioDbnlYTppcZGuCY6ow3/D5vMggb2Ywgu4dMrpTJX0JdQAIcRRUElOIxF3yEooa9gUb9ZbA==
+ dependencies:
+ "@babel/types" "^7.13.12"
+
"@babel/helper-skip-transparent-expression-wrappers@^7.12.1":
version "7.12.1"
resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.12.1.tgz#462dc63a7e435ade8468385c63d2b84cce4b3cbf"
@@ -270,10 +299,19 @@
chalk "^2.0.0"
js-tokens "^4.0.0"
-"@babel/parser@^7.12.13", "@babel/parser@^7.13.0", "@babel/parser@^7.13.10":
- version "7.13.10"
- resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.13.10.tgz#8f8f9bf7b3afa3eabd061f7a5bcdf4fec3c48409"
- integrity sha512-0s7Mlrw9uTWkYua7xWr99Wpk2bnGa0ANleKfksYAES8LpWH4gW1OUr42vqKNf0us5UQNfru2wPqMqRITzq/SIQ==
+"@babel/parser@^7.12.13", "@babel/parser@^7.13.13":
+ version "7.13.13"
+ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.13.13.tgz#42f03862f4aed50461e543270916b47dd501f0df"
+ integrity sha512-OhsyMrqygfk5v8HmWwOzlYjJrtLaFhF34MrfG/Z73DgYCI6ojNUTUp2TYbtnjo8PegeJp12eamsNettCQjKjVw==
+
+"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.13.12":
+ version "7.13.12"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.13.12.tgz#a3484d84d0b549f3fc916b99ee4783f26fabad2a"
+ integrity sha512-d0u3zWKcoZf379fOeJdr1a5WPDny4aOFZ6hlfKivgK0LY7ZxNfoaHL2fWwdGtHyVvra38FC+HVYkO+byfSA8AQ==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.13.0"
+ "@babel/helper-skip-transparent-expression-wrappers" "^7.12.1"
+ "@babel/plugin-proposal-optional-chaining" "^7.13.12"
"@babel/plugin-proposal-async-generator-functions@^7.13.8":
version "7.13.8"
@@ -359,10 +397,10 @@
"@babel/helper-plugin-utils" "^7.13.0"
"@babel/plugin-syntax-optional-catch-binding" "^7.8.3"
-"@babel/plugin-proposal-optional-chaining@^7.13.8":
- version "7.13.8"
- resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.13.8.tgz#e39df93efe7e7e621841babc197982e140e90756"
- integrity sha512-hpbBwbTgd7Cz1QryvwJZRo1U0k1q8uyBmeXOSQUjdg/A2TASkhR/rz7AyqZ/kS8kbpsNA80rOYbxySBJAqmhhQ==
+"@babel/plugin-proposal-optional-chaining@^7.13.12":
+ version "7.13.12"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.13.12.tgz#ba9feb601d422e0adea6760c2bd6bbb7bfec4866"
+ integrity sha512-fcEdKOkIB7Tf4IxrgEVeFC4zeJSTr78no9wTdBuZZbqF64kzllU0ybo2zrzm7gUQfxGhBgq4E39oRs8Zx/RMYQ==
dependencies:
"@babel/helper-plugin-utils" "^7.13.0"
"@babel/helper-skip-transparent-expression-wrappers" "^7.12.1"
@@ -744,15 +782,16 @@
"@babel/helper-create-regexp-features-plugin" "^7.12.13"
"@babel/helper-plugin-utils" "^7.12.13"
-"@babel/preset-env@7.13.10":
- version "7.13.10"
- resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.13.10.tgz#b5cde31d5fe77ab2a6ab3d453b59041a1b3a5252"
- integrity sha512-nOsTScuoRghRtUsRr/c69d042ysfPHcu+KOB4A9aAO9eJYqrkat+LF8G1yp1HD18QiwixT2CisZTr/0b3YZPXQ==
+"@babel/preset-env@7.13.12":
+ version "7.13.12"
+ resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.13.12.tgz#6dff470478290582ac282fb77780eadf32480237"
+ integrity sha512-JzElc6jk3Ko6zuZgBtjOd01pf9yYDEIH8BcqVuYIuOkzOwDesoa/Nz4gIo4lBG6K861KTV9TvIgmFuT6ytOaAA==
dependencies:
- "@babel/compat-data" "^7.13.8"
+ "@babel/compat-data" "^7.13.12"
"@babel/helper-compilation-targets" "^7.13.10"
"@babel/helper-plugin-utils" "^7.13.0"
"@babel/helper-validator-option" "^7.12.17"
+ "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.13.12"
"@babel/plugin-proposal-async-generator-functions" "^7.13.8"
"@babel/plugin-proposal-class-properties" "^7.13.0"
"@babel/plugin-proposal-dynamic-import" "^7.13.8"
@@ -763,7 +802,7 @@
"@babel/plugin-proposal-numeric-separator" "^7.12.13"
"@babel/plugin-proposal-object-rest-spread" "^7.13.8"
"@babel/plugin-proposal-optional-catch-binding" "^7.13.8"
- "@babel/plugin-proposal-optional-chaining" "^7.13.8"
+ "@babel/plugin-proposal-optional-chaining" "^7.13.12"
"@babel/plugin-proposal-private-methods" "^7.13.0"
"@babel/plugin-proposal-unicode-property-regex" "^7.12.13"
"@babel/plugin-syntax-async-generators" "^7.8.4"
@@ -811,7 +850,7 @@
"@babel/plugin-transform-unicode-escapes" "^7.12.13"
"@babel/plugin-transform-unicode-regex" "^7.12.13"
"@babel/preset-modules" "^0.1.4"
- "@babel/types" "^7.13.0"
+ "@babel/types" "^7.13.12"
babel-plugin-polyfill-corejs2 "^0.1.4"
babel-plugin-polyfill-corejs3 "^0.1.3"
babel-plugin-polyfill-regenerator "^0.1.2"
@@ -854,25 +893,24 @@
"@babel/parser" "^7.12.13"
"@babel/types" "^7.12.13"
-"@babel/traverse@^7.12.13", "@babel/traverse@^7.13.0":
- version "7.13.0"
- resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.13.0.tgz#6d95752475f86ee7ded06536de309a65fc8966cc"
- integrity sha512-xys5xi5JEhzC3RzEmSGrs/b3pJW/o87SypZ+G/PhaE7uqVQNv/jlmVIBXuoh5atqQ434LfXV+sf23Oxj0bchJQ==
+"@babel/traverse@^7.12.13", "@babel/traverse@^7.13.0", "@babel/traverse@^7.13.13":
+ version "7.13.13"
+ resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.13.13.tgz#39aa9c21aab69f74d948a486dd28a2dbdbf5114d"
+ integrity sha512-CblEcwmXKR6eP43oQGG++0QMTtCjAsa3frUuzHoiIJWpaIIi8dwMyEFUJoXRLxagGqCK+jALRwIO+o3R9p/uUg==
dependencies:
"@babel/code-frame" "^7.12.13"
- "@babel/generator" "^7.13.0"
+ "@babel/generator" "^7.13.9"
"@babel/helper-function-name" "^7.12.13"
"@babel/helper-split-export-declaration" "^7.12.13"
- "@babel/parser" "^7.13.0"
- "@babel/types" "^7.13.0"
+ "@babel/parser" "^7.13.13"
+ "@babel/types" "^7.13.13"
debug "^4.1.0"
globals "^11.1.0"
- lodash "^4.17.19"
-"@babel/types@^7.12.1", "@babel/types@^7.12.13", "@babel/types@^7.13.0", "@babel/types@^7.4.4":
- version "7.13.0"
- resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.13.0.tgz#74424d2816f0171b4100f0ab34e9a374efdf7f80"
- integrity sha512-hE+HE8rnG1Z6Wzo+MhaKE5lM5eMx71T4EHJgku2E3xIfaULhDcxiiRxUYgwX8qwP1BBSlag+TdGOt6JAidIZTA==
+"@babel/types@^7.12.1", "@babel/types@^7.12.13", "@babel/types@^7.13.0", "@babel/types@^7.13.12", "@babel/types@^7.13.13", "@babel/types@^7.13.14", "@babel/types@^7.4.4":
+ version "7.13.14"
+ resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.13.14.tgz#c35a4abb15c7cd45a2746d78ab328e362cbace0d"
+ integrity sha512-A2aa3QTkWoyqsZZFl56MLUsfmh7O0gN41IPvXAE/++8ojpbz12SszD7JEGYVdn4f9Kt4amIei07swF1h4AqmmQ==
dependencies:
"@babel/helper-validator-identifier" "^7.12.11"
lodash "^4.17.19"
@@ -1040,22 +1078,22 @@
"@webassemblyjs/ast" "1.11.0"
"@xtuc/long" "4.2.2"
-"@webpack-cli/configtest@^1.0.1":
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/@webpack-cli/configtest/-/configtest-1.0.1.tgz#241aecfbdc715eee96bed447ed402e12ec171935"
- integrity sha512-B+4uBUYhpzDXmwuo3V9yBH6cISwxEI4J+NO5ggDaGEEHb0osY/R7MzeKc0bHURXQuZjMM4qD+bSJCKIuI3eNBQ==
+"@webpack-cli/configtest@^1.0.2":
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/@webpack-cli/configtest/-/configtest-1.0.2.tgz#2a20812bfb3a2ebb0b27ee26a52eeb3e3f000836"
+ integrity sha512-3OBzV2fBGZ5TBfdW50cha1lHDVf9vlvRXnjpVbJBa20pSZQaSkMJZiwA8V2vD9ogyeXn8nU5s5A6mHyf5jhMzA==
-"@webpack-cli/info@^1.2.2":
- version "1.2.2"
- resolved "https://registry.yarnpkg.com/@webpack-cli/info/-/info-1.2.2.tgz#ef3c0cd947a1fa083e174a59cb74e0b6195c236c"
- integrity sha512-5U9kUJHnwU+FhKH4PWGZuBC1hTEPYyxGSL5jjoBI96Gx8qcYJGOikpiIpFoTq8mmgX3im2zAo2wanv/alD74KQ==
+"@webpack-cli/info@^1.2.3":
+ version "1.2.3"
+ resolved "https://registry.yarnpkg.com/@webpack-cli/info/-/info-1.2.3.tgz#ef819d10ace2976b6d134c7c823a3e79ee31a92c"
+ integrity sha512-lLek3/T7u40lTqzCGpC6CAbY6+vXhdhmwFRxZLMnRm6/sIF/7qMpT8MocXCRQfz0JAh63wpbXLMnsQ5162WS7Q==
dependencies:
envinfo "^7.7.3"
-"@webpack-cli/serve@^1.3.0":
- version "1.3.0"
- resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-1.3.0.tgz#2730c770f5f1f132767c63dcaaa4ec28f8c56a6c"
- integrity sha512-k2p2VrONcYVX1wRRrf0f3X2VGltLWcv+JzXRBDmvCxGlCeESx4OXw91TsWeKOkp784uNoVQo313vxJFHXPPwfw==
+"@webpack-cli/serve@^1.3.1":
+ version "1.3.1"
+ resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-1.3.1.tgz#911d1b3ff4a843304b9c3bacf67bb34672418441"
+ integrity sha512-0qXvpeYO6vaNoRBI52/UsbcaBydJCggoBBnIo/ovQQdn6fug0BgwsjorV1hVS7fMqGVTZGcVxv8334gjmbj5hw==
"@xtuc/ieee754@^1.2.0":
version "1.2.0"
@@ -1375,12 +1413,7 @@ color-name@~1.1.4:
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
-colorette@^1.2.1:
- version "1.2.1"
- resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.1.tgz#4d0b921325c14faf92633086a536db6e89564b1b"
- integrity sha512-puCDz0CzydiSYOrnXpz/PKd69zRrribezjtE9yd4zvytoRc8+RY/KJPvtPFKZS3E3wP6neGyMe0vOTlHO5L3Pw==
-
-colorette@^1.2.2:
+colorette@^1.2.1, colorette@^1.2.2:
version "1.2.2"
resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.2.tgz#cbcc79d5e99caea2dbf10eb3a26fd8b3e6acfa94"
integrity sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==
@@ -1446,10 +1479,10 @@ cross-spawn@^7.0.1, cross-spawn@^7.0.3:
shebang-command "^2.0.0"
which "^2.0.1"
-css-loader@5.1.3:
- version "5.1.3"
- resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-5.1.3.tgz#87f6fc96816b20debe3cf682f85c7e56a963d0d1"
- integrity sha512-CoPZvyh8sLiGARK3gqczpfdedbM74klGWurF2CsNZ2lhNaXdLIUks+3Mfax3WBeRuHoglU+m7KG/+7gY6G4aag==
+css-loader@5.2.0:
+ version "5.2.0"
+ resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-5.2.0.tgz#a9ecda190500863673ce4434033710404efbff00"
+ integrity sha512-MfRo2MjEeLXMlUkeUwN71Vx5oc6EJnx5UQ4Yi9iUtYQvrPtwLUucYptz0hc6n++kdNcyF5olYBS4vPjJDAcLkw==
dependencies:
camelcase "^6.2.0"
cssesc "^3.0.0"
@@ -2475,10 +2508,10 @@ totalist@^1.0.0:
resolved "https://registry.yarnpkg.com/totalist/-/totalist-1.1.0.tgz#a4d65a3e546517701e3e5c37a47a70ac97fe56df"
integrity sha512-gduQwd1rOdDMGxFG1gEvhV88Oirdo2p+KjoYFU7k2g+i7n6AFFbDQ5kMPUsW0pNbfQsB/cwXvT1i4Bue0s9g5g==
-ts-loader@8.0.18:
- version "8.0.18"
- resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-8.0.18.tgz#b2385cbe81c34ad9f997915129cdde3ad92a61ea"
- integrity sha512-hRZzkydPX30XkLaQwJTDcWDoxZHK6IrEMDQpNd7tgcakFruFkeUp/aY+9hBb7BUGb+ZWKI0jiOGMo0MckwzdDQ==
+ts-loader@8.1.0:
+ version "8.1.0"
+ resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-8.1.0.tgz#d6292487df279c7cc79b6d3b70bb9d31682b693e"
+ integrity sha512-YiQipGGAFj2zBfqLhp28yUvPP9jUGqHxRzrGYuc82Z2wM27YIHbElXiaZDc93c3x0mz4zvBmS6q/DgExpdj37A==
dependencies:
chalk "^4.1.0"
enhanced-resolve "^4.0.0"
@@ -2559,15 +2592,15 @@ webpack-bundle-analyzer@4.4.0:
sirv "^1.0.7"
ws "^7.3.1"
-webpack-cli@4.5.0:
- version "4.5.0"
- resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-4.5.0.tgz#b5213b84adf6e1f5de6391334c9fa53a48850466"
- integrity sha512-wXg/ef6Ibstl2f50mnkcHblRPN/P9J4Nlod5Hg9HGFgSeF8rsqDGHJeVe4aR26q9l62TUJi6vmvC2Qz96YJw1Q==
+webpack-cli@4.6.0:
+ version "4.6.0"
+ resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-4.6.0.tgz#27ae86bfaec0cf393fcfd58abdc5a229ad32fd16"
+ integrity sha512-9YV+qTcGMjQFiY7Nb1kmnupvb1x40lfpj8pwdO/bom+sQiP4OBMKjHq29YQrlDWDPZO9r/qWaRRywKaRDKqBTA==
dependencies:
"@discoveryjs/json-ext" "^0.5.0"
- "@webpack-cli/configtest" "^1.0.1"
- "@webpack-cli/info" "^1.2.2"
- "@webpack-cli/serve" "^1.3.0"
+ "@webpack-cli/configtest" "^1.0.2"
+ "@webpack-cli/info" "^1.2.3"
+ "@webpack-cli/serve" "^1.3.1"
colorette "^1.2.1"
commander "^7.0.0"
enquirer "^2.3.6"
@@ -2595,10 +2628,10 @@ webpack-sources@^2.1.1:
source-list-map "^2.0.1"
source-map "^0.6.1"
-webpack@5.27.1:
- version "5.27.1"
- resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.27.1.tgz#6808fb6e45e35290cdb8ae43c7a10884839a3079"
- integrity sha512-rxIDsPZ3Apl3JcqiemiLmWH+hAq04YeOXqvCxNZOnTp8ZgM9NEPtbu4CaMfMEf9KShnx/Ym8uLGmM6P4XnwCoA==
+webpack@5.30.0:
+ version "5.30.0"
+ resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.30.0.tgz#07d87c182a060e0c2491062f3dc0edc85a29d884"
+ integrity sha512-Zr9NIri5yzpfmaMea2lSMV1UygbW0zQsSlGLMgKUm63ACXg6alhd1u4v5UBSBjzYKXJN6BNMGVM7w165e7NxYA==
dependencies:
"@types/eslint-scope" "^3.7.0"
"@types/estree" "^0.0.46"
diff --git a/src/renderer/renderElement.ts b/src/renderer/renderElement.ts
index 89048ba19..9485a033f 100644
--- a/src/renderer/renderElement.ts
+++ b/src/renderer/renderElement.ts
@@ -349,12 +349,12 @@ const generateElementShape = (
if (element.type === "arrow") {
const { startArrowhead = null, endArrowhead = "arrow" } = element;
- function getArrowheadShapes(
+ const getArrowheadShapes = (
element: ExcalidrawLinearElement,
shape: Drawable[],
position: "start" | "end",
arrowhead: Arrowhead,
- ) {
+ ) => {
const arrowheadPoints = getArrowheadPoints(
element,
shape,
@@ -392,7 +392,7 @@ const generateElementShape = (
generator.line(x3, y3, x2, y2, options),
generator.line(x4, y4, x2, y2, options),
];
- }
+ };
if (startArrowhead !== null) {
const shapes = getArrowheadShapes(
diff --git a/src/renderer/renderScene.ts b/src/renderer/renderScene.ts
index ae6a54335..d96bedc9f 100644
--- a/src/renderer/renderScene.ts
+++ b/src/renderer/renderScene.ts
@@ -48,7 +48,7 @@ import {
TransformHandleType,
} from "../element/transformHandles";
import { viewportCoordsToSceneCoords, supportsEmoji } from "../utils";
-import { UserIdleState } from "../excalidraw-app/collab/types";
+import { UserIdleState } from "../types";
import { THEME_FILTER } from "../constants";
const hasEmojiSupport = supportsEmoji();
diff --git a/src/tests/__snapshots__/excalidrawPackage.test.tsx.snap b/src/tests/__snapshots__/excalidrawPackage.test.tsx.snap
new file mode 100644
index 000000000..e4f486896
--- /dev/null
+++ b/src/tests/__snapshots__/excalidrawPackage.test.tsx.snap
@@ -0,0 +1,439 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[` Test UIOptions prop Test canvasActions should not hide any UI element when canvasActions is "undefined" 1`] = `
+
+
+ Canvas actions
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+`;
+
+exports[` Test UIOptions prop should not hide any UI element when the UIOptions prop is "undefined" 1`] = `
+
+
+ Canvas actions
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+`;
diff --git a/src/tests/__snapshots__/regressionTests.test.tsx.snap b/src/tests/__snapshots__/regressionTests.test.tsx.snap
index 9925cd72b..f1ca8a63f 100644
--- a/src/tests/__snapshots__/regressionTests.test.tsx.snap
+++ b/src/tests/__snapshots__/regressionTests.test.tsx.snap
@@ -460,7 +460,7 @@ Object {
exports[`given element A and group of elements B and given both are selected when user clicks on B, on pointer up only elements from B should be selected: [end of test] number of elements 1`] = `3`;
-exports[`given element A and group of elements B and given both are selected when user clicks on B, on pointer up only elements from B should be selected: [end of test] number of renders 1`] = `26`;
+exports[`given element A and group of elements B and given both are selected when user clicks on B, on pointer up only elements from B should be selected: [end of test] number of renders 1`] = `27`;
exports[`given element A and group of elements B and given both are selected when user shift-clicks on B, on pointer up only element A should be selected: [end of test] appState 1`] = `
Object {
@@ -928,7 +928,7 @@ Object {
exports[`given element A and group of elements B and given both are selected when user shift-clicks on B, on pointer up only element A should be selected: [end of test] number of elements 1`] = `3`;
-exports[`given element A and group of elements B and given both are selected when user shift-clicks on B, on pointer up only element A should be selected: [end of test] number of renders 1`] = `22`;
+exports[`given element A and group of elements B and given both are selected when user shift-clicks on B, on pointer up only element A should be selected: [end of test] number of renders 1`] = `23`;
exports[`regression tests Cmd/Ctrl-click exclusively select element under pointer: [end of test] appState 1`] = `
Object {
@@ -1705,7 +1705,7 @@ Object {
exports[`regression tests Cmd/Ctrl-click exclusively select element under pointer: [end of test] number of elements 1`] = `3`;
-exports[`regression tests Cmd/Ctrl-click exclusively select element under pointer: [end of test] number of renders 1`] = `41`;
+exports[`regression tests Cmd/Ctrl-click exclusively select element under pointer: [end of test] number of renders 1`] = `42`;
exports[`regression tests Drags selected element when hitting only bounding box and keeps element selected: [end of test] appState 1`] = `
Object {
@@ -1910,7 +1910,7 @@ Object {
exports[`regression tests Drags selected element when hitting only bounding box and keeps element selected: [end of test] number of elements 1`] = `1`;
-exports[`regression tests Drags selected element when hitting only bounding box and keeps element selected: [end of test] number of renders 1`] = `10`;
+exports[`regression tests Drags selected element when hitting only bounding box and keeps element selected: [end of test] number of renders 1`] = `11`;
exports[`regression tests adjusts z order when grouping: [end of test] appState 1`] = `
Object {
@@ -2369,7 +2369,7 @@ Object {
exports[`regression tests adjusts z order when grouping: [end of test] number of elements 1`] = `3`;
-exports[`regression tests adjusts z order when grouping: [end of test] number of renders 1`] = `20`;
+exports[`regression tests adjusts z order when grouping: [end of test] number of renders 1`] = `21`;
exports[`regression tests alt-drag duplicates an element: [end of test] appState 1`] = `
Object {
@@ -2623,7 +2623,7 @@ Object {
exports[`regression tests alt-drag duplicates an element: [end of test] number of elements 1`] = `2`;
-exports[`regression tests alt-drag duplicates an element: [end of test] number of renders 1`] = `10`;
+exports[`regression tests alt-drag duplicates an element: [end of test] number of renders 1`] = `11`;
exports[`regression tests arrow keys: [end of test] appState 1`] = `
Object {
@@ -2788,7 +2788,7 @@ Object {
exports[`regression tests arrow keys: [end of test] number of elements 1`] = `1`;
-exports[`regression tests arrow keys: [end of test] number of renders 1`] = `19`;
+exports[`regression tests arrow keys: [end of test] number of renders 1`] = `20`;
exports[`regression tests can drag element that covers another element, while another elem is selected: [end of test] appState 1`] = `
Object {
@@ -3266,7 +3266,7 @@ Object {
exports[`regression tests can drag element that covers another element, while another elem is selected: [end of test] number of elements 1`] = `3`;
-exports[`regression tests can drag element that covers another element, while another elem is selected: [end of test] number of renders 1`] = `18`;
+exports[`regression tests can drag element that covers another element, while another elem is selected: [end of test] number of renders 1`] = `19`;
exports[`regression tests change the properties of a shape: [end of test] appState 1`] = `
Object {
@@ -3503,7 +3503,7 @@ Object {
exports[`regression tests change the properties of a shape: [end of test] number of elements 1`] = `1`;
-exports[`regression tests change the properties of a shape: [end of test] number of renders 1`] = `11`;
+exports[`regression tests change the properties of a shape: [end of test] number of renders 1`] = `12`;
exports[`regression tests click on an element and drag it: [dragged] appState 1`] = `
Object {
@@ -3708,7 +3708,7 @@ Object {
exports[`regression tests click on an element and drag it: [dragged] number of elements 1`] = `1`;
-exports[`regression tests click on an element and drag it: [dragged] number of renders 1`] = `10`;
+exports[`regression tests click on an element and drag it: [dragged] number of renders 1`] = `11`;
exports[`regression tests click on an element and drag it: [end of test] appState 1`] = `
Object {
@@ -3953,7 +3953,7 @@ Object {
exports[`regression tests click on an element and drag it: [end of test] number of elements 1`] = `1`;
-exports[`regression tests click on an element and drag it: [end of test] number of renders 1`] = `13`;
+exports[`regression tests click on an element and drag it: [end of test] number of renders 1`] = `14`;
exports[`regression tests click to select a shape: [end of test] appState 1`] = `
Object {
@@ -4206,7 +4206,7 @@ Object {
exports[`regression tests click to select a shape: [end of test] number of elements 1`] = `2`;
-exports[`regression tests click to select a shape: [end of test] number of renders 1`] = `13`;
+exports[`regression tests click to select a shape: [end of test] number of renders 1`] = `14`;
exports[`regression tests click-drag to select a group: [end of test] appState 1`] = `
Object {
@@ -4568,7 +4568,7 @@ Object {
exports[`regression tests click-drag to select a group: [end of test] number of elements 1`] = `3`;
-exports[`regression tests click-drag to select a group: [end of test] number of renders 1`] = `19`;
+exports[`regression tests click-drag to select a group: [end of test] number of renders 1`] = `20`;
exports[`regression tests deselects group of selected elements on pointer down when pointer doesn't hit any element: [end of test] appState 1`] = `
Object {
@@ -4864,7 +4864,7 @@ Object {
exports[`regression tests deselects group of selected elements on pointer down when pointer doesn't hit any element: [end of test] number of elements 1`] = `2`;
-exports[`regression tests deselects group of selected elements on pointer down when pointer doesn't hit any element: [end of test] number of renders 1`] = `14`;
+exports[`regression tests deselects group of selected elements on pointer down when pointer doesn't hit any element: [end of test] number of renders 1`] = `15`;
exports[`regression tests deselects group of selected elements on pointer up when pointer hits common bounding box without hitting any element: [end of test] appState 1`] = `
Object {
@@ -5172,7 +5172,7 @@ Object {
exports[`regression tests deselects group of selected elements on pointer up when pointer hits common bounding box without hitting any element: [end of test] number of elements 1`] = `2`;
-exports[`regression tests deselects group of selected elements on pointer up when pointer hits common bounding box without hitting any element: [end of test] number of renders 1`] = `15`;
+exports[`regression tests deselects group of selected elements on pointer up when pointer hits common bounding box without hitting any element: [end of test] number of renders 1`] = `16`;
exports[`regression tests deselects selected element on pointer down when pointer doesn't hit any element: [end of test] appState 1`] = `
Object {
@@ -5381,7 +5381,7 @@ Object {
exports[`regression tests deselects selected element on pointer down when pointer doesn't hit any element: [end of test] number of elements 1`] = `1`;
-exports[`regression tests deselects selected element on pointer down when pointer doesn't hit any element: [end of test] number of renders 1`] = `8`;
+exports[`regression tests deselects selected element on pointer down when pointer doesn't hit any element: [end of test] number of renders 1`] = `9`;
exports[`regression tests deselects selected element, on pointer up, when click hits element bounding box but doesn't hit the element: [end of test] appState 1`] = `
Object {
@@ -5568,7 +5568,7 @@ Object {
exports[`regression tests deselects selected element, on pointer up, when click hits element bounding box but doesn't hit the element: [end of test] number of elements 1`] = `1`;
-exports[`regression tests deselects selected element, on pointer up, when click hits element bounding box but doesn't hit the element: [end of test] number of renders 1`] = `9`;
+exports[`regression tests deselects selected element, on pointer up, when click hits element bounding box but doesn't hit the element: [end of test] number of renders 1`] = `10`;
exports[`regression tests double click to edit a group: [end of test] appState 1`] = `
Object {
@@ -6022,7 +6022,7 @@ Object {
exports[`regression tests double click to edit a group: [end of test] number of elements 1`] = `3`;
-exports[`regression tests double click to edit a group: [end of test] number of renders 1`] = `18`;
+exports[`regression tests double click to edit a group: [end of test] number of renders 1`] = `19`;
exports[`regression tests drags selected elements from point inside common bounding box that doesn't hit any element and keeps elements selected after dragging: [end of test] appState 1`] = `
Object {
@@ -6341,7 +6341,7 @@ Object {
exports[`regression tests drags selected elements from point inside common bounding box that doesn't hit any element and keeps elements selected after dragging: [end of test] number of elements 1`] = `2`;
-exports[`regression tests drags selected elements from point inside common bounding box that doesn't hit any element and keeps elements selected after dragging: [end of test] number of renders 1`] = `16`;
+exports[`regression tests drags selected elements from point inside common bounding box that doesn't hit any element and keeps elements selected after dragging: [end of test] number of renders 1`] = `17`;
exports[`regression tests draw every type of shape: [end of test] appState 1`] = `
Object {
@@ -8376,7 +8376,7 @@ Object {
exports[`regression tests draw every type of shape: [end of test] number of elements 1`] = `8`;
-exports[`regression tests draw every type of shape: [end of test] number of renders 1`] = `51`;
+exports[`regression tests draw every type of shape: [end of test] number of renders 1`] = `52`;
exports[`regression tests given a group of selected elements with an element that is not selected inside the group common bounding box when element that is not selected is clicked should switch selection to not selected element on pointer up: [end of test] appState 1`] = `
Object {
@@ -8739,7 +8739,7 @@ Object {
exports[`regression tests given a group of selected elements with an element that is not selected inside the group common bounding box when element that is not selected is clicked should switch selection to not selected element on pointer up: [end of test] number of elements 1`] = `3`;
-exports[`regression tests given a group of selected elements with an element that is not selected inside the group common bounding box when element that is not selected is clicked should switch selection to not selected element on pointer up: [end of test] number of renders 1`] = `19`;
+exports[`regression tests given a group of selected elements with an element that is not selected inside the group common bounding box when element that is not selected is clicked should switch selection to not selected element on pointer up: [end of test] number of renders 1`] = `20`;
exports[`regression tests given a selected element A and a not selected element B with higher z-index than A and given B partialy overlaps A when there's a shift-click on the overlapped section B is added to the selection: [end of test] appState 1`] = `
Object {
@@ -8995,7 +8995,7 @@ Object {
exports[`regression tests given a selected element A and a not selected element B with higher z-index than A and given B partialy overlaps A when there's a shift-click on the overlapped section B is added to the selection: [end of test] number of elements 1`] = `2`;
-exports[`regression tests given a selected element A and a not selected element B with higher z-index than A and given B partialy overlaps A when there's a shift-click on the overlapped section B is added to the selection: [end of test] number of renders 1`] = `17`;
+exports[`regression tests given a selected element A and a not selected element B with higher z-index than A and given B partialy overlaps A when there's a shift-click on the overlapped section B is added to the selection: [end of test] number of renders 1`] = `18`;
exports[`regression tests given selected element A with lower z-index than unselected element B and given B is partially over A when clicking intersection between A and B B should be selected on pointer up: [end of test] appState 1`] = `
Object {
@@ -9249,7 +9249,7 @@ Object {
exports[`regression tests given selected element A with lower z-index than unselected element B and given B is partially over A when clicking intersection between A and B B should be selected on pointer up: [end of test] number of elements 1`] = `2`;
-exports[`regression tests given selected element A with lower z-index than unselected element B and given B is partially over A when clicking intersection between A and B B should be selected on pointer up: [end of test] number of renders 1`] = `17`;
+exports[`regression tests given selected element A with lower z-index than unselected element B and given B is partially over A when clicking intersection between A and B B should be selected on pointer up: [end of test] number of renders 1`] = `18`;
exports[`regression tests given selected element A with lower z-index than unselected element B and given B is partially over A when dragging on intersection between A and B A should be dragged and keep being selected: [end of test] appState 1`] = `
Object {
@@ -9565,7 +9565,7 @@ Object {
exports[`regression tests given selected element A with lower z-index than unselected element B and given B is partially over A when dragging on intersection between A and B A should be dragged and keep being selected: [end of test] number of elements 1`] = `2`;
-exports[`regression tests given selected element A with lower z-index than unselected element B and given B is partially over A when dragging on intersection between A and B A should be dragged and keep being selected: [end of test] number of renders 1`] = `18`;
+exports[`regression tests given selected element A with lower z-index than unselected element B and given B is partially over A when dragging on intersection between A and B A should be dragged and keep being selected: [end of test] number of renders 1`] = `19`;
exports[`regression tests key 2 selects rectangle tool: [end of test] appState 1`] = `
Object {
@@ -9730,7 +9730,7 @@ Object {
exports[`regression tests key 2 selects rectangle tool: [end of test] number of elements 1`] = `1`;
-exports[`regression tests key 2 selects rectangle tool: [end of test] number of renders 1`] = `7`;
+exports[`regression tests key 2 selects rectangle tool: [end of test] number of renders 1`] = `8`;
exports[`regression tests key 3 selects diamond tool: [end of test] appState 1`] = `
Object {
@@ -9895,7 +9895,7 @@ Object {
exports[`regression tests key 3 selects diamond tool: [end of test] number of elements 1`] = `1`;
-exports[`regression tests key 3 selects diamond tool: [end of test] number of renders 1`] = `7`;
+exports[`regression tests key 3 selects diamond tool: [end of test] number of renders 1`] = `8`;
exports[`regression tests key 4 selects ellipse tool: [end of test] appState 1`] = `
Object {
@@ -10060,7 +10060,7 @@ Object {
exports[`regression tests key 4 selects ellipse tool: [end of test] number of elements 1`] = `1`;
-exports[`regression tests key 4 selects ellipse tool: [end of test] number of renders 1`] = `7`;
+exports[`regression tests key 4 selects ellipse tool: [end of test] number of renders 1`] = `8`;
exports[`regression tests key 5 selects arrow tool: [end of test] appState 1`] = `
Object {
@@ -10255,7 +10255,7 @@ Object {
exports[`regression tests key 5 selects arrow tool: [end of test] number of elements 1`] = `1`;
-exports[`regression tests key 5 selects arrow tool: [end of test] number of renders 1`] = `8`;
+exports[`regression tests key 5 selects arrow tool: [end of test] number of renders 1`] = `9`;
exports[`regression tests key 6 selects line tool: [end of test] appState 1`] = `
Object {
@@ -10450,7 +10450,7 @@ Object {
exports[`regression tests key 6 selects line tool: [end of test] number of elements 1`] = `1`;
-exports[`regression tests key 6 selects line tool: [end of test] number of renders 1`] = `7`;
+exports[`regression tests key 6 selects line tool: [end of test] number of renders 1`] = `8`;
exports[`regression tests key 7 selects draw tool: [end of test] appState 1`] = `
Object {
@@ -10645,7 +10645,7 @@ Object {
exports[`regression tests key 7 selects draw tool: [end of test] number of elements 1`] = `1`;
-exports[`regression tests key 7 selects draw tool: [end of test] number of renders 1`] = `7`;
+exports[`regression tests key 7 selects draw tool: [end of test] number of renders 1`] = `8`;
exports[`regression tests key a selects arrow tool: [end of test] appState 1`] = `
Object {
@@ -10840,7 +10840,7 @@ Object {
exports[`regression tests key a selects arrow tool: [end of test] number of elements 1`] = `1`;
-exports[`regression tests key a selects arrow tool: [end of test] number of renders 1`] = `8`;
+exports[`regression tests key a selects arrow tool: [end of test] number of renders 1`] = `9`;
exports[`regression tests key d selects diamond tool: [end of test] appState 1`] = `
Object {
@@ -11005,7 +11005,7 @@ Object {
exports[`regression tests key d selects diamond tool: [end of test] number of elements 1`] = `1`;
-exports[`regression tests key d selects diamond tool: [end of test] number of renders 1`] = `7`;
+exports[`regression tests key d selects diamond tool: [end of test] number of renders 1`] = `8`;
exports[`regression tests key e selects ellipse tool: [end of test] appState 1`] = `
Object {
@@ -11170,7 +11170,7 @@ Object {
exports[`regression tests key e selects ellipse tool: [end of test] number of elements 1`] = `1`;
-exports[`regression tests key e selects ellipse tool: [end of test] number of renders 1`] = `7`;
+exports[`regression tests key e selects ellipse tool: [end of test] number of renders 1`] = `8`;
exports[`regression tests key l selects line tool: [end of test] appState 1`] = `
Object {
@@ -11365,7 +11365,7 @@ Object {
exports[`regression tests key l selects line tool: [end of test] number of elements 1`] = `1`;
-exports[`regression tests key l selects line tool: [end of test] number of renders 1`] = `7`;
+exports[`regression tests key l selects line tool: [end of test] number of renders 1`] = `8`;
exports[`regression tests key r selects rectangle tool: [end of test] appState 1`] = `
Object {
@@ -11530,7 +11530,7 @@ Object {
exports[`regression tests key r selects rectangle tool: [end of test] number of elements 1`] = `1`;
-exports[`regression tests key r selects rectangle tool: [end of test] number of renders 1`] = `7`;
+exports[`regression tests key r selects rectangle tool: [end of test] number of renders 1`] = `8`;
exports[`regression tests key x selects draw tool: [end of test] appState 1`] = `
Object {
@@ -11725,7 +11725,7 @@ Object {
exports[`regression tests key x selects draw tool: [end of test] number of elements 1`] = `1`;
-exports[`regression tests key x selects draw tool: [end of test] number of renders 1`] = `7`;
+exports[`regression tests key x selects draw tool: [end of test] number of renders 1`] = `8`;
exports[`regression tests make a group and duplicate it: [end of test] appState 1`] = `
Object {
@@ -12442,7 +12442,7 @@ Object {
exports[`regression tests make a group and duplicate it: [end of test] number of elements 1`] = `6`;
-exports[`regression tests make a group and duplicate it: [end of test] number of renders 1`] = `22`;
+exports[`regression tests make a group and duplicate it: [end of test] number of renders 1`] = `23`;
exports[`regression tests noop interaction after undo shouldn't create history entry: [end of test] appState 1`] = `
Object {
@@ -12696,7 +12696,7 @@ Object {
exports[`regression tests noop interaction after undo shouldn't create history entry: [end of test] number of elements 1`] = `2`;
-exports[`regression tests noop interaction after undo shouldn't create history entry: [end of test] number of renders 1`] = `19`;
+exports[`regression tests noop interaction after undo shouldn't create history entry: [end of test] number of renders 1`] = `20`;
exports[`regression tests pinch-to-zoom works: [end of test] appState 1`] = `
Object {
@@ -12799,7 +12799,7 @@ Object {
exports[`regression tests pinch-to-zoom works: [end of test] number of elements 1`] = `0`;
-exports[`regression tests pinch-to-zoom works: [end of test] number of renders 1`] = `9`;
+exports[`regression tests pinch-to-zoom works: [end of test] number of renders 1`] = `10`;
exports[`regression tests rerenders UI on language change: [end of test] appState 1`] = `
Object {
@@ -12900,7 +12900,7 @@ Object {
exports[`regression tests rerenders UI on language change: [end of test] number of elements 1`] = `0`;
-exports[`regression tests rerenders UI on language change: [end of test] number of renders 1`] = `8`;
+exports[`regression tests rerenders UI on language change: [end of test] number of renders 1`] = `9`;
exports[`regression tests selecting 'Add to library' in context menu adds element to library: [end of test] appState 1`] = `
Object {
@@ -13065,7 +13065,7 @@ Object {
exports[`regression tests selecting 'Add to library' in context menu adds element to library: [end of test] number of elements 1`] = `1`;
-exports[`regression tests selecting 'Add to library' in context menu adds element to library: [end of test] number of renders 1`] = `7`;
+exports[`regression tests selecting 'Add to library' in context menu adds element to library: [end of test] number of renders 1`] = `8`;
exports[`regression tests selecting 'Bring forward' in context menu brings element forward: [end of test] appState 1`] = `
Object {
@@ -13374,7 +13374,7 @@ Object {
exports[`regression tests selecting 'Bring forward' in context menu brings element forward: [end of test] number of elements 1`] = `2`;
-exports[`regression tests selecting 'Bring forward' in context menu brings element forward: [end of test] number of renders 1`] = `13`;
+exports[`regression tests selecting 'Bring forward' in context menu brings element forward: [end of test] number of renders 1`] = `14`;
exports[`regression tests selecting 'Bring to front' in context menu brings element to front: [end of test] appState 1`] = `
Object {
@@ -13683,7 +13683,7 @@ Object {
exports[`regression tests selecting 'Bring to front' in context menu brings element to front: [end of test] number of elements 1`] = `2`;
-exports[`regression tests selecting 'Bring to front' in context menu brings element to front: [end of test] number of renders 1`] = `13`;
+exports[`regression tests selecting 'Bring to front' in context menu brings element to front: [end of test] number of renders 1`] = `14`;
exports[`regression tests selecting 'Copy styles' in context menu copies styles: [end of test] appState 1`] = `
Object {
@@ -13848,7 +13848,7 @@ Object {
exports[`regression tests selecting 'Copy styles' in context menu copies styles: [end of test] number of elements 1`] = `1`;
-exports[`regression tests selecting 'Copy styles' in context menu copies styles: [end of test] number of renders 1`] = `8`;
+exports[`regression tests selecting 'Copy styles' in context menu copies styles: [end of test] number of renders 1`] = `9`;
exports[`regression tests selecting 'Delete' in context menu deletes element: [end of test] appState 1`] = `
Object {
@@ -14045,7 +14045,7 @@ Object {
exports[`regression tests selecting 'Delete' in context menu deletes element: [end of test] number of elements 1`] = `1`;
-exports[`regression tests selecting 'Delete' in context menu deletes element: [end of test] number of renders 1`] = `8`;
+exports[`regression tests selecting 'Delete' in context menu deletes element: [end of test] number of renders 1`] = `9`;
exports[`regression tests selecting 'Duplicate' in context menu duplicates element: [end of test] appState 1`] = `
Object {
@@ -14295,7 +14295,7 @@ Object {
exports[`regression tests selecting 'Duplicate' in context menu duplicates element: [end of test] number of elements 1`] = `2`;
-exports[`regression tests selecting 'Duplicate' in context menu duplicates element: [end of test] number of renders 1`] = `8`;
+exports[`regression tests selecting 'Duplicate' in context menu duplicates element: [end of test] number of renders 1`] = `9`;
exports[`regression tests selecting 'Group selection' in context menu groups selected elements: [end of test] appState 1`] = `
Object {
@@ -14620,7 +14620,7 @@ Object {
exports[`regression tests selecting 'Group selection' in context menu groups selected elements: [end of test] number of elements 1`] = `2`;
-exports[`regression tests selecting 'Group selection' in context menu groups selected elements: [end of test] number of renders 1`] = `14`;
+exports[`regression tests selecting 'Group selection' in context menu groups selected elements: [end of test] number of renders 1`] = `15`;
exports[`regression tests selecting 'Paste styles' in context menu pastes styles: [end of test] appState 1`] = `
Object {
@@ -15342,7 +15342,7 @@ Object {
exports[`regression tests selecting 'Paste styles' in context menu pastes styles: [end of test] number of elements 1`] = `2`;
-exports[`regression tests selecting 'Paste styles' in context menu pastes styles: [end of test] number of renders 1`] = `23`;
+exports[`regression tests selecting 'Paste styles' in context menu pastes styles: [end of test] number of renders 1`] = `24`;
exports[`regression tests selecting 'Send backward' in context menu sends element backward: [end of test] appState 1`] = `
Object {
@@ -15651,7 +15651,7 @@ Object {
exports[`regression tests selecting 'Send backward' in context menu sends element backward: [end of test] number of elements 1`] = `2`;
-exports[`regression tests selecting 'Send backward' in context menu sends element backward: [end of test] number of renders 1`] = `12`;
+exports[`regression tests selecting 'Send backward' in context menu sends element backward: [end of test] number of renders 1`] = `13`;
exports[`regression tests selecting 'Send to back' in context menu sends element to back: [end of test] appState 1`] = `
Object {
@@ -15960,7 +15960,7 @@ Object {
exports[`regression tests selecting 'Send to back' in context menu sends element to back: [end of test] number of elements 1`] = `2`;
-exports[`regression tests selecting 'Send to back' in context menu sends element to back: [end of test] number of renders 1`] = `12`;
+exports[`regression tests selecting 'Send to back' in context menu sends element to back: [end of test] number of renders 1`] = `13`;
exports[`regression tests selecting 'Ungroup selection' in context menu ungroups selected group: [end of test] appState 1`] = `
Object {
@@ -16340,7 +16340,7 @@ Object {
exports[`regression tests selecting 'Ungroup selection' in context menu ungroups selected group: [end of test] number of elements 1`] = `2`;
-exports[`regression tests selecting 'Ungroup selection' in context menu ungroups selected group: [end of test] number of renders 1`] = `15`;
+exports[`regression tests selecting 'Ungroup selection' in context menu ungroups selected group: [end of test] number of renders 1`] = `16`;
exports[`regression tests shift click on selected element should deselect it on pointer up: [end of test] appState 1`] = `
Object {
@@ -16508,7 +16508,7 @@ Object {
exports[`regression tests shift click on selected element should deselect it on pointer up: [end of test] number of elements 1`] = `1`;
-exports[`regression tests shift click on selected element should deselect it on pointer up: [end of test] number of renders 1`] = `9`;
+exports[`regression tests shift click on selected element should deselect it on pointer up: [end of test] number of renders 1`] = `10`;
exports[`regression tests shift-click to multiselect, then drag: [end of test] appState 1`] = `
Object {
@@ -16830,7 +16830,7 @@ Object {
exports[`regression tests shift-click to multiselect, then drag: [end of test] number of elements 1`] = `2`;
-exports[`regression tests shift-click to multiselect, then drag: [end of test] number of renders 1`] = `18`;
+exports[`regression tests shift-click to multiselect, then drag: [end of test] number of renders 1`] = `19`;
exports[`regression tests should show fill icons when element has non transparent background: [end of test] appState 1`] = `
Object {
@@ -17034,7 +17034,7 @@ Object {
exports[`regression tests should show fill icons when element has non transparent background: [end of test] number of elements 1`] = `1`;
-exports[`regression tests should show fill icons when element has non transparent background: [end of test] number of renders 1`] = `11`;
+exports[`regression tests should show fill icons when element has non transparent background: [end of test] number of renders 1`] = `12`;
exports[`regression tests shows 'Group selection' in context menu for multiple selected elements: [end of test] appState 1`] = `
Object {
@@ -17290,7 +17290,7 @@ Object {
exports[`regression tests shows 'Group selection' in context menu for multiple selected elements: [end of test] number of elements 1`] = `2`;
-exports[`regression tests shows 'Group selection' in context menu for multiple selected elements: [end of test] number of renders 1`] = `15`;
+exports[`regression tests shows 'Group selection' in context menu for multiple selected elements: [end of test] number of renders 1`] = `16`;
exports[`regression tests shows 'Ungroup selection' in context menu for group inside selected elements: [end of test] appState 1`] = `
Object {
@@ -17618,7 +17618,7 @@ Object {
exports[`regression tests shows 'Ungroup selection' in context menu for group inside selected elements: [end of test] number of elements 1`] = `2`;
-exports[`regression tests shows 'Ungroup selection' in context menu for group inside selected elements: [end of test] number of renders 1`] = `16`;
+exports[`regression tests shows 'Ungroup selection' in context menu for group inside selected elements: [end of test] number of renders 1`] = `17`;
exports[`regression tests shows context menu for canvas: [end of test] appState 1`] = `
Object {
@@ -17719,7 +17719,7 @@ Object {
exports[`regression tests shows context menu for canvas: [end of test] number of elements 1`] = `0`;
-exports[`regression tests shows context menu for canvas: [end of test] number of renders 1`] = `3`;
+exports[`regression tests shows context menu for canvas: [end of test] number of renders 1`] = `4`;
exports[`regression tests shows context menu for element: [end of test] appState 1`] = `
Object {
@@ -17884,7 +17884,7 @@ Object {
exports[`regression tests shows context menu for element: [end of test] number of elements 1`] = `1`;
-exports[`regression tests shows context menu for element: [end of test] number of renders 1`] = `7`;
+exports[`regression tests shows context menu for element: [end of test] number of renders 1`] = `8`;
exports[`regression tests single-clicking on a subgroup of a selected group should not alter selection: [end of test] appState 1`] = `
Object {
@@ -18706,7 +18706,7 @@ Object {
exports[`regression tests single-clicking on a subgroup of a selected group should not alter selection: [end of test] number of elements 1`] = `4`;
-exports[`regression tests single-clicking on a subgroup of a selected group should not alter selection: [end of test] number of renders 1`] = `37`;
+exports[`regression tests single-clicking on a subgroup of a selected group should not alter selection: [end of test] number of renders 1`] = `38`;
exports[`regression tests spacebar + drag scrolls the canvas: [end of test] appState 1`] = `
Object {
@@ -18807,7 +18807,7 @@ Object {
exports[`regression tests spacebar + drag scrolls the canvas: [end of test] number of elements 1`] = `0`;
-exports[`regression tests spacebar + drag scrolls the canvas: [end of test] number of renders 1`] = `6`;
+exports[`regression tests spacebar + drag scrolls the canvas: [end of test] number of renders 1`] = `7`;
exports[`regression tests supports nested groups: [end of test] appState 1`] = `
Object {
@@ -19540,7 +19540,7 @@ Object {
exports[`regression tests supports nested groups: [end of test] number of elements 1`] = `3`;
-exports[`regression tests supports nested groups: [end of test] number of renders 1`] = `30`;
+exports[`regression tests supports nested groups: [end of test] number of renders 1`] = `31`;
exports[`regression tests switches from group of selected elements to another element on pointer down: [end of test] appState 1`] = `
Object {
@@ -19946,7 +19946,7 @@ Object {
exports[`regression tests switches from group of selected elements to another element on pointer down: [end of test] number of elements 1`] = `3`;
-exports[`regression tests switches from group of selected elements to another element on pointer down: [end of test] number of renders 1`] = `18`;
+exports[`regression tests switches from group of selected elements to another element on pointer down: [end of test] number of renders 1`] = `19`;
exports[`regression tests switches selected element on pointer down: [end of test] appState 1`] = `
Object {
@@ -20242,7 +20242,7 @@ Object {
exports[`regression tests switches selected element on pointer down: [end of test] number of elements 1`] = `2`;
-exports[`regression tests switches selected element on pointer down: [end of test] number of renders 1`] = `12`;
+exports[`regression tests switches selected element on pointer down: [end of test] number of renders 1`] = `13`;
exports[`regression tests two-finger scroll works: [end of test] appState 1`] = `
Object {
@@ -20345,7 +20345,7 @@ Object {
exports[`regression tests two-finger scroll works: [end of test] number of elements 1`] = `0`;
-exports[`regression tests two-finger scroll works: [end of test] number of renders 1`] = `11`;
+exports[`regression tests two-finger scroll works: [end of test] number of renders 1`] = `12`;
exports[`regression tests undo/redo drawing an element: [end of test] appState 1`] = `
Object {
@@ -20844,7 +20844,7 @@ Object {
exports[`regression tests undo/redo drawing an element: [end of test] number of elements 1`] = `3`;
-exports[`regression tests undo/redo drawing an element: [end of test] number of renders 1`] = `28`;
+exports[`regression tests undo/redo drawing an element: [end of test] number of renders 1`] = `29`;
exports[`regression tests updates fontSize & fontFamily appState: [end of test] appState 1`] = `
Object {
@@ -20945,7 +20945,7 @@ Object {
exports[`regression tests updates fontSize & fontFamily appState: [end of test] number of elements 1`] = `0`;
-exports[`regression tests updates fontSize & fontFamily appState: [end of test] number of renders 1`] = `5`;
+exports[`regression tests updates fontSize & fontFamily appState: [end of test] number of renders 1`] = `6`;
exports[`regression tests zoom hotkeys: [end of test] appState 1`] = `
Object {
@@ -21046,4 +21046,4 @@ Object {
exports[`regression tests zoom hotkeys: [end of test] number of elements 1`] = `0`;
-exports[`regression tests zoom hotkeys: [end of test] number of renders 1`] = `5`;
+exports[`regression tests zoom hotkeys: [end of test] number of renders 1`] = `6`;
diff --git a/src/tests/__snapshots__/selection.test.tsx.snap b/src/tests/__snapshots__/selection.test.tsx.snap
index 5eb07b065..920bf5a0c 100644
--- a/src/tests/__snapshots__/selection.test.tsx.snap
+++ b/src/tests/__snapshots__/selection.test.tsx.snap
@@ -36,8 +36,8 @@ Object {
"version": 3,
"versionNonce": 449462985,
"width": 30,
- "x": 30,
- "y": 20,
+ "x": 10,
+ "y": 10,
}
`;
@@ -77,8 +77,8 @@ Object {
"version": 3,
"versionNonce": 449462985,
"width": 30,
- "x": 30,
- "y": 20,
+ "x": 10,
+ "y": 10,
}
`;
@@ -103,8 +103,8 @@ Object {
"version": 2,
"versionNonce": 1278240551,
"width": 30,
- "x": 30,
- "y": 20,
+ "x": 10,
+ "y": 10,
}
`;
@@ -129,8 +129,8 @@ Object {
"version": 2,
"versionNonce": 1278240551,
"width": 30,
- "x": 30,
- "y": 20,
+ "x": 10,
+ "y": 10,
}
`;
@@ -155,7 +155,7 @@ Object {
"version": 2,
"versionNonce": 1278240551,
"width": 30,
- "x": 30,
- "y": 20,
+ "x": 10,
+ "y": 10,
}
`;
diff --git a/src/tests/collab.test.tsx b/src/tests/collab.test.tsx
index ad76d2952..9065abbf8 100644
--- a/src/tests/collab.test.tsx
+++ b/src/tests/collab.test.tsx
@@ -60,7 +60,7 @@ describe("collaboration", () => {
]);
expect(API.getStateHistory().length).toBe(1);
});
- h.collab.openPortal();
+ window.collab.openPortal();
await waitFor(() => {
expect(h.elements).toEqual([expect.objectContaining({ id: "A" })]);
expect(API.getStateHistory().length).toBe(1);
diff --git a/src/tests/dragCreate.test.tsx b/src/tests/dragCreate.test.tsx
index 7a55ed89a..895f882d3 100644
--- a/src/tests/dragCreate.test.tsx
+++ b/src/tests/dragCreate.test.tsx
@@ -3,7 +3,12 @@ import ReactDOM from "react-dom";
import ExcalidrawApp from "../excalidraw-app";
import * as Renderer from "../renderer/renderScene";
import { KEYS } from "../keys";
-import { render, fireEvent } from "./test-utils";
+import {
+ render,
+ fireEvent,
+ mockBoundingClientRect,
+ restoreOriginalGetBoundingClientRect,
+} from "./test-utils";
import { ExcalidrawLinearElement } from "../element/types";
import { reseed } from "../random";
@@ -37,7 +42,7 @@ describe("add element to the scene when pointer dragging long enough", () => {
// finish (position does not matter)
fireEvent.pointerUp(canvas);
- expect(renderScene).toHaveBeenCalledTimes(7);
+ expect(renderScene).toHaveBeenCalledTimes(8);
expect(h.state.selectionElement).toBeNull();
expect(h.elements.length).toEqual(1);
@@ -68,7 +73,7 @@ describe("add element to the scene when pointer dragging long enough", () => {
// finish (position does not matter)
fireEvent.pointerUp(canvas);
- expect(renderScene).toHaveBeenCalledTimes(7);
+ expect(renderScene).toHaveBeenCalledTimes(8);
expect(h.state.selectionElement).toBeNull();
expect(h.elements.length).toEqual(1);
@@ -99,7 +104,7 @@ describe("add element to the scene when pointer dragging long enough", () => {
// finish (position does not matter)
fireEvent.pointerUp(canvas);
- expect(renderScene).toHaveBeenCalledTimes(7);
+ expect(renderScene).toHaveBeenCalledTimes(8);
expect(h.state.selectionElement).toBeNull();
expect(h.elements.length).toEqual(1);
@@ -130,7 +135,7 @@ describe("add element to the scene when pointer dragging long enough", () => {
// finish (position does not matter)
fireEvent.pointerUp(canvas);
- expect(renderScene).toHaveBeenCalledTimes(7);
+ expect(renderScene).toHaveBeenCalledTimes(8);
expect(h.state.selectionElement).toBeNull();
expect(h.elements.length).toEqual(1);
@@ -165,7 +170,7 @@ describe("add element to the scene when pointer dragging long enough", () => {
// finish (position does not matter)
fireEvent.pointerUp(canvas);
- expect(renderScene).toHaveBeenCalledTimes(7);
+ expect(renderScene).toHaveBeenCalledTimes(8);
expect(h.state.selectionElement).toBeNull();
expect(h.elements.length).toEqual(1);
@@ -184,6 +189,13 @@ describe("add element to the scene when pointer dragging long enough", () => {
});
describe("do not add element to the scene if size is too small", () => {
+ beforeAll(() => {
+ mockBoundingClientRect();
+ });
+ afterAll(() => {
+ restoreOriginalGetBoundingClientRect();
+ });
+
it("rectangle", async () => {
const { getByToolName, container } = await render();
// select tool
diff --git a/src/tests/excalidrawPackage.test.tsx b/src/tests/excalidrawPackage.test.tsx
index a64b73f39..f417630fe 100644
--- a/src/tests/excalidrawPackage.test.tsx
+++ b/src/tests/excalidrawPackage.test.tsx
@@ -130,4 +130,86 @@ describe("", () => {
expect(textInput?.nodeName).toBe("SPAN");
});
});
+
+ describe("Test UIOptions prop", () => {
+ it('should not hide any UI element when the UIOptions prop is "undefined"', async () => {
+ await render();
+
+ const canvasActions = document.querySelector(
+ 'section[aria-labelledby="canvasActions-title"]',
+ );
+
+ expect(canvasActions).toMatchSnapshot();
+ });
+
+ describe("Test canvasActions", () => {
+ it('should not hide any UI element when canvasActions is "undefined"', async () => {
+ await render();
+
+ const canvasActions = document.querySelector(
+ 'section[aria-labelledby="canvasActions-title"]',
+ );
+
+ expect(canvasActions).toMatchSnapshot();
+ });
+
+ it("should hide clear canvas button when clearCanvas is false", async () => {
+ const { container } = await render(
+ ,
+ );
+
+ expect(queryByTestId(container, "clear-canvas-button")).toBeNull();
+ });
+
+ it("should hide export button when export is false", async () => {
+ const { container } = await render(
+ ,
+ );
+
+ expect(queryByTestId(container, "export-button")).toBeNull();
+ });
+
+ it("should hide load button when loadScene is false", async () => {
+ const { container } = await render(
+ ,
+ );
+
+ expect(queryByTestId(container, "load-button")).toBeNull();
+ });
+
+ it("should hide save as button when saveAsScene is false", async () => {
+ const { container } = await render(
+ ,
+ );
+
+ expect(queryByTestId(container, "save-as-button")).toBeNull();
+ });
+
+ it("should hide save button when saveScene is false", async () => {
+ const { container } = await render(
+ ,
+ );
+
+ expect(queryByTestId(container, "save-button")).toBeNull();
+ });
+
+ it("should hide the canvas background picker when changeViewBackgroundColor is false", async () => {
+ const { container } = await render(
+ ,
+ );
+
+ expect(queryByTestId(container, "canvas-background-picker")).toBeNull();
+ });
+
+ it("should hide the theme toggle when theme is false", async () => {
+ const { container } = await render(
+ ,
+ );
+
+ expect(queryByTestId(container, "toggle-dark-mode")).toBeNull();
+ });
+ });
+ });
});
diff --git a/src/tests/fixtures/diagramFixture.ts b/src/tests/fixtures/diagramFixture.ts
new file mode 100644
index 000000000..e5019c9a9
--- /dev/null
+++ b/src/tests/fixtures/diagramFixture.ts
@@ -0,0 +1,31 @@
+import {
+ diamondFixture,
+ ellipseFixture,
+ rectangleFixture,
+} from "./elementFixture";
+
+export const diagramFixture = {
+ type: "excalidraw",
+ version: 2,
+ source: "https://excalidraw.com",
+ elements: [diamondFixture, ellipseFixture, rectangleFixture],
+ appState: {
+ viewBackgroundColor: "#ffffff",
+ gridSize: null,
+ },
+};
+
+export const diagramFactory = ({
+ overrides = {},
+ elementOverrides = {},
+} = {}) => ({
+ ...diagramFixture,
+ elements: [
+ { ...diamondFixture, ...elementOverrides },
+ { ...ellipseFixture, ...elementOverrides },
+ { ...rectangleFixture, ...elementOverrides },
+ ],
+ ...overrides,
+});
+
+export default diagramFixture;
diff --git a/src/tests/fixtures/elementFixture.ts b/src/tests/fixtures/elementFixture.ts
new file mode 100644
index 000000000..d3ec64a7d
--- /dev/null
+++ b/src/tests/fixtures/elementFixture.ts
@@ -0,0 +1,37 @@
+import { ExcalidrawElement } from "../../element/types";
+
+const elementBase: Omit = {
+ id: "vWrqOAfkind2qcm7LDAGZ",
+ x: 414,
+ y: 237,
+ width: 214,
+ height: 214,
+ angle: 0,
+ strokeColor: "#000000",
+ backgroundColor: "#15aabf",
+ fillStyle: "hachure",
+ strokeWidth: 1,
+ strokeStyle: "solid",
+ roughness: 1,
+ opacity: 100,
+ groupIds: [],
+ strokeSharpness: "sharp",
+ seed: 1041657908,
+ version: 120,
+ versionNonce: 1188004276,
+ isDeleted: false,
+ boundElementIds: null,
+};
+
+export const rectangleFixture: ExcalidrawElement = {
+ ...elementBase,
+ type: "rectangle",
+};
+export const ellipseFixture: ExcalidrawElement = {
+ ...elementBase,
+ type: "ellipse",
+};
+export const diamondFixture: ExcalidrawElement = {
+ ...elementBase,
+ type: "diamond",
+};
diff --git a/src/tests/flip.test.tsx b/src/tests/flip.test.tsx
new file mode 100644
index 000000000..b2d0cad4f
--- /dev/null
+++ b/src/tests/flip.test.tsx
@@ -0,0 +1,615 @@
+import React from "react";
+import ReactDOM from "react-dom";
+import { render } from "./test-utils";
+import App from "../components/App";
+import { defaultLang, setLanguage } from "../i18n";
+import { UI, Pointer } from "./helpers/ui";
+import { API } from "./helpers/api";
+import { actionFlipHorizontal, actionFlipVertical } from "../actions";
+
+const { h } = window;
+
+const mouse = new Pointer("mouse");
+
+beforeEach(async () => {
+ // Unmount ReactDOM from root
+ ReactDOM.unmountComponentAtNode(document.getElementById("root")!);
+ mouse.reset();
+
+ await setLanguage(defaultLang);
+ render();
+});
+
+const createAndSelectOneRectangle = (angle: number = 0) => {
+ UI.createElement("rectangle", {
+ x: 0,
+ y: 0,
+ width: 100,
+ height: 50,
+ angle,
+ });
+};
+
+const createAndSelectOneDiamond = (angle: number = 0) => {
+ UI.createElement("diamond", {
+ x: 0,
+ y: 0,
+ width: 100,
+ height: 50,
+ angle,
+ });
+};
+
+const createAndSelectOneEllipse = (angle: number = 0) => {
+ UI.createElement("ellipse", {
+ x: 0,
+ y: 0,
+ width: 100,
+ height: 50,
+ angle,
+ });
+};
+
+const createAndSelectOneArrow = (angle: number = 0) => {
+ UI.createElement("arrow", {
+ x: 0,
+ y: 0,
+ width: 100,
+ height: 50,
+ angle,
+ });
+};
+
+const createAndSelectOneLine = (angle: number = 0) => {
+ UI.createElement("line", {
+ x: 0,
+ y: 0,
+ width: 100,
+ height: 50,
+ angle,
+ });
+};
+
+const createAndReturnOneDraw = (angle: number = 0) => {
+ return UI.createElement("draw", {
+ x: 0,
+ y: 0,
+ width: 50,
+ height: 100,
+ angle,
+ });
+};
+
+// Rectangle element
+
+it("flips an unrotated rectangle horizontally correctly", () => {
+ createAndSelectOneRectangle();
+
+ expect(API.getSelectedElements()[0].x).toEqual(0);
+
+ expect(API.getSelectedElements()[0].y).toEqual(0);
+
+ const originalWidth = API.getSelectedElements()[0].width;
+ const originalHeight = API.getSelectedElements()[0].height;
+
+ h.app.actionManager.executeAction(actionFlipHorizontal);
+
+ // Check if x position did not change
+ expect(API.getSelectedElements()[0].x).toEqual(0);
+
+ expect(API.getSelectedElements()[0].y).toEqual(0);
+
+ // Check if width and height did not change
+ expect(API.getSelectedElements()[0].width).toEqual(originalWidth);
+
+ expect(API.getSelectedElements()[0].height).toEqual(originalHeight);
+});
+
+it("flips an unrotated rectangle vertically correctly", () => {
+ createAndSelectOneRectangle();
+
+ expect(API.getSelectedElements()[0].x).toEqual(0);
+
+ expect(API.getSelectedElements()[0].y).toEqual(0);
+
+ const originalWidth = API.getSelectedElements()[0].width;
+ const originalHeight = API.getSelectedElements()[0].height;
+
+ h.app.actionManager.executeAction(actionFlipVertical);
+
+ // Check if x position did not change
+ expect(API.getSelectedElements()[0].x).toEqual(0);
+
+ expect(API.getSelectedElements()[0].y).toEqual(0);
+
+ // Check if width and height did not change
+ expect(API.getSelectedElements()[0].width).toEqual(originalWidth);
+
+ expect(API.getSelectedElements()[0].height).toEqual(originalHeight);
+});
+
+it("flips a rotated rectangle horizontally correctly", () => {
+ const originalAngle = (3 * Math.PI) / 4;
+ const expectedAngle = (5 * Math.PI) / 4;
+
+ createAndSelectOneRectangle(originalAngle);
+
+ expect(API.getSelectedElements()[0].x).toEqual(0);
+
+ expect(API.getSelectedElements()[0].y).toEqual(0);
+
+ const originalWidth = API.getSelectedElements()[0].width;
+ const originalHeight = API.getSelectedElements()[0].height;
+
+ h.app.actionManager.executeAction(actionFlipHorizontal);
+
+ // Check if x position did not change
+ expect(API.getSelectedElements()[0].x).toEqual(0);
+
+ expect(API.getSelectedElements()[0].y).toEqual(0);
+
+ // Check if width and height did not change
+ expect(API.getSelectedElements()[0].width).toEqual(originalWidth);
+
+ expect(API.getSelectedElements()[0].height).toEqual(originalHeight);
+
+ // Check angle
+ expect(API.getSelectedElements()[0].angle).toBeCloseTo(expectedAngle);
+});
+
+it("flips a rotated rectangle vertically correctly", () => {
+ const originalAngle = (3 * Math.PI) / 4;
+ const expectedAgnle = Math.PI / 4;
+
+ createAndSelectOneRectangle(originalAngle);
+
+ expect(API.getSelectedElements()[0].x).toEqual(0);
+
+ expect(API.getSelectedElements()[0].y).toEqual(0);
+
+ const originalWidth = API.getSelectedElements()[0].width;
+ const originalHeight = API.getSelectedElements()[0].height;
+
+ h.app.actionManager.executeAction(actionFlipVertical);
+
+ // Check if x position did not change
+ expect(API.getSelectedElements()[0].x).toEqual(0);
+
+ expect(API.getSelectedElements()[0].y).toEqual(0);
+
+ // Check if width and height did not change
+ expect(API.getSelectedElements()[0].width).toEqual(originalWidth);
+
+ expect(API.getSelectedElements()[0].height).toEqual(originalHeight);
+
+ // Check angle
+ expect(API.getSelectedElements()[0].angle).toBeCloseTo(expectedAgnle);
+});
+
+// Diamond element
+
+it("flips an unrotated diamond horizontally correctly", () => {
+ createAndSelectOneDiamond();
+
+ expect(API.getSelectedElements()[0].x).toEqual(0);
+
+ expect(API.getSelectedElements()[0].y).toEqual(0);
+
+ const originalWidth = API.getSelectedElements()[0].width;
+ const originalHeight = API.getSelectedElements()[0].height;
+
+ h.app.actionManager.executeAction(actionFlipHorizontal);
+
+ // Check if x position did not change
+ expect(API.getSelectedElements()[0].x).toEqual(0);
+
+ expect(API.getSelectedElements()[0].y).toEqual(0);
+
+ // Check if width and height did not change
+ expect(API.getSelectedElements()[0].width).toEqual(originalWidth);
+
+ expect(API.getSelectedElements()[0].height).toEqual(originalHeight);
+});
+
+it("flips an unrotated diamond vertically correctly", () => {
+ createAndSelectOneDiamond();
+
+ expect(API.getSelectedElements()[0].x).toEqual(0);
+
+ expect(API.getSelectedElements()[0].y).toEqual(0);
+
+ const originalWidth = API.getSelectedElements()[0].width;
+ const originalHeight = API.getSelectedElements()[0].height;
+
+ h.app.actionManager.executeAction(actionFlipVertical);
+
+ // Check if x position did not change
+ expect(API.getSelectedElements()[0].x).toEqual(0);
+
+ expect(API.getSelectedElements()[0].y).toEqual(0);
+
+ // Check if width and height did not change
+ expect(API.getSelectedElements()[0].width).toEqual(originalWidth);
+
+ expect(API.getSelectedElements()[0].height).toEqual(originalHeight);
+});
+
+it("flips a rotated diamond horizontally correctly", () => {
+ const originalAngle = (5 * Math.PI) / 4;
+ const expectedAngle = (3 * Math.PI) / 4;
+
+ createAndSelectOneDiamond(originalAngle);
+
+ expect(API.getSelectedElements()[0].x).toEqual(0);
+
+ expect(API.getSelectedElements()[0].y).toEqual(0);
+
+ const originalWidth = API.getSelectedElements()[0].width;
+ const originalHeight = API.getSelectedElements()[0].height;
+
+ h.app.actionManager.executeAction(actionFlipHorizontal);
+
+ // Check if x position did not change
+ expect(API.getSelectedElements()[0].x).toEqual(0);
+
+ expect(API.getSelectedElements()[0].y).toEqual(0);
+
+ // Check if width and height did not change
+ expect(API.getSelectedElements()[0].width).toEqual(originalWidth);
+
+ expect(API.getSelectedElements()[0].height).toEqual(originalHeight);
+
+ // Check angle
+ expect(API.getSelectedElements()[0].angle).toBeCloseTo(expectedAngle);
+});
+
+it("flips a rotated diamond vertically correctly", () => {
+ const originalAngle = (5 * Math.PI) / 4;
+ const expectedAngle = (7 * Math.PI) / 4;
+
+ createAndSelectOneDiamond(originalAngle);
+
+ expect(API.getSelectedElements()[0].x).toEqual(0);
+
+ expect(API.getSelectedElements()[0].y).toEqual(0);
+
+ const originalWidth = API.getSelectedElements()[0].width;
+ const originalHeight = API.getSelectedElements()[0].height;
+
+ h.app.actionManager.executeAction(actionFlipVertical);
+
+ // Check if x position did not change
+ expect(API.getSelectedElements()[0].x).toEqual(0);
+
+ expect(API.getSelectedElements()[0].y).toEqual(0);
+
+ // Check if width and height did not change
+ expect(API.getSelectedElements()[0].width).toEqual(originalWidth);
+
+ expect(API.getSelectedElements()[0].height).toEqual(originalHeight);
+
+ // Check angle
+ expect(API.getSelectedElements()[0].angle).toBeCloseTo(expectedAngle);
+});
+
+// Ellipse element
+
+it("flips an unrotated ellipse horizontally correctly", () => {
+ createAndSelectOneEllipse();
+
+ expect(API.getSelectedElements()[0].x).toEqual(0);
+
+ expect(API.getSelectedElements()[0].y).toEqual(0);
+
+ const originalWidth = API.getSelectedElements()[0].width;
+ const originalHeight = API.getSelectedElements()[0].height;
+
+ h.app.actionManager.executeAction(actionFlipHorizontal);
+
+ // Check if x position did not change
+ expect(API.getSelectedElements()[0].x).toEqual(0);
+
+ expect(API.getSelectedElements()[0].y).toEqual(0);
+
+ // Check if width and height did not change
+ expect(API.getSelectedElements()[0].width).toEqual(originalWidth);
+
+ expect(API.getSelectedElements()[0].height).toEqual(originalHeight);
+});
+
+it("flips an unrotated ellipse vertically correctly", () => {
+ createAndSelectOneEllipse();
+
+ expect(API.getSelectedElements()[0].x).toEqual(0);
+
+ expect(API.getSelectedElements()[0].y).toEqual(0);
+
+ const originalWidth = API.getSelectedElements()[0].width;
+ const originalHeight = API.getSelectedElements()[0].height;
+
+ h.app.actionManager.executeAction(actionFlipVertical);
+
+ // Check if x position did not change
+ expect(API.getSelectedElements()[0].x).toEqual(0);
+
+ expect(API.getSelectedElements()[0].y).toEqual(0);
+
+ // Check if width and height did not change
+ expect(API.getSelectedElements()[0].width).toEqual(originalWidth);
+
+ expect(API.getSelectedElements()[0].height).toEqual(originalHeight);
+});
+
+it("flips a rotated ellipse horizontally correctly", () => {
+ const originalAngle = (7 * Math.PI) / 4;
+ const expectedAngle = Math.PI / 4;
+
+ createAndSelectOneEllipse(originalAngle);
+
+ expect(API.getSelectedElements()[0].x).toEqual(0);
+
+ expect(API.getSelectedElements()[0].y).toEqual(0);
+
+ const originalWidth = API.getSelectedElements()[0].width;
+ const originalHeight = API.getSelectedElements()[0].height;
+
+ h.app.actionManager.executeAction(actionFlipHorizontal);
+
+ // Check if x position did not change
+ expect(API.getSelectedElements()[0].x).toEqual(0);
+
+ expect(API.getSelectedElements()[0].y).toEqual(0);
+
+ // Check if width and height did not change
+ expect(API.getSelectedElements()[0].width).toEqual(originalWidth);
+
+ expect(API.getSelectedElements()[0].height).toEqual(originalHeight);
+
+ // Check angle
+ expect(API.getSelectedElements()[0].angle).toBeCloseTo(expectedAngle);
+});
+
+it("flips a rotated ellipse vertically correctly", () => {
+ const originalAngle = (7 * Math.PI) / 4;
+ const expectedAngle = (5 * Math.PI) / 4;
+
+ createAndSelectOneEllipse(originalAngle);
+
+ expect(API.getSelectedElements()[0].x).toEqual(0);
+
+ expect(API.getSelectedElements()[0].y).toEqual(0);
+
+ const originalWidth = API.getSelectedElements()[0].width;
+ const originalHeight = API.getSelectedElements()[0].height;
+
+ h.app.actionManager.executeAction(actionFlipVertical);
+
+ // Check if x position did not change
+ expect(API.getSelectedElements()[0].x).toEqual(0);
+
+ expect(API.getSelectedElements()[0].y).toEqual(0);
+
+ // Check if width and height did not change
+ expect(API.getSelectedElements()[0].width).toEqual(originalWidth);
+
+ expect(API.getSelectedElements()[0].height).toEqual(originalHeight);
+
+ // Check angle
+ expect(API.getSelectedElements()[0].angle).toBeCloseTo(expectedAngle);
+});
+
+// Arrow element
+
+it("flips an unrotated arrow horizontally correctly", () => {
+ createAndSelectOneArrow();
+
+ const originalWidth = API.getSelectedElements()[0].width;
+ const originalHeight = API.getSelectedElements()[0].height;
+
+ h.app.actionManager.executeAction(actionFlipHorizontal);
+
+ // Check if width and height did not change
+ expect(API.getSelectedElements()[0].width).toEqual(originalWidth);
+
+ expect(API.getSelectedElements()[0].height).toEqual(originalHeight);
+});
+
+it("flips an unrotated arrow vertically correctly", () => {
+ createAndSelectOneArrow();
+
+ const originalWidth = API.getSelectedElements()[0].width;
+ const originalHeight = API.getSelectedElements()[0].height;
+
+ h.app.actionManager.executeAction(actionFlipVertical);
+
+ // Check if width and height did not change
+ expect(API.getSelectedElements()[0].width).toEqual(originalWidth);
+
+ expect(API.getSelectedElements()[0].height).toEqual(originalHeight);
+});
+
+it("flips a rotated arrow horizontally correctly", () => {
+ const originalAngle = Math.PI / 4;
+ const expectedAngle = (7 * Math.PI) / 4;
+ createAndSelectOneArrow(originalAngle);
+
+ const originalWidth = API.getSelectedElements()[0].width;
+ const originalHeight = API.getSelectedElements()[0].height;
+
+ h.app.actionManager.executeAction(actionFlipHorizontal);
+
+ // Check if width and height did not change
+ expect(API.getSelectedElements()[0].width).toEqual(originalWidth);
+
+ expect(API.getSelectedElements()[0].height).toEqual(originalHeight);
+
+ // Check angle
+ expect(API.getSelectedElements()[0].angle).toBeCloseTo(expectedAngle);
+});
+
+it("flips a rotated arrow vertically correctly", () => {
+ const originalAngle = Math.PI / 4;
+ const expectedAngle = (3 * Math.PI) / 4;
+ createAndSelectOneArrow(originalAngle);
+
+ const originalWidth = API.getSelectedElements()[0].width;
+ const originalHeight = API.getSelectedElements()[0].height;
+
+ h.app.actionManager.executeAction(actionFlipVertical);
+
+ // Check if width and height did not change
+ expect(API.getSelectedElements()[0].width).toEqual(originalWidth);
+
+ expect(API.getSelectedElements()[0].height).toEqual(originalHeight);
+
+ // Check angle
+ expect(API.getSelectedElements()[0].angle).toBeCloseTo(expectedAngle);
+});
+
+// Line element
+
+it("flips an unrotated line horizontally correctly", () => {
+ createAndSelectOneLine();
+
+ const originalWidth = API.getSelectedElements()[0].width;
+ const originalHeight = API.getSelectedElements()[0].height;
+
+ h.app.actionManager.executeAction(actionFlipHorizontal);
+
+ // Check if width and height did not change
+ expect(API.getSelectedElements()[0].width).toEqual(originalWidth);
+
+ expect(API.getSelectedElements()[0].height).toEqual(originalHeight);
+});
+
+it("flips an unrotated line vertically correctly", () => {
+ createAndSelectOneLine();
+
+ const originalWidth = API.getSelectedElements()[0].width;
+ const originalHeight = API.getSelectedElements()[0].height;
+
+ h.app.actionManager.executeAction(actionFlipVertical);
+
+ // Check if width and height did not change
+ expect(API.getSelectedElements()[0].width).toEqual(originalWidth);
+
+ expect(API.getSelectedElements()[0].height).toEqual(originalHeight);
+});
+
+it("flips a rotated line horizontally correctly", () => {
+ const originalAngle = Math.PI / 4;
+ const expectedAngle = (7 * Math.PI) / 4;
+
+ createAndSelectOneLine(originalAngle);
+
+ const originalWidth = API.getSelectedElements()[0].width;
+ const originalHeight = API.getSelectedElements()[0].height;
+
+ h.app.actionManager.executeAction(actionFlipHorizontal);
+
+ // Check if width and height did not change
+ expect(API.getSelectedElements()[0].width).toEqual(originalWidth);
+
+ expect(API.getSelectedElements()[0].height).toEqual(originalHeight);
+
+ // Check angle
+ expect(API.getSelectedElements()[0].angle).toBeCloseTo(expectedAngle);
+});
+
+it("flips a rotated line vertically correctly", () => {
+ const originalAngle = Math.PI / 4;
+ const expectedAngle = (3 * Math.PI) / 4;
+
+ createAndSelectOneLine(originalAngle);
+
+ const originalWidth = API.getSelectedElements()[0].width;
+ const originalHeight = API.getSelectedElements()[0].height;
+
+ h.app.actionManager.executeAction(actionFlipVertical);
+
+ // Check if width and height did not change
+ expect(API.getSelectedElements()[0].width).toEqual(originalWidth);
+
+ expect(API.getSelectedElements()[0].height).toEqual(originalHeight);
+
+ // Check angle
+ expect(API.getSelectedElements()[0].angle).toBeCloseTo(expectedAngle);
+});
+
+// Draw element
+
+it("flips an unrotated drawing horizontally correctly", () => {
+ const draw = createAndReturnOneDraw();
+ // select draw, since not done automatically
+ h.state.selectedElementIds[draw.id] = true;
+
+ const originalWidth = draw.width;
+ const originalHeight = draw.height;
+
+ h.app.actionManager.executeAction(actionFlipHorizontal);
+
+ // Check if width and height did not change
+ expect(draw.width).toEqual(originalWidth);
+
+ expect(draw.height).toEqual(originalHeight);
+});
+
+it("flips an unrotated drawing vertically correctly", () => {
+ const draw = createAndReturnOneDraw();
+ // select draw, since not done automatically
+ h.state.selectedElementIds[draw.id] = true;
+
+ const originalWidth = draw.width;
+ const originalHeight = draw.height;
+
+ h.app.actionManager.executeAction(actionFlipVertical);
+
+ // Check if width and height did not change
+ expect(draw.width).toEqual(originalWidth);
+
+ expect(draw.height).toEqual(originalHeight);
+});
+
+it("flips a rotated drawing horizontally correctly", () => {
+ const originalAngle = Math.PI / 4;
+ const expectedAngle = (7 * Math.PI) / 4;
+
+ const draw = createAndReturnOneDraw(originalAngle);
+ // select draw, since not done automatically
+ h.state.selectedElementIds[draw.id] = true;
+
+ const originalWidth = draw.width;
+ const originalHeight = draw.height;
+
+ h.app.actionManager.executeAction(actionFlipHorizontal);
+
+ // Check if width and height did not change
+ expect(draw.width).toEqual(originalWidth);
+
+ expect(draw.height).toEqual(originalHeight);
+
+ // Check angle
+ expect(draw.angle).toBeCloseTo(expectedAngle);
+});
+
+it("flips a rotated drawing vertically correctly", () => {
+ const originalAngle = Math.PI / 4;
+ const expectedAngle = (3 * Math.PI) / 4;
+
+ const draw = createAndReturnOneDraw(originalAngle);
+ // select draw, since not done automatically
+ h.state.selectedElementIds[draw.id] = true;
+
+ const originalWidth = draw.width;
+ const originalHeight = draw.height;
+
+ h.app.actionManager.executeAction(actionFlipVertical);
+
+ // Check if width and height did not change
+ expect(API.getSelectedElement().width).toEqual(originalWidth);
+
+ expect(API.getSelectedElement().height).toEqual(originalHeight);
+
+ // Check angle
+ expect(API.getSelectedElement().angle).toBeCloseTo(expectedAngle);
+});
diff --git a/src/tests/helpers/ui.ts b/src/tests/helpers/ui.ts
index 5f8f23c76..5bcb3dddc 100644
--- a/src/tests/helpers/ui.ts
+++ b/src/tests/helpers/ui.ts
@@ -6,6 +6,7 @@ import {
import { CODES } from "../../keys";
import { ToolName } from "../queries/toolQueries";
import { fireEvent, GlobalTestState } from "../test-utils";
+import { mutateElement } from "../../element/mutateElement";
import { API } from "./api";
const { h } = window;
@@ -202,6 +203,7 @@ export class UI {
size = 10,
width = size,
height = width,
+ angle = 0,
}: {
position?: number;
x?: number;
@@ -209,6 +211,7 @@ export class UI {
size?: number;
width?: number;
height?: number;
+ angle?: number;
} = {},
): (T extends "arrow" | "line" | "draw"
? ExcalidrawLinearElement
@@ -231,6 +234,10 @@ export class UI {
const origElement = h.elements[h.elements.length - 1] as any;
+ if (angle !== 0) {
+ mutateElement(origElement, { angle });
+ }
+
return new Proxy(
{},
{
diff --git a/src/tests/move.test.tsx b/src/tests/move.test.tsx
index e356f89a3..312b3eec6 100644
--- a/src/tests/move.test.tsx
+++ b/src/tests/move.test.tsx
@@ -38,7 +38,7 @@ describe("move element", () => {
fireEvent.pointerMove(canvas, { clientX: 60, clientY: 70 });
fireEvent.pointerUp(canvas);
- expect(renderScene).toHaveBeenCalledTimes(7);
+ expect(renderScene).toHaveBeenCalledTimes(8);
expect(h.state.selectionElement).toBeNull();
expect(h.elements.length).toEqual(1);
expect(h.state.selectedElementIds[h.elements[0].id]).toBeTruthy();
@@ -120,7 +120,7 @@ describe("duplicate element on move when ALT is clicked", () => {
fireEvent.pointerMove(canvas, { clientX: 60, clientY: 70 });
fireEvent.pointerUp(canvas);
- expect(renderScene).toHaveBeenCalledTimes(7);
+ expect(renderScene).toHaveBeenCalledTimes(8);
expect(h.state.selectionElement).toBeNull();
expect(h.elements.length).toEqual(1);
expect(h.state.selectedElementIds[h.elements[0].id]).toBeTruthy();
diff --git a/src/tests/multiPointCreate.test.tsx b/src/tests/multiPointCreate.test.tsx
index 93bbc6639..748f8807b 100644
--- a/src/tests/multiPointCreate.test.tsx
+++ b/src/tests/multiPointCreate.test.tsx
@@ -1,6 +1,11 @@
import React from "react";
import ReactDOM from "react-dom";
-import { render, fireEvent } from "./test-utils";
+import {
+ render,
+ fireEvent,
+ mockBoundingClientRect,
+ restoreOriginalGetBoundingClientRect,
+} from "./test-utils";
import ExcalidrawApp from "../excalidraw-app";
import * as Renderer from "../renderer/renderScene";
import { KEYS } from "../keys";
@@ -20,6 +25,14 @@ beforeEach(() => {
const { h } = window;
describe("remove shape in non linear elements", () => {
+ beforeAll(() => {
+ mockBoundingClientRect();
+ });
+
+ afterAll(() => {
+ restoreOriginalGetBoundingClientRect();
+ });
+
it("rectangle", async () => {
const { getByToolName, container } = await render();
// select tool
@@ -88,7 +101,7 @@ describe("multi point mode in linear elements", () => {
fireEvent.pointerUp(canvas);
fireEvent.keyDown(document, { key: KEYS.ENTER });
- expect(renderScene).toHaveBeenCalledTimes(13);
+ expect(renderScene).toHaveBeenCalledTimes(14);
expect(h.elements.length).toEqual(1);
const element = h.elements[0] as ExcalidrawLinearElement;
@@ -129,7 +142,7 @@ describe("multi point mode in linear elements", () => {
fireEvent.pointerUp(canvas);
fireEvent.keyDown(document, { key: KEYS.ENTER });
- expect(renderScene).toHaveBeenCalledTimes(13);
+ expect(renderScene).toHaveBeenCalledTimes(14);
expect(h.elements.length).toEqual(1);
const element = h.elements[0] as ExcalidrawLinearElement;
diff --git a/src/tests/packages/__snapshots__/utils.test.ts.snap b/src/tests/packages/__snapshots__/utils.test.ts.snap
new file mode 100644
index 000000000..851897b34
--- /dev/null
+++ b/src/tests/packages/__snapshots__/utils.test.ts.snap
@@ -0,0 +1,76 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`exportToSvg with default arguments 1`] = `
+Object {
+ "collaborators": Map {},
+ "currentChartType": "bar",
+ "currentItemBackgroundColor": "transparent",
+ "currentItemEndArrowhead": "arrow",
+ "currentItemFillStyle": "hachure",
+ "currentItemFontFamily": 1,
+ "currentItemFontSize": 20,
+ "currentItemLinearStrokeSharpness": "round",
+ "currentItemOpacity": 100,
+ "currentItemRoughness": 1,
+ "currentItemStartArrowhead": null,
+ "currentItemStrokeColor": "#000000",
+ "currentItemStrokeSharpness": "sharp",
+ "currentItemStrokeStyle": "solid",
+ "currentItemStrokeWidth": 1,
+ "currentItemTextAlign": "left",
+ "cursorButton": "up",
+ "draggingElement": null,
+ "editingElement": null,
+ "editingGroupId": null,
+ "editingLinearElement": null,
+ "elementLocked": false,
+ "elementType": "selection",
+ "errorMessage": null,
+ "exportBackground": true,
+ "exportEmbedScene": false,
+ "exportPadding": undefined,
+ "exportWithDarkMode": false,
+ "fileHandle": null,
+ "gridSize": null,
+ "isBindingEnabled": true,
+ "isLibraryOpen": false,
+ "isLoading": false,
+ "isResizing": false,
+ "isRotating": false,
+ "lastPointerDownWith": "mouse",
+ "metadata": undefined,
+ "multiElement": null,
+ "name": "name",
+ "openMenu": null,
+ "pasteDialog": Object {
+ "data": null,
+ "shown": false,
+ },
+ "previousSelectedElementIds": Object {},
+ "resizingElement": null,
+ "scrollX": 0,
+ "scrollY": 0,
+ "scrolledOutside": false,
+ "selectedElementIds": Object {},
+ "selectedGroupIds": Object {},
+ "selectionElement": null,
+ "shouldAddWatermark": false,
+ "shouldCacheIgnoreZoom": false,
+ "showHelpDialog": false,
+ "showStats": false,
+ "startBoundElement": null,
+ "suggestedBindings": Array [],
+ "theme": "light",
+ "toastMessage": null,
+ "viewBackgroundColor": "#ffffff",
+ "viewModeEnabled": false,
+ "zenModeEnabled": false,
+ "zoom": Object {
+ "translation": Object {
+ "x": 0,
+ "y": 0,
+ },
+ "value": 1,
+ },
+}
+`;
diff --git a/src/tests/packages/utils.test.ts b/src/tests/packages/utils.test.ts
new file mode 100644
index 000000000..1eaad1095
--- /dev/null
+++ b/src/tests/packages/utils.test.ts
@@ -0,0 +1,121 @@
+import * as utils from "../../packages/utils";
+import { diagramFactory } from "../fixtures/diagramFixture";
+import * as mockedSceneExportUtils from "../../scene/export";
+
+jest.mock("../../scene/export", () => ({
+ __esmodule: true,
+ ...jest.requireActual("../../scene/export"),
+ exportToSvg: jest.fn(),
+}));
+
+describe("exportToCanvas", () => {
+ const EXPORT_PADDING = 10;
+
+ it("with default arguments", () => {
+ const canvas = utils.exportToCanvas({
+ ...diagramFactory({ elementOverrides: { width: 100, height: 100 } }),
+ });
+
+ expect(canvas.width).toBe(100 + 2 * EXPORT_PADDING);
+ expect(canvas.height).toBe(100 + 2 * EXPORT_PADDING);
+ });
+
+ it("when custom width and height", () => {
+ const canvas = utils.exportToCanvas({
+ ...diagramFactory({ elementOverrides: { width: 100, height: 100 } }),
+ getDimensions: () => ({ width: 200, height: 200, scale: 1 }),
+ });
+
+ expect(canvas.width).toBe(200);
+ expect(canvas.height).toBe(200);
+ });
+});
+
+describe("exportToBlob", () => {
+ describe("mime type", () => {
+ afterEach(jest.restoreAllMocks);
+
+ it("should change image/jpg to image/jpeg", async () => {
+ const blob = await utils.exportToBlob({
+ ...diagramFactory(),
+ getDimensions: (width, height) => ({ width, height, scale: 1 }),
+ mimeType: "image/jpg",
+ });
+ expect(blob?.type).toBe("image/jpeg");
+ });
+
+ it("should default to image/png", async () => {
+ const blob = await utils.exportToBlob({
+ ...diagramFactory(),
+ });
+ expect(blob?.type).toBe("image/png");
+ });
+
+ it("should warn when using quality with image/png", async () => {
+ const consoleSpy = jest
+ .spyOn(console, "warn")
+ .mockImplementationOnce(() => void 0);
+
+ await utils.exportToBlob({
+ ...diagramFactory(),
+ mimeType: "image/png",
+ quality: 1,
+ });
+
+ expect(consoleSpy).toHaveBeenCalledWith(
+ '"quality" will be ignored for "image/png" mimeType',
+ );
+ });
+ });
+});
+
+describe("exportToSvg", () => {
+ const mockedExportUtil = mockedSceneExportUtils.exportToSvg as jest.Mock;
+ const passedElements = () => mockedExportUtil.mock.calls[0][0];
+ const passedOptions = () => mockedExportUtil.mock.calls[0][1];
+
+ afterEach(jest.resetAllMocks);
+
+ it("with default arguments", () => {
+ utils.exportToSvg({
+ ...diagramFactory({
+ overrides: { appState: void 0 },
+ }),
+ });
+
+ const passedOptionsWhenDefault = {
+ ...passedOptions(),
+ // To avoid varying snapshots
+ name: "name",
+ };
+
+ expect(passedElements().length).toBe(3);
+ expect(passedOptionsWhenDefault).toMatchSnapshot();
+ });
+
+ it("with deleted elements", () => {
+ utils.exportToSvg({
+ ...diagramFactory({
+ overrides: { appState: void 0 },
+ elementOverrides: { isDeleted: true },
+ }),
+ });
+
+ expect(passedElements().length).toBe(0);
+ });
+
+ it("with exportPadding and metadata", () => {
+ const METADATA = "some metada";
+
+ utils.exportToSvg({
+ ...diagramFactory({ overrides: { appState: { name: "diagram name" } } }),
+ exportPadding: 0,
+ metadata: METADATA,
+ });
+
+ expect(passedElements().length).toBe(3);
+ expect(passedOptions()).toEqual(
+ expect.objectContaining({ exportPadding: 0, metadata: METADATA }),
+ );
+ });
+});
diff --git a/src/tests/regressionTests.test.tsx b/src/tests/regressionTests.test.tsx
index b87f5fa84..a356b9cb6 100644
--- a/src/tests/regressionTests.test.tsx
+++ b/src/tests/regressionTests.test.tsx
@@ -76,6 +76,7 @@ beforeEach(async () => {
finger2.reset();
await render();
+ h.setState({ height: 768, width: 1024 });
});
afterEach(() => {
@@ -653,6 +654,8 @@ describe("regression tests", () => {
"pasteStyles",
"deleteSelectedElements",
"addToLibrary",
+ "flipHorizontal",
+ "flipVertical",
"sendBackward",
"bringForward",
"sendToBack",
diff --git a/src/tests/resize.test.tsx b/src/tests/resize.test.tsx
index 70c79fe6b..f730fcd87 100644
--- a/src/tests/resize.test.tsx
+++ b/src/tests/resize.test.tsx
@@ -122,12 +122,12 @@ describe("resize rectangle ellipses and diamond elements", () => {
);
});
-function resize(
+const resize = (
element: ExcalidrawElement,
handleDir: TransformHandleDirection,
mouseMove: [number, number],
keyboardModifiers: KeyboardModifiers = {},
-) {
+) => {
mouse.select(element);
const handle = getTransformHandles(element, h.state.zoom, "mouse")[
handleDir
@@ -140,4 +140,4 @@ function resize(
mouse.move(mouseMove[0], mouseMove[1]);
mouse.up();
});
-}
+};
diff --git a/src/tests/scene/__snapshots__/export.test.ts.snap b/src/tests/scene/__snapshots__/export.test.ts.snap
new file mode 100644
index 000000000..56dfa7b77
--- /dev/null
+++ b/src/tests/scene/__snapshots__/export.test.ts.snap
@@ -0,0 +1,70 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`exportToSvg with default arguments 1`] = `
+
+`;
diff --git a/src/tests/scene/export.test.ts b/src/tests/scene/export.test.ts
new file mode 100644
index 000000000..e1ce7a7da
--- /dev/null
+++ b/src/tests/scene/export.test.ts
@@ -0,0 +1,96 @@
+import { NonDeletedExcalidrawElement } from "../../element/types";
+import * as exportUtils from "../../scene/export";
+import { diamondFixture, ellipseFixture } from "../fixtures/elementFixture";
+
+describe("exportToSvg", () => {
+ const ELEMENT_HEIGHT = 100;
+ const ELEMENT_WIDTH = 100;
+ const ELEMENTS = [
+ { ...diamondFixture, height: ELEMENT_HEIGHT, width: ELEMENT_WIDTH },
+ { ...ellipseFixture, height: ELEMENT_HEIGHT, width: ELEMENT_WIDTH },
+ ] as NonDeletedExcalidrawElement[];
+
+ const DEFAULT_OPTIONS = {
+ exportBackground: false,
+ viewBackgroundColor: "#ffffff",
+ shouldAddWatermark: false,
+ };
+
+ it("with default arguments", () => {
+ const svgElement = exportUtils.exportToSvg(ELEMENTS, DEFAULT_OPTIONS);
+
+ expect(svgElement).toMatchSnapshot();
+ });
+
+ it("with background color", () => {
+ const BACKGROUND_COLOR = "#abcdef";
+
+ const svgElement = exportUtils.exportToSvg(ELEMENTS, {
+ ...DEFAULT_OPTIONS,
+ exportBackground: true,
+ viewBackgroundColor: BACKGROUND_COLOR,
+ });
+
+ expect(svgElement.querySelector("rect")).toHaveAttribute(
+ "fill",
+ BACKGROUND_COLOR,
+ );
+ });
+
+ it("with watermark", () => {
+ const svgElement = exportUtils.exportToSvg(ELEMENTS, {
+ ...DEFAULT_OPTIONS,
+ shouldAddWatermark: true,
+ });
+
+ expect(svgElement.querySelector("text")?.textContent).toMatchInlineSnapshot(
+ `"Made with Excalidraw"`,
+ );
+ });
+
+ it("with dark mode", () => {
+ const svgElement = exportUtils.exportToSvg(ELEMENTS, {
+ ...DEFAULT_OPTIONS,
+ exportWithDarkMode: true,
+ });
+
+ expect(svgElement.getAttribute("filter")).toMatchInlineSnapshot(
+ `"themeFilter"`,
+ );
+ });
+
+ it("with exportPadding, metadata", () => {
+ const svgElement = exportUtils.exportToSvg(ELEMENTS, {
+ ...DEFAULT_OPTIONS,
+ exportPadding: 0,
+ metadata: "some metadata",
+ });
+
+ expect(svgElement.innerHTML).toMatch(/some metadata/);
+ expect(svgElement).toHaveAttribute("height", ELEMENT_HEIGHT.toString());
+ expect(svgElement).toHaveAttribute("width", ELEMENT_WIDTH.toString());
+ expect(svgElement).toHaveAttribute(
+ "viewBox",
+ `0 0 ${ELEMENT_WIDTH} ${ELEMENT_HEIGHT}`,
+ );
+ });
+
+ it("with scale", () => {
+ const SCALE = 2;
+
+ const svgElement = exportUtils.exportToSvg(ELEMENTS, {
+ ...DEFAULT_OPTIONS,
+ exportPadding: 0,
+ scale: SCALE,
+ });
+
+ expect(svgElement).toHaveAttribute(
+ "height",
+ (ELEMENT_HEIGHT * SCALE).toString(),
+ );
+ expect(svgElement).toHaveAttribute(
+ "width",
+ (ELEMENT_WIDTH * SCALE).toString(),
+ );
+ });
+});
diff --git a/src/tests/scroll.test.tsx b/src/tests/scroll.test.tsx
index d1951a28a..4887e10e3 100644
--- a/src/tests/scroll.test.tsx
+++ b/src/tests/scroll.test.tsx
@@ -1,5 +1,10 @@
import React from "react";
-import { render, waitFor } from "./test-utils";
+import {
+ mockBoundingClientRect,
+ render,
+ restoreOriginalGetBoundingClientRect,
+ waitFor,
+} from "./test-utils";
import Excalidraw from "../packages/excalidraw/index";
import { API } from "./helpers/api";
@@ -7,34 +12,19 @@ const { h } = window;
describe("appState", () => {
it("scroll-to-content on init works with non-zero offsets", async () => {
- const WIDTH = 600;
- const HEIGHT = 700;
- const OFFSET_LEFT = 200;
- const OFFSET_TOP = 100;
+ const WIDTH = 200;
+ const HEIGHT = 100;
+ const OFFSET_LEFT = 20;
+ const OFFSET_TOP = 10;
const ELEM_WIDTH = 100;
const ELEM_HEIGHT = 60;
- const originalGetBoundingClientRect =
- global.window.HTMLDivElement.prototype.getBoundingClientRect;
- // override getBoundingClientRect as by default it will always return all values as 0 even if customized in html
- global.window.HTMLDivElement.prototype.getBoundingClientRect = () => ({
- top: OFFSET_TOP,
- left: OFFSET_LEFT,
- bottom: 10,
- right: 10,
- width: 100,
- x: 10,
- y: 20,
- height: 100,
- toJSON: () => {},
- });
+ mockBoundingClientRect();
await render(
{
,
);
await waitFor(() => {
- expect(h.state.width).toBe(WIDTH);
- expect(h.state.height).toBe(HEIGHT);
+ expect(h.state.width).toBe(200);
+ expect(h.state.height).toBe(100);
expect(h.state.offsetLeft).toBe(OFFSET_LEFT);
expect(h.state.offsetTop).toBe(OFFSET_TOP);
@@ -59,6 +49,6 @@ describe("appState", () => {
expect(h.state.scrollX).toBe(WIDTH / 2 - ELEM_WIDTH / 2);
expect(h.state.scrollY).toBe(HEIGHT / 2 - ELEM_HEIGHT / 2);
});
- global.window.HTMLDivElement.prototype.getBoundingClientRect = originalGetBoundingClientRect;
+ restoreOriginalGetBoundingClientRect();
});
});
diff --git a/src/tests/selection.test.tsx b/src/tests/selection.test.tsx
index 004c4bcd9..b882713ad 100644
--- a/src/tests/selection.test.tsx
+++ b/src/tests/selection.test.tsx
@@ -1,6 +1,11 @@
import React from "react";
import ReactDOM from "react-dom";
-import { render, fireEvent } from "./test-utils";
+import {
+ render,
+ fireEvent,
+ mockBoundingClientRect,
+ restoreOriginalGetBoundingClientRect,
+} from "./test-utils";
import ExcalidrawApp from "../excalidraw-app";
import * as Renderer from "../renderer/renderScene";
import { KEYS } from "../keys";
@@ -77,6 +82,14 @@ describe("selection element", () => {
});
describe("select single element on the scene", () => {
+ beforeAll(() => {
+ mockBoundingClientRect();
+ });
+
+ afterAll(() => {
+ restoreOriginalGetBoundingClientRect();
+ });
+
it("rectangle", async () => {
const { getByToolName, container } = await render();
const canvas = container.querySelector("canvas")!;
diff --git a/src/tests/test-utils.ts b/src/tests/test-utils.ts
index f78295be6..dfe243bbd 100644
--- a/src/tests/test-utils.ts
+++ b/src/tests/test-utils.ts
@@ -100,5 +100,27 @@ const initLocalStorage = (data: ImportedDataState) => {
};
export const updateSceneData = (data: SceneData) => {
- (window.h.collab as any).excalidrawAPI.updateScene(data);
+ (window.collab as any).excalidrawAPI.updateScene(data);
+};
+
+const originalGetBoundingClientRect =
+ global.window.HTMLDivElement.prototype.getBoundingClientRect;
+
+export const mockBoundingClientRect = () => {
+ // override getBoundingClientRect as by default it will always return all values as 0 even if customized in html
+ global.window.HTMLDivElement.prototype.getBoundingClientRect = () => ({
+ top: 10,
+ left: 20,
+ bottom: 10,
+ right: 10,
+ width: 200,
+ x: 10,
+ y: 20,
+ height: 100,
+ toJSON: () => {},
+ });
+};
+
+export const restoreOriginalGetBoundingClientRect = () => {
+ global.window.HTMLDivElement.prototype.getBoundingClientRect = originalGetBoundingClientRect;
};
diff --git a/src/types.ts b/src/types.ts
index 09c631415..42b510ef9 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -20,7 +20,6 @@ import { ExcalidrawImperativeAPI } from "./components/App";
import type { ResolvablePromise } from "./utils";
import { Spreadsheet } from "./charts";
import { Language } from "./i18n";
-import { UserIdleState } from "./excalidraw-app/collab/types";
export type Point = Readonly;
@@ -161,8 +160,6 @@ export type ExcalidrawAPIRefValue =
};
export interface ExcalidrawProps {
- width?: number;
- height?: number;
onChange?: (
elements: readonly ExcalidrawElement[],
appState: AppState,
@@ -189,6 +186,11 @@ export interface ExcalidrawProps {
libraryReturnUrl?: string;
theme?: "dark" | "light";
name?: string;
+ renderCustomStats?: (
+ elements: readonly NonDeletedExcalidrawElement[],
+ appState: AppState,
+ ) => JSX.Element;
+ UIOptions?: UIOptions;
}
export type SceneData = {
@@ -197,3 +199,29 @@ export type SceneData = {
collaborators?: Map;
commitToHistory?: boolean;
};
+
+export enum UserIdleState {
+ ACTIVE = "active",
+ AWAY = "away",
+ IDLE = "idle",
+}
+
+type CanvasActions = {
+ changeViewBackgroundColor?: boolean;
+ clearCanvas?: boolean;
+ export?: boolean;
+ loadScene?: boolean;
+ saveAsScene?: boolean;
+ saveScene?: boolean;
+ theme?: boolean;
+};
+
+export type UIOptions = {
+ canvasActions?: CanvasActions;
+};
+
+export type AppProps = ExcalidrawProps & {
+ UIOptions: {
+ canvasActions: Required;
+ };
+};
diff --git a/src/utils.ts b/src/utils.ts
index 22f86dc64..11959d5cf 100644
--- a/src/utils.ts
+++ b/src/utils.ts
@@ -123,17 +123,25 @@ export const debounce = (
timeout: number,
) => {
let handle = 0;
- let lastArgs: T;
+ let lastArgs: T | null = null;
const ret = (...args: T) => {
lastArgs = args;
clearTimeout(handle);
- handle = window.setTimeout(() => fn(...args), timeout);
+ handle = window.setTimeout(() => {
+ lastArgs = null;
+ fn(...args);
+ }, timeout);
};
ret.flush = () => {
clearTimeout(handle);
- fn(...(lastArgs || []));
+ if (lastArgs) {
+ const _lastArgs = lastArgs;
+ lastArgs = null;
+ fn(..._lastArgs);
+ }
};
ret.cancel = () => {
+ lastArgs = null;
clearTimeout(handle);
};
return ret;
diff --git a/tsconfig-types.json b/tsconfig-types.json
new file mode 100644
index 000000000..148459807
--- /dev/null
+++ b/tsconfig-types.json
@@ -0,0 +1,18 @@
+{
+ "include": ["src/packages/excalidraw", "src/global.d.ts", "src/css.d.ts"],
+ "compilerOptions": {
+ "allowJs": true,
+ "declaration": true,
+ "emitDeclarationOnly": true,
+ "outDir": "src/packages/excalidraw/types",
+ "jsx": "react-jsx",
+ "target": "es6",
+ "lib": ["dom", "dom.iterable", "esnext"],
+ "module": "esnext",
+ "moduleResolution": "node",
+ "resolveJsonModule": true,
+ "skipLibCheck": true,
+ "allowSyntheticDefaultImports": true,
+ "strict": true
+ }
+}
diff --git a/yarn.lock b/yarn.lock
index 9c41d30d7..88d938f98 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -28,9 +28,9 @@
dependencies:
"@babel/highlight" "^7.12.13"
-"@babel/compat-data@^7.12.1", "@babel/compat-data@^7.13.0", "@babel/compat-data@^7.13.8":
- version "7.13.11"
- resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.13.11.tgz#9c8fe523c206979c9a81b1e12fe50c1254f1aa35"
+"@babel/compat-data@^7.12.1", "@babel/compat-data@^7.13.0", "@babel/compat-data@^7.13.12", "@babel/compat-data@^7.13.8":
+ version "7.13.12"
+ resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.13.12.tgz#a8a5ccac19c200f9dd49624cac6e19d7be1236a1"
"@babel/core@7.12.3", "@babel/core@^7.1.0", "@babel/core@^7.12.3", "@babel/core@^7.7.5", "@babel/core@^7.8.4":
version "7.12.3"
@@ -53,7 +53,7 @@
semver "^5.4.1"
source-map "^0.5.0"
-"@babel/generator@^7.12.1", "@babel/generator@^7.13.0":
+"@babel/generator@^7.12.1", "@babel/generator@^7.13.9":
version "7.13.9"
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.13.9.tgz#3a7aa96f9efb8e2be42d38d80e2ceb4c64d8de39"
dependencies:
@@ -75,10 +75,10 @@
"@babel/types" "^7.12.13"
"@babel/helper-compilation-targets@^7.12.1", "@babel/helper-compilation-targets@^7.13.0", "@babel/helper-compilation-targets@^7.13.10", "@babel/helper-compilation-targets@^7.13.8":
- version "7.13.10"
- resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.13.10.tgz#1310a1678cb8427c07a753750da4f8ce442bdd0c"
+ version "7.13.13"
+ resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.13.13.tgz#2b2972a0926474853f41e4adbc69338f520600e5"
dependencies:
- "@babel/compat-data" "^7.13.8"
+ "@babel/compat-data" "^7.13.12"
"@babel/helper-validator-option" "^7.12.17"
browserslist "^4.14.5"
semver "^6.3.0"
@@ -140,11 +140,11 @@
"@babel/traverse" "^7.13.0"
"@babel/types" "^7.13.0"
-"@babel/helper-member-expression-to-functions@^7.13.0":
- version "7.13.0"
- resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.13.0.tgz#6aa4bb678e0f8c22f58cdb79451d30494461b091"
+"@babel/helper-member-expression-to-functions@^7.13.0", "@babel/helper-member-expression-to-functions@^7.13.12":
+ version "7.13.12"
+ resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.13.12.tgz#dfe368f26d426a07299d8d6513821768216e6d72"
dependencies:
- "@babel/types" "^7.13.0"
+ "@babel/types" "^7.13.12"
"@babel/helper-module-imports@^7.0.0", "@babel/helper-module-imports@^7.12.1", "@babel/helper-module-imports@^7.12.13":
version "7.12.13"
@@ -152,19 +152,24 @@
dependencies:
"@babel/types" "^7.12.13"
-"@babel/helper-module-transforms@^7.12.1", "@babel/helper-module-transforms@^7.13.0":
- version "7.13.0"
- resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.13.0.tgz#42eb4bd8eea68bab46751212c357bfed8b40f6f1"
+"@babel/helper-module-imports@^7.13.12":
+ version "7.13.12"
+ resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.13.12.tgz#c6a369a6f3621cb25da014078684da9196b61977"
dependencies:
- "@babel/helper-module-imports" "^7.12.13"
- "@babel/helper-replace-supers" "^7.13.0"
- "@babel/helper-simple-access" "^7.12.13"
+ "@babel/types" "^7.13.12"
+
+"@babel/helper-module-transforms@^7.12.1", "@babel/helper-module-transforms@^7.13.0":
+ version "7.13.12"
+ resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.13.12.tgz#600e58350490828d82282631a1422268e982ba96"
+ dependencies:
+ "@babel/helper-module-imports" "^7.13.12"
+ "@babel/helper-replace-supers" "^7.13.12"
+ "@babel/helper-simple-access" "^7.13.12"
"@babel/helper-split-export-declaration" "^7.12.13"
"@babel/helper-validator-identifier" "^7.12.11"
"@babel/template" "^7.12.13"
"@babel/traverse" "^7.13.0"
- "@babel/types" "^7.13.0"
- lodash "^4.17.19"
+ "@babel/types" "^7.13.12"
"@babel/helper-optimise-call-expression@^7.12.13":
version "7.12.13"
@@ -184,20 +189,20 @@
"@babel/helper-wrap-function" "^7.13.0"
"@babel/types" "^7.13.0"
-"@babel/helper-replace-supers@^7.12.13", "@babel/helper-replace-supers@^7.13.0":
- version "7.13.0"
- resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.13.0.tgz#6034b7b51943094cb41627848cb219cb02be1d24"
+"@babel/helper-replace-supers@^7.12.13", "@babel/helper-replace-supers@^7.13.0", "@babel/helper-replace-supers@^7.13.12":
+ version "7.13.12"
+ resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.13.12.tgz#6442f4c1ad912502481a564a7386de0c77ff3804"
dependencies:
- "@babel/helper-member-expression-to-functions" "^7.13.0"
+ "@babel/helper-member-expression-to-functions" "^7.13.12"
"@babel/helper-optimise-call-expression" "^7.12.13"
"@babel/traverse" "^7.13.0"
- "@babel/types" "^7.13.0"
+ "@babel/types" "^7.13.12"
-"@babel/helper-simple-access@^7.12.13":
- version "7.12.13"
- resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.12.13.tgz#8478bcc5cacf6aa1672b251c1d2dde5ccd61a6c4"
+"@babel/helper-simple-access@^7.12.13", "@babel/helper-simple-access@^7.13.12":
+ version "7.13.12"
+ resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.13.12.tgz#dd6c538afb61819d205a012c31792a39c7a5eaf6"
dependencies:
- "@babel/types" "^7.12.13"
+ "@babel/types" "^7.13.12"
"@babel/helper-skip-transparent-expression-wrappers@^7.12.1":
version "7.12.1"
@@ -244,9 +249,17 @@
chalk "^2.0.0"
js-tokens "^4.0.0"
-"@babel/parser@^7.1.0", "@babel/parser@^7.12.13", "@babel/parser@^7.12.3", "@babel/parser@^7.13.0", "@babel/parser@^7.7.0":
- version "7.13.11"
- resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.13.11.tgz#f93ebfc99d21c1772afbbaa153f47e7ce2f50b88"
+"@babel/parser@^7.1.0", "@babel/parser@^7.12.13", "@babel/parser@^7.12.3", "@babel/parser@^7.13.13", "@babel/parser@^7.7.0":
+ version "7.13.13"
+ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.13.13.tgz#42f03862f4aed50461e543270916b47dd501f0df"
+
+"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.13.12":
+ version "7.13.12"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.13.12.tgz#a3484d84d0b549f3fc916b99ee4783f26fabad2a"
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.13.0"
+ "@babel/helper-skip-transparent-expression-wrappers" "^7.12.1"
+ "@babel/plugin-proposal-optional-chaining" "^7.13.12"
"@babel/plugin-proposal-async-generator-functions@^7.12.1", "@babel/plugin-proposal-async-generator-functions@^7.13.8":
version "7.13.8"
@@ -359,9 +372,9 @@
"@babel/helper-skip-transparent-expression-wrappers" "^7.12.1"
"@babel/plugin-syntax-optional-chaining" "^7.8.0"
-"@babel/plugin-proposal-optional-chaining@^7.13.8":
- version "7.13.8"
- resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.13.8.tgz#e39df93efe7e7e621841babc197982e140e90756"
+"@babel/plugin-proposal-optional-chaining@^7.13.12":
+ version "7.13.12"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.13.12.tgz#ba9feb601d422e0adea6760c2bd6bbb7bfec4866"
dependencies:
"@babel/helper-plugin-utils" "^7.13.0"
"@babel/helper-skip-transparent-expression-wrappers" "^7.12.1"
@@ -854,13 +867,14 @@
semver "^5.5.0"
"@babel/preset-env@^7.12.1", "@babel/preset-env@^7.8.4":
- version "7.13.10"
- resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.13.10.tgz#b5cde31d5fe77ab2a6ab3d453b59041a1b3a5252"
+ version "7.13.12"
+ resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.13.12.tgz#6dff470478290582ac282fb77780eadf32480237"
dependencies:
- "@babel/compat-data" "^7.13.8"
+ "@babel/compat-data" "^7.13.12"
"@babel/helper-compilation-targets" "^7.13.10"
"@babel/helper-plugin-utils" "^7.13.0"
"@babel/helper-validator-option" "^7.12.17"
+ "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.13.12"
"@babel/plugin-proposal-async-generator-functions" "^7.13.8"
"@babel/plugin-proposal-class-properties" "^7.13.0"
"@babel/plugin-proposal-dynamic-import" "^7.13.8"
@@ -871,7 +885,7 @@
"@babel/plugin-proposal-numeric-separator" "^7.12.13"
"@babel/plugin-proposal-object-rest-spread" "^7.13.8"
"@babel/plugin-proposal-optional-catch-binding" "^7.13.8"
- "@babel/plugin-proposal-optional-chaining" "^7.13.8"
+ "@babel/plugin-proposal-optional-chaining" "^7.13.12"
"@babel/plugin-proposal-private-methods" "^7.13.0"
"@babel/plugin-proposal-unicode-property-regex" "^7.12.13"
"@babel/plugin-syntax-async-generators" "^7.8.4"
@@ -919,7 +933,7 @@
"@babel/plugin-transform-unicode-escapes" "^7.12.13"
"@babel/plugin-transform-unicode-regex" "^7.12.13"
"@babel/preset-modules" "^0.1.4"
- "@babel/types" "^7.13.0"
+ "@babel/types" "^7.13.12"
babel-plugin-polyfill-corejs2 "^0.1.4"
babel-plugin-polyfill-corejs3 "^0.1.3"
babel-plugin-polyfill-regenerator "^0.1.2"
@@ -993,20 +1007,19 @@
"@babel/types" "^7.12.13"
"@babel/traverse@^7.1.0", "@babel/traverse@^7.12.1", "@babel/traverse@^7.13.0", "@babel/traverse@^7.7.0":
- version "7.13.0"
- resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.13.0.tgz#6d95752475f86ee7ded06536de309a65fc8966cc"
+ version "7.13.13"
+ resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.13.13.tgz#39aa9c21aab69f74d948a486dd28a2dbdbf5114d"
dependencies:
"@babel/code-frame" "^7.12.13"
- "@babel/generator" "^7.13.0"
+ "@babel/generator" "^7.13.9"
"@babel/helper-function-name" "^7.12.13"
"@babel/helper-split-export-declaration" "^7.12.13"
- "@babel/parser" "^7.13.0"
- "@babel/types" "^7.13.0"
+ "@babel/parser" "^7.13.13"
+ "@babel/types" "^7.13.13"
debug "^4.1.0"
globals "^11.1.0"
- lodash "^4.17.19"
-"@babel/types@^7.0.0", "@babel/types@^7.12.1", "@babel/types@^7.12.13", "@babel/types@^7.12.17", "@babel/types@^7.12.6", "@babel/types@^7.13.0", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4", "@babel/types@^7.7.0":
+"@babel/types@^7.0.0", "@babel/types@^7.12.1", "@babel/types@^7.12.13", "@babel/types@^7.12.17", "@babel/types@^7.12.6", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4", "@babel/types@^7.7.0":
version "7.13.0"
resolved "https://registry.npmjs.org/@babel/types/-/types-7.13.0.tgz"
dependencies:
@@ -1014,6 +1027,14 @@
lodash "^4.17.19"
to-fast-properties "^2.0.0"
+"@babel/types@^7.13.0", "@babel/types@^7.13.12", "@babel/types@^7.13.13":
+ version "7.13.13"
+ resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.13.13.tgz#dcd8b815b38f537a3697ce84c8e3cc62197df96f"
+ dependencies:
+ "@babel/helper-validator-identifier" "^7.12.11"
+ lodash "^4.17.19"
+ to-fast-properties "^2.0.0"
+
"@bcoe/v8-coverage@^0.2.3":
version "0.2.3"
resolved "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz"
@@ -1299,8 +1320,8 @@
p-defer "^3.0.0"
"@grpc/grpc-js@^1.0.0", "@grpc/grpc-js@~1.2.0":
- version "1.2.11"
- resolved "https://registry.yarnpkg.com/@grpc/grpc-js/-/grpc-js-1.2.11.tgz#68faa56bded64844294dc6429185503376f05ff1"
+ version "1.2.12"
+ resolved "https://registry.yarnpkg.com/@grpc/grpc-js/-/grpc-js-1.2.12.tgz#0153f27512acf69184bb52c0a1035ca91d6c14b0"
dependencies:
"@types/node" ">=12.12.47"
google-auth-library "^6.1.1"
@@ -1846,8 +1867,8 @@
defer-to-connect "^1.0.1"
"@testing-library/dom@^7.28.1":
- version "7.30.0"
- resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-7.30.0.tgz#53697851f7708a1448cc30b74a2ea056dd709cd6"
+ version "7.30.1"
+ resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-7.30.1.tgz#07b6f3ccd7f1f1e34ab0406932073e2971817f3d"
dependencies:
"@babel/code-frame" "^7.10.4"
"@babel/runtime" "^7.12.5"
@@ -1939,8 +1960,8 @@
"@types/json-schema" "*"
"@types/estree@*":
- version "0.0.46"
- resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.46.tgz#0fb6bfbbeabd7a30880504993369c4bf1deab1fe"
+ version "0.0.47"
+ resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.47.tgz#d7a51db20f0650efec24cd04994f523d93172ed4"
"@types/estree@0.0.39":
version "0.0.39"
@@ -1979,9 +2000,9 @@
dependencies:
"@types/istanbul-lib-report" "*"
-"@types/jest@*", "@types/jest@26.0.20":
- version "26.0.20"
- resolved "https://registry.npmjs.org/@types/jest/-/jest-26.0.20.tgz"
+"@types/jest@*", "@types/jest@26.0.22":
+ version "26.0.22"
+ resolved "https://registry.yarnpkg.com/@types/jest/-/jest-26.0.22.tgz#8308a1debdf1b807aa47be2838acdcd91e88fbe6"
dependencies:
jest-diff "^26.0.0"
pretty-format "^26.0.0"
@@ -2013,12 +2034,12 @@
resolved "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz"
"@types/node@*", "@types/node@>=12.12.47":
- version "14.14.35"
- resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.35.tgz#42c953a4e2b18ab931f72477e7012172f4ffa313"
+ version "14.14.37"
+ resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.37.tgz#a3dd8da4eb84a996c36e331df98d82abd76b516e"
"@types/node@^13.7.0":
- version "13.13.47"
- resolved "https://registry.yarnpkg.com/@types/node/-/node-13.13.47.tgz#6eca42c3462821309b26edbc2eff0db1e37ab9bc"
+ version "13.13.48"
+ resolved "https://registry.yarnpkg.com/@types/node/-/node-13.13.48.tgz#46a3df718aed5217277f2395a682e055a487e341"
"@types/normalize-package-data@^2.4.0":
version "2.4.0"
@@ -2046,13 +2067,13 @@
"@types/react-dom@17.0.2":
version "17.0.2"
- resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-17.0.2.tgz#35654cf6c49ae162d5bc90843d5437dc38008d43"
+ resolved "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.2.tgz"
dependencies:
"@types/react" "*"
"@types/react@*", "@types/react@17.0.3":
version "17.0.3"
- resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.3.tgz#ba6e215368501ac3826951eef2904574c262cc79"
+ resolved "https://registry.npmjs.org/@types/react/-/react-17.0.3.tgz"
dependencies:
"@types/prop-types" "*"
"@types/scheduler" "*"
@@ -2070,7 +2091,7 @@
"@types/scheduler@*":
version "0.16.1"
- resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.1.tgz#18845205e86ff0038517aab7a18a62a6b9f71275"
+ resolved "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.1.tgz"
"@types/socket.io-client@1.4.36":
version "1.4.36"
@@ -2084,7 +2105,11 @@
version "2.0.0"
resolved "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.0.tgz"
-"@types/tapable@*", "@types/tapable@^1.0.5":
+"@types/tapable@^1":
+ version "1.0.7"
+ resolved "https://registry.yarnpkg.com/@types/tapable/-/tapable-1.0.7.tgz#545158342f949e8fd3bfd813224971ecddc3fac4"
+
+"@types/tapable@^1.0.5":
version "1.0.6"
resolved "https://registry.npmjs.org/@types/tapable/-/tapable-1.0.6.tgz"
@@ -2109,12 +2134,12 @@
source-map "^0.7.3"
"@types/webpack@^4.41.8":
- version "4.41.26"
- resolved "https://registry.yarnpkg.com/@types/webpack/-/webpack-4.41.26.tgz#27a30d7d531e16489f9c7607c747be6bc1a459ef"
+ version "4.41.27"
+ resolved "https://registry.yarnpkg.com/@types/webpack/-/webpack-4.41.27.tgz#f47da488c8037e7f1b2dbf2714fbbacb61ec0ffc"
dependencies:
"@types/anymatch" "*"
"@types/node" "*"
- "@types/tapable" "*"
+ "@types/tapable" "^1"
"@types/uglify-js" "*"
"@types/webpack-sources" "*"
source-map "^0.6.0"
@@ -2130,11 +2155,11 @@
"@types/yargs-parser" "*"
"@typescript-eslint/eslint-plugin@^4.5.0":
- version "4.18.0"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.18.0.tgz#50fbce93211b5b690895d20ebec6fe8db48af1f6"
+ version "4.19.0"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.19.0.tgz#56f8da9ee118fe9763af34d6a526967234f6a7f0"
dependencies:
- "@typescript-eslint/experimental-utils" "4.18.0"
- "@typescript-eslint/scope-manager" "4.18.0"
+ "@typescript-eslint/experimental-utils" "4.19.0"
+ "@typescript-eslint/scope-manager" "4.19.0"
debug "^4.1.1"
functional-red-black-tree "^1.0.1"
lodash "^4.17.15"
@@ -2142,14 +2167,14 @@
semver "^7.3.2"
tsutils "^3.17.1"
-"@typescript-eslint/experimental-utils@4.18.0", "@typescript-eslint/experimental-utils@^4.0.1":
- version "4.18.0"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.18.0.tgz#ed6c955b940334132b17100d2917449b99a91314"
+"@typescript-eslint/experimental-utils@4.19.0", "@typescript-eslint/experimental-utils@^4.0.1":
+ version "4.19.0"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.19.0.tgz#9ca379919906dc72cb0fcd817d6cb5aa2d2054c6"
dependencies:
"@types/json-schema" "^7.0.3"
- "@typescript-eslint/scope-manager" "4.18.0"
- "@typescript-eslint/types" "4.18.0"
- "@typescript-eslint/typescript-estree" "4.18.0"
+ "@typescript-eslint/scope-manager" "4.19.0"
+ "@typescript-eslint/types" "4.19.0"
+ "@typescript-eslint/typescript-estree" "4.19.0"
eslint-scope "^5.0.0"
eslint-utils "^2.0.0"
@@ -2164,28 +2189,28 @@
eslint-utils "^2.0.0"
"@typescript-eslint/parser@^4.5.0":
- version "4.18.0"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.18.0.tgz#a211edb14a69fc5177054bec04c95b185b4dde21"
+ version "4.19.0"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.19.0.tgz#4ae77513b39f164f1751f21f348d2e6cb2d11128"
dependencies:
- "@typescript-eslint/scope-manager" "4.18.0"
- "@typescript-eslint/types" "4.18.0"
- "@typescript-eslint/typescript-estree" "4.18.0"
+ "@typescript-eslint/scope-manager" "4.19.0"
+ "@typescript-eslint/types" "4.19.0"
+ "@typescript-eslint/typescript-estree" "4.19.0"
debug "^4.1.1"
-"@typescript-eslint/scope-manager@4.18.0":
- version "4.18.0"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.18.0.tgz#d75b55234c35d2ff6ac945758d6d9e53be84a427"
+"@typescript-eslint/scope-manager@4.19.0":
+ version "4.19.0"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.19.0.tgz#5e0b49eca4df7684205d957c9856f4e720717a4f"
dependencies:
- "@typescript-eslint/types" "4.18.0"
- "@typescript-eslint/visitor-keys" "4.18.0"
+ "@typescript-eslint/types" "4.19.0"
+ "@typescript-eslint/visitor-keys" "4.19.0"
"@typescript-eslint/types@3.10.1":
version "3.10.1"
resolved "https://registry.npmjs.org/@typescript-eslint/types/-/types-3.10.1.tgz"
-"@typescript-eslint/types@4.18.0":
- version "4.18.0"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.18.0.tgz#bebe323f81f2a7e2e320fac9415e60856267584a"
+"@typescript-eslint/types@4.19.0":
+ version "4.19.0"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.19.0.tgz#5181d5d2afd02e5b8f149ebb37ffc8bd7b07a568"
"@typescript-eslint/typescript-estree@3.10.1":
version "3.10.1"
@@ -2200,12 +2225,12 @@
semver "^7.3.2"
tsutils "^3.17.1"
-"@typescript-eslint/typescript-estree@4.18.0":
- version "4.18.0"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.18.0.tgz#756d3e61da8c16ab99185532c44872f4cd5538cb"
+"@typescript-eslint/typescript-estree@4.19.0":
+ version "4.19.0"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.19.0.tgz#8a709ffa400284ab72df33376df085e2e2f61147"
dependencies:
- "@typescript-eslint/types" "4.18.0"
- "@typescript-eslint/visitor-keys" "4.18.0"
+ "@typescript-eslint/types" "4.19.0"
+ "@typescript-eslint/visitor-keys" "4.19.0"
debug "^4.1.1"
globby "^11.0.1"
is-glob "^4.0.1"
@@ -2218,11 +2243,11 @@
dependencies:
eslint-visitor-keys "^1.1.0"
-"@typescript-eslint/visitor-keys@4.18.0":
- version "4.18.0"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.18.0.tgz#4e6fe2a175ee33418318a029610845a81e2ff7b6"
+"@typescript-eslint/visitor-keys@4.19.0":
+ version "4.19.0"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.19.0.tgz#cbea35109cbd9b26e597644556be4546465d8f7f"
dependencies:
- "@typescript-eslint/types" "4.18.0"
+ "@typescript-eslint/types" "4.19.0"
eslint-visitor-keys "^2.0.0"
"@webassemblyjs/ast@1.9.0":
@@ -2457,8 +2482,8 @@ ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.2, ajv@^6.12.3, ajv@^6.12.4, ajv
uri-js "^4.2.2"
ajv@^7.0.2:
- version "7.2.3"
- resolved "https://registry.yarnpkg.com/ajv/-/ajv-7.2.3.tgz#ca78d1cf458d7d36d1c3fa0794dd143406db5772"
+ version "7.2.4"
+ resolved "https://registry.yarnpkg.com/ajv/-/ajv-7.2.4.tgz#8e239d4d56cf884bccca8cca362f508446dc160f"
dependencies:
fast-deep-equal "^3.1.1"
json-schema-traverse "^1.0.0"
@@ -2487,7 +2512,13 @@ ansi-escapes@^3.1.0, ansi-escapes@^3.2.0:
version "3.2.0"
resolved "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz"
-ansi-escapes@^4.2.1, ansi-escapes@^4.3.0, ansi-escapes@^4.3.1:
+ansi-escapes@^4.2.1, ansi-escapes@^4.3.0:
+ version "4.3.2"
+ resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e"
+ dependencies:
+ type-fest "^0.21.3"
+
+ansi-escapes@^4.3.1:
version "4.3.1"
resolved "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz"
dependencies:
@@ -2626,7 +2657,7 @@ array-flatten@^2.1.0:
version "2.1.2"
resolved "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz"
-array-includes@^3.1.1, array-includes@^3.1.2:
+array-includes@^3.1.1, array-includes@^3.1.2, array-includes@^3.1.3:
version "3.1.3"
resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.3.tgz#c7f619b382ad2afaf5326cddfdc0afc61af7690a"
dependencies:
@@ -2662,9 +2693,9 @@ array.prototype.flat@^1.2.3:
define-properties "^1.1.3"
es-abstract "^1.18.0-next.1"
-array.prototype.flatmap@^1.2.3:
+array.prototype.flatmap@^1.2.4:
version "1.2.4"
- resolved "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.2.4.tgz"
+ resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.2.4.tgz#94cfd47cc1556ec0747d97f7c7738c58122004c9"
dependencies:
call-bind "^1.0.0"
define-properties "^1.1.3"
@@ -3174,9 +3205,10 @@ brorand@^1.0.1, brorand@^1.1.0:
version "1.1.0"
resolved "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz"
-browser-fs-access@0.15.3:
- version "0.15.3"
- resolved "https://registry.npmjs.org/browser-fs-access/-/browser-fs-access-0.15.3.tgz"
+browser-fs-access@0.16.2:
+ version "0.16.2"
+ resolved "https://registry.yarnpkg.com/browser-fs-access/-/browser-fs-access-0.16.2.tgz#0707e4b7ae237625b0a513f1ba79080fb33298a6"
+ integrity sha512-wuboS/Vmm85dYIvaY/oIVboNFr3YI1bV+PF19t6gtDUcjaXN7yyroJ/oKdkXJZp6UeQPdWP/tgKGmFbQkfVrYA==
browser-process-hrtime@^1.0.0:
version "1.0.0"
@@ -4704,8 +4736,8 @@ ejs@^2.6.1:
resolved "https://registry.npmjs.org/ejs/-/ejs-2.7.4.tgz"
electron-to-chromium@^1.3.564, electron-to-chromium@^1.3.649:
- version "1.3.693"
- resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.693.tgz#5089c506a925c31f93fcb173a003a22e341115dd"
+ version "1.3.701"
+ resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.701.tgz#5e796ed7ce88cd77bc7bf831cf311ef6b067c389"
elliptic@^6.5.3:
version "6.5.4"
@@ -5001,20 +5033,21 @@ eslint-plugin-react-hooks@^4.2.0:
resolved "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.2.0.tgz"
eslint-plugin-react@^7.21.5:
- version "7.22.0"
- resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.22.0.tgz#3d1c542d1d3169c45421c1215d9470e341707269"
+ version "7.23.1"
+ resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.23.1.tgz#f1a2e844c0d1967c822388204a8bc4dee8415b11"
dependencies:
- array-includes "^3.1.1"
- array.prototype.flatmap "^1.2.3"
+ array-includes "^3.1.3"
+ array.prototype.flatmap "^1.2.4"
doctrine "^2.1.0"
has "^1.0.3"
jsx-ast-utils "^2.4.1 || ^3.0.0"
- object.entries "^1.1.2"
- object.fromentries "^2.0.2"
- object.values "^1.1.1"
+ minimatch "^3.0.4"
+ object.entries "^1.1.3"
+ object.fromentries "^2.0.4"
+ object.values "^1.1.3"
prop-types "^15.7.2"
- resolve "^1.18.1"
- string.prototype.matchall "^4.0.2"
+ resolve "^2.0.0-next.3"
+ string.prototype.matchall "^4.0.4"
eslint-plugin-testing-library@^3.9.2:
version "3.10.1"
@@ -5109,8 +5142,8 @@ eslint@^6.8.0:
v8-compile-cache "^2.0.3"
eslint@^7.11.0:
- version "7.22.0"
- resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.22.0.tgz#07ecc61052fec63661a2cab6bd507127c07adc6f"
+ version "7.23.0"
+ resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.23.0.tgz#8d029d252f6e8cf45894b4bee08f5493f8e94325"
dependencies:
"@babel/code-frame" "7.12.11"
"@eslint/eslintrc" "^0.4.0"
@@ -6074,8 +6107,8 @@ google-auth-library@^6.1.1, google-auth-library@^6.1.2, google-auth-library@^6.1
lru-cache "^6.0.0"
google-auth-library@^7.0.2:
- version "7.0.2"
- resolved "https://registry.yarnpkg.com/google-auth-library/-/google-auth-library-7.0.2.tgz#cab6fc7f94ebecc97be6133d6519d9946ccf3e9d"
+ version "7.0.3"
+ resolved "https://registry.yarnpkg.com/google-auth-library/-/google-auth-library-7.0.3.tgz#a38e853722ac1a4f14a7ff4d170fd0b0bf37766b"
dependencies:
arrify "^2.0.0"
base64-js "^1.3.0"
@@ -6173,7 +6206,7 @@ has-ansi@^2.0.0:
dependencies:
ansi-regex "^2.0.0"
-has-bigints@^1.0.0:
+has-bigints@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.1.tgz#64fe6acb020673e3b78db035a5af69aa9d07b113"
@@ -6199,14 +6232,14 @@ has-flag@^4.0.0:
version "4.0.0"
resolved "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz"
-has-symbols@^1.0.0, has-symbols@^1.0.2:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.2.tgz#165d3070c00309752a1236a479331e3ac56f1423"
-
has-symbols@^1.0.1:
version "1.0.1"
resolved "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz"
+has-symbols@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.2.tgz#165d3070c00309752a1236a479331e3ac56f1423"
+
has-unicode@^2.0.0:
version "2.0.1"
resolved "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz"
@@ -6647,7 +6680,7 @@ internal-ip@^4.3.0:
default-gateway "^4.2.0"
ipaddr.js "^1.9.0"
-internal-slot@^1.0.2:
+internal-slot@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.3.tgz#7347e307deeea2faac2ac6205d4bc7d34967f59c"
dependencies:
@@ -8477,11 +8510,7 @@ nan@^2.12.1, nan@^2.14.2:
version "2.14.2"
resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.2.tgz#f5376400695168f4cc694ac9393d0c9585eeea19"
-nanoid@3.1.21:
- version "3.1.21"
- resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.1.21.tgz"
-
-nanoid@^3.1.20:
+nanoid@3.1.22, nanoid@^3.1.20:
version "3.1.22"
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.22.tgz#b35f8fb7d151990a8aebd5aa5015c03cf726f844"
@@ -8762,7 +8791,7 @@ object.assign@^4.1.0, object.assign@^4.1.2:
has-symbols "^1.0.1"
object-keys "^1.1.1"
-object.entries@^1.1.0, object.entries@^1.1.2:
+object.entries@^1.1.0, object.entries@^1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.3.tgz#c601c7f168b62374541a07ddbd3e2d5e4f7711a6"
dependencies:
@@ -8771,7 +8800,7 @@ object.entries@^1.1.0, object.entries@^1.1.2:
es-abstract "^1.18.0-next.1"
has "^1.0.3"
-object.fromentries@^2.0.2:
+object.fromentries@^2.0.4:
version "2.0.4"
resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.4.tgz#26e1ba5c4571c5c6f0890cef4473066456a120b8"
dependencies:
@@ -8794,7 +8823,7 @@ object.pick@^1.3.0:
dependencies:
isobject "^3.0.1"
-object.values@^1.1.0, object.values@^1.1.1:
+object.values@^1.1.0, object.values@^1.1.1, object.values@^1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.3.tgz#eaa8b1e17589f02f698db093f7c62ee1699742ee"
dependencies:
@@ -10248,13 +10277,13 @@ react-dev-utils@^11.0.3:
strip-ansi "6.0.0"
text-table "0.2.0"
-react-dom@17.0.1:
- version "17.0.1"
- resolved "https://registry.npmjs.org/react-dom/-/react-dom-17.0.1.tgz"
+react-dom@17.0.2:
+ version "17.0.2"
+ resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-17.0.2.tgz#ecffb6845e3ad8dbfcdc498f0d0a939736502c23"
dependencies:
loose-envify "^1.1.0"
object-assign "^4.1.1"
- scheduler "^0.20.1"
+ scheduler "^0.20.2"
react-error-overlay@^6.0.9:
version "6.0.9"
@@ -10337,9 +10366,9 @@ react-scripts@4.0.3:
optionalDependencies:
fsevents "^2.1.3"
-react@17.0.1:
- version "17.0.1"
- resolved "https://registry.npmjs.org/react/-/react-17.0.1.tgz"
+react@17.0.2:
+ version "17.0.2"
+ resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037"
dependencies:
loose-envify "^1.1.0"
object-assign "^4.1.1"
@@ -10490,7 +10519,7 @@ regex-parser@^2.2.11:
version "2.2.11"
resolved "https://registry.npmjs.org/regex-parser/-/regex-parser-2.2.11.tgz"
-regexp.prototype.flags@^1.2.0, regexp.prototype.flags@^1.3.0:
+regexp.prototype.flags@^1.2.0, regexp.prototype.flags@^1.3.1:
version "1.3.1"
resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.3.1.tgz#7ef352ae8d159e758c0eadca6f8fcb4eef07be26"
dependencies:
@@ -10533,8 +10562,8 @@ regjsgen@^0.5.1:
resolved "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.2.tgz"
regjsparser@^0.6.4:
- version "0.6.7"
- resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.6.7.tgz#c00164e1e6713c2e3ee641f1701c4b7aa0a7f86c"
+ version "0.6.9"
+ resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.6.9.tgz#b489eef7c9a2ce43727627011429cf833a7183e6"
dependencies:
jsesc "~0.5.0"
@@ -10676,6 +10705,13 @@ resolve@^1.10.0, resolve@^1.12.0, resolve@^1.13.1, resolve@^1.14.2, resolve@^1.1
is-core-module "^2.2.0"
path-parse "^1.0.6"
+resolve@^2.0.0-next.3:
+ version "2.0.0-next.3"
+ resolved "https://registry.yarnpkg.com/resolve/-/resolve-2.0.0-next.3.tgz#d41016293d4a8586a39ca5d9b5f15cbea1f55e46"
+ dependencies:
+ is-core-module "^2.2.0"
+ path-parse "^1.0.6"
+
responselike@^1.0.2:
version "1.0.2"
resolved "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz"
@@ -10903,9 +10939,9 @@ saxes@^5.0.0:
dependencies:
xmlchars "^2.2.0"
-scheduler@^0.20.1:
- version "0.20.1"
- resolved "https://registry.npmjs.org/scheduler/-/scheduler-0.20.1.tgz"
+scheduler@^0.20.2:
+ version "0.20.2"
+ resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.20.2.tgz#4baee39436e34aa93b4874bddcbf0fe8b8b50e91"
dependencies:
loose-envify "^1.1.0"
object-assign "^4.1.1"
@@ -10974,9 +11010,15 @@ semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.2.0, semver@^6.3.0:
version "6.3.0"
resolved "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz"
-semver@^7.0.0, semver@^7.1.3, semver@^7.2.1, semver@^7.3.2:
+semver@^7.0.0, semver@^7.1.3:
version "7.3.4"
- resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.4.tgz#27aaa7d2e4ca76452f98d3add093a72c943edc97"
+ resolved "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz"
+ dependencies:
+ lru-cache "^6.0.0"
+
+semver@^7.2.1, semver@^7.3.2:
+ version "7.3.5"
+ resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7"
dependencies:
lru-cache "^6.0.0"
@@ -11095,7 +11137,7 @@ shellwords@^0.1.1:
version "0.1.1"
resolved "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz"
-side-channel@^1.0.3, side-channel@^1.0.4:
+side-channel@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf"
dependencies:
@@ -11488,17 +11530,17 @@ string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0:
is-fullwidth-code-point "^3.0.0"
strip-ansi "^6.0.0"
-string.prototype.matchall@^4.0.2:
- version "4.0.3"
- resolved "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.3.tgz"
+string.prototype.matchall@^4.0.4:
+ version "4.0.4"
+ resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.4.tgz#608f255e93e072107f5de066f81a2dfb78cf6b29"
dependencies:
- call-bind "^1.0.0"
+ call-bind "^1.0.2"
define-properties "^1.1.3"
- es-abstract "^1.18.0-next.1"
+ es-abstract "^1.18.0-next.2"
has-symbols "^1.0.1"
- internal-slot "^1.0.2"
- regexp.prototype.flags "^1.3.0"
- side-channel "^1.0.3"
+ internal-slot "^1.0.3"
+ regexp.prototype.flags "^1.3.1"
+ side-channel "^1.0.4"
string.prototype.trimend@^1.0.4:
version "1.0.4"
@@ -11865,7 +11907,7 @@ through2@2.0.1:
through2@^2.0.0:
version "2.0.5"
- resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd"
+ resolved "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz"
dependencies:
readable-stream "~2.3.6"
xtend "~4.0.1"
@@ -12068,6 +12110,10 @@ type-fest@^0.20.2:
version "0.20.2"
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4"
+type-fest@^0.21.3:
+ version "0.21.3"
+ resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37"
+
type-fest@^0.3.1:
version "0.3.1"
resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.3.1.tgz"
@@ -12110,13 +12156,13 @@ typescript@4.2.3:
resolved "https://registry.npmjs.org/typescript/-/typescript-4.2.3.tgz"
unbox-primitive@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.0.tgz#eeacbc4affa28e9b3d36b5eaeccc50b3251b1d3f"
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.1.tgz#085e215625ec3162574dc8859abee78a59b14471"
dependencies:
function-bind "^1.1.1"
- has-bigints "^1.0.0"
- has-symbols "^1.0.0"
- which-boxed-primitive "^1.0.1"
+ has-bigints "^1.0.1"
+ has-symbols "^1.0.2"
+ which-boxed-primitive "^1.0.2"
unicode-canonical-property-names-ecmascript@^1.0.4:
version "1.0.4"
@@ -12305,7 +12351,7 @@ util.promisify@1.0.0:
util.promisify@~1.0.0:
version "1.0.1"
- resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.0.1.tgz#6baf7774b80eeb0f7520d8b81d07982a59abbaee"
+ resolved "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.1.tgz"
dependencies:
define-properties "^1.1.3"
es-abstract "^1.17.2"
@@ -12574,7 +12620,7 @@ whatwg-url@^8.0.0:
tr46 "^2.0.2"
webidl-conversions "^6.1.0"
-which-boxed-primitive@^1.0.1:
+which-boxed-primitive@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6"
dependencies: