Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| eda35a25b3 |
@@ -1,5 +1,5 @@
|
|||||||
REACT_APP_BACKEND_V1_GET_URL=https://json.excalidraw.com/api/v1/
|
REACT_APP_BACKEND_V1_GET_URL=https://json.excalidraw.com/api/v1/
|
||||||
REACT_APP_BACKEND_V2_GET_URL=https://json.excalidraw.com/api/v2/
|
REACT_APP_BACKEND_V2_GET_URL=https://json.excalidraw.com/api/v2/
|
||||||
REACT_APP_BACKEND_V2_POST_URL=https://json.excalidraw.com/api/v2/post/
|
REACT_APP_BACKEND_V2_POST_URL=https://json.excalidraw.com/api/v2/post/
|
||||||
REACT_APP_SOCKET_SERVER_URL=https://portal.excalidraw.com
|
REACT_APP_SOCKET_SERVER_URL=https://excalidraw-socket.herokuapp.com
|
||||||
REACT_APP_FIREBASE_CONFIG='{"apiKey":"AIzaSyAd15pYlMci_xIp9ko6wkEsDzAAA0Dn0RU","authDomain":"excalidraw-room-persistence.firebaseapp.com","databaseURL":"https://excalidraw-room-persistence.firebaseio.com","projectId":"excalidraw-room-persistence","storageBucket":"excalidraw-room-persistence.appspot.com","messagingSenderId":"654800341332","appId":"1:654800341332:web:4a692de832b55bd57ce0c1"}'
|
REACT_APP_FIREBASE_CONFIG='{"apiKey":"AIzaSyAd15pYlMci_xIp9ko6wkEsDzAAA0Dn0RU","authDomain":"excalidraw-room-persistence.firebaseapp.com","databaseURL":"https://excalidraw-room-persistence.firebaseio.com","projectId":"excalidraw-room-persistence","storageBucket":"excalidraw-room-persistence.appspot.com","messagingSenderId":"654800341332","appId":"1:654800341332:web:4a692de832b55bd57ce0c1"}'
|
||||||
|
|||||||
Generated
+9
-9
@@ -3030,9 +3030,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@testing-library/jest-dom": {
|
"@testing-library/jest-dom": {
|
||||||
"version": "5.11.8",
|
"version": "5.11.6",
|
||||||
"resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.11.8.tgz",
|
"resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.11.6.tgz",
|
||||||
"integrity": "sha512-ScyKrWQM5xNcr79PkSewnA79CLaoxVskE+f7knTOhDD9ftZSA1Jw8mj+pneqhEu3x37ncNfW84NUr7lqK+mXjA==",
|
"integrity": "sha512-cVZyUNRWwUKI0++yepYpYX7uhrP398I+tGz4zOlLVlUYnZS+Svuxv4fwLeCIy7TnBYKXUaOlQr3vopxL8ZfEnA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@babel/runtime": "^7.9.2",
|
"@babel/runtime": "^7.9.2",
|
||||||
"@types/testing-library__jest-dom": "^5.9.1",
|
"@types/testing-library__jest-dom": "^5.9.1",
|
||||||
@@ -9039,9 +9039,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"firebase-tools": {
|
"firebase-tools": {
|
||||||
"version": "9.1.0",
|
"version": "9.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/firebase-tools/-/firebase-tools-9.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/firebase-tools/-/firebase-tools-9.0.1.tgz",
|
||||||
"integrity": "sha512-hTfxL2meJSl5WuwAS6bEJ5nay7tu3MNYb4ZL2KmPL7yLM3IeT+Qd2z1evHhW1VvbDXKR6RTwlBxzdWPs4l75kA==",
|
"integrity": "sha512-OktyHgjIBJR/JPNU4Xv4NsRLWu5gDnTmYd88VUsMwzUMCkbao2NNpeBi6+0rn6U1zNhwP2WW9PngccJWg/wvSA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@google-cloud/pubsub": "^2.7.0",
|
"@google-cloud/pubsub": "^2.7.0",
|
||||||
@@ -10085,9 +10085,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@types/node": {
|
"@types/node": {
|
||||||
"version": "12.19.11",
|
"version": "12.19.9",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.19.11.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.19.9.tgz",
|
||||||
"integrity": "sha512-bwVfNTFZOrGXyiQ6t4B9sZerMSShWNsGRw8tC5DY1qImUNczS9SjT4G6PnzjCnxsu5Ubj6xjL2lgwddkxtQl5w==",
|
"integrity": "sha512-yj0DOaQeUrk3nJ0bd3Y5PeDRJ6W0r+kilosLA+dzF3dola/o9hxhMSg2sFvVcA2UHS5JSOsZp4S0c1OEXc4m1Q==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"ansi-styles": {
|
"ansi-styles": {
|
||||||
|
|||||||
+2
-2
@@ -21,7 +21,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sentry/browser": "5.29.2",
|
"@sentry/browser": "5.29.2",
|
||||||
"@sentry/integrations": "5.29.2",
|
"@sentry/integrations": "5.29.2",
|
||||||
"@testing-library/jest-dom": "5.11.8",
|
"@testing-library/jest-dom": "5.11.6",
|
||||||
"@testing-library/react": "11.2.2",
|
"@testing-library/react": "11.2.2",
|
||||||
"@types/jest": "26.0.19",
|
"@types/jest": "26.0.19",
|
||||||
"@types/nanoid": "2.1.0",
|
"@types/nanoid": "2.1.0",
|
||||||
@@ -54,7 +54,7 @@
|
|||||||
"@types/pako": "1.0.1",
|
"@types/pako": "1.0.1",
|
||||||
"eslint-config-prettier": "7.1.0",
|
"eslint-config-prettier": "7.1.0",
|
||||||
"eslint-plugin-prettier": "3.3.0",
|
"eslint-plugin-prettier": "3.3.0",
|
||||||
"firebase-tools": "9.1.0",
|
"firebase-tools": "9.0.1",
|
||||||
"husky": "4.3.6",
|
"husky": "4.3.6",
|
||||||
"jest-canvas-mock": "2.3.0",
|
"jest-canvas-mock": "2.3.0",
|
||||||
"lint-staged": "10.5.3",
|
"lint-staged": "10.5.3",
|
||||||
|
|||||||
@@ -1,22 +1,23 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { EVENT_ACTION, EVENT_CHANGE, trackEvent } from "../analytics";
|
|
||||||
import { getDefaultAppState } from "../appState";
|
|
||||||
import colors from "../colors";
|
|
||||||
import { ColorPicker } from "../components/ColorPicker";
|
import { ColorPicker } from "../components/ColorPicker";
|
||||||
import { resetZoom, trash, zoomIn, zoomOut } from "../components/icons";
|
import { getDefaultAppState } from "../appState";
|
||||||
|
import { trash, zoomIn, zoomOut, resetZoom } from "../components/icons";
|
||||||
import { ToolButton } from "../components/ToolButton";
|
import { ToolButton } from "../components/ToolButton";
|
||||||
import { getCommonBounds, getNonDeletedElements } from "../element";
|
import { t } from "../i18n";
|
||||||
|
import { getNormalizedZoom, getSelectedElements } from "../scene";
|
||||||
|
import { getNonDeletedElements } from "../element";
|
||||||
|
import { CODES, KEYS } from "../keys";
|
||||||
|
import { getShortcutKey } from "../utils";
|
||||||
|
import useIsMobile from "../is-mobile";
|
||||||
|
import { register } from "./register";
|
||||||
import { newElementWith } from "../element/mutateElement";
|
import { newElementWith } from "../element/mutateElement";
|
||||||
import { ExcalidrawElement } from "../element/types";
|
import { ExcalidrawElement } from "../element/types";
|
||||||
import { t } from "../i18n";
|
|
||||||
import useIsMobile from "../is-mobile";
|
|
||||||
import { CODES, KEYS } from "../keys";
|
|
||||||
import { getNormalizedZoom, getSelectedElements } from "../scene";
|
|
||||||
import { centerScrollOn } from "../scene/scroll";
|
|
||||||
import { getNewZoom } from "../scene/zoom";
|
|
||||||
import { AppState, NormalizedZoomValue } from "../types";
|
import { AppState, NormalizedZoomValue } from "../types";
|
||||||
import { getNewSceneName, getShortcutKey } from "../utils";
|
import { getCommonBounds } from "../element";
|
||||||
import { register } from "./register";
|
import { getNewZoom } from "../scene/zoom";
|
||||||
|
import { centerScrollOn } from "../scene/scroll";
|
||||||
|
import { EVENT_ACTION, EVENT_CHANGE, trackEvent } from "../analytics";
|
||||||
|
import colors from "../colors";
|
||||||
|
|
||||||
export const actionChangeViewBackgroundColor = register({
|
export const actionChangeViewBackgroundColor = register({
|
||||||
name: "changeViewBackgroundColor",
|
name: "changeViewBackgroundColor",
|
||||||
@@ -59,7 +60,6 @@ export const actionClearCanvas = register({
|
|||||||
),
|
),
|
||||||
appState: {
|
appState: {
|
||||||
...getDefaultAppState(),
|
...getDefaultAppState(),
|
||||||
name: getNewSceneName(),
|
|
||||||
appearance: appState.appearance,
|
appearance: appState.appearance,
|
||||||
elementLocked: appState.elementLocked,
|
elementLocked: appState.elementLocked,
|
||||||
exportBackground: appState.exportBackground,
|
exportBackground: appState.exportBackground,
|
||||||
@@ -67,7 +67,6 @@ export const actionClearCanvas = register({
|
|||||||
gridSize: appState.gridSize,
|
gridSize: appState.gridSize,
|
||||||
shouldAddWatermark: appState.shouldAddWatermark,
|
shouldAddWatermark: appState.shouldAddWatermark,
|
||||||
showStats: appState.showStats,
|
showStats: appState.showStats,
|
||||||
pasteDialog: appState.pasteDialog,
|
|
||||||
},
|
},
|
||||||
commitToHistory: true,
|
commitToHistory: true,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -9,10 +9,9 @@ import { loadFromJSON, saveAsJSON } from "../data";
|
|||||||
import { t } from "../i18n";
|
import { t } from "../i18n";
|
||||||
import useIsMobile from "../is-mobile";
|
import useIsMobile from "../is-mobile";
|
||||||
import { KEYS } from "../keys";
|
import { KEYS } from "../keys";
|
||||||
import { muteFSAbortError } from "../utils";
|
import { getDateTime, muteFSAbortError } from "../utils";
|
||||||
import { register } from "./register";
|
import { register } from "./register";
|
||||||
import "../components/ToolIcon.scss";
|
import "../components/ToolIcon.scss";
|
||||||
import { SCENE_NAME_FALLBACK } from "../constants";
|
|
||||||
|
|
||||||
export const actionChangeProjectName = register({
|
export const actionChangeProjectName = register({
|
||||||
name: "changeProjectName",
|
name: "changeProjectName",
|
||||||
@@ -23,7 +22,7 @@ export const actionChangeProjectName = register({
|
|||||||
PanelComponent: ({ appState, updateData }) => (
|
PanelComponent: ({ appState, updateData }) => (
|
||||||
<ProjectName
|
<ProjectName
|
||||||
label={t("labels.fileTitle")}
|
label={t("labels.fileTitle")}
|
||||||
value={appState.name || SCENE_NAME_FALLBACK}
|
value={appState.name || `${t("labels.untitled")}-${getDateTime()}`}
|
||||||
onChange={(name: string) => updateData(name)}
|
onChange={(name: string) => updateData(name)}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
|
|||||||
+64
-75
@@ -1,84 +1,75 @@
|
|||||||
import oc from "open-color";
|
import oc from "open-color";
|
||||||
|
import { AppState, FlooredNumber, NormalizedZoomValue } from "./types";
|
||||||
import {
|
import {
|
||||||
DEFAULT_FONT_FAMILY,
|
|
||||||
DEFAULT_FONT_SIZE,
|
DEFAULT_FONT_SIZE,
|
||||||
SCENE_NAME_FALLBACK,
|
DEFAULT_FONT_FAMILY,
|
||||||
DEFAULT_TEXT_ALIGN,
|
DEFAULT_TEXT_ALIGN,
|
||||||
} from "./constants";
|
} from "./constants";
|
||||||
import { AppState, FlooredNumber, NormalizedZoomValue } from "./types";
|
|
||||||
|
|
||||||
type DefaultAppState = Omit<AppState, "offsetTop" | "offsetLeft" | "name"> & {
|
export const getDefaultAppState = (): Omit<
|
||||||
/**
|
AppState,
|
||||||
* You should override this with current appState.name, or whatever is
|
"offsetTop" | "offsetLeft"
|
||||||
* applicable at a given place where you get default appState.
|
> => {
|
||||||
*/
|
|
||||||
name: undefined;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getDefaultAppState = (): DefaultAppState => {
|
|
||||||
return {
|
return {
|
||||||
appearance: "light",
|
appearance: "light",
|
||||||
collaborators: new Map(),
|
isLoading: false,
|
||||||
currentChartType: "bar",
|
|
||||||
currentItemBackgroundColor: "transparent",
|
|
||||||
currentItemEndArrowhead: "arrow",
|
|
||||||
currentItemFillStyle: "hachure",
|
|
||||||
currentItemFontFamily: DEFAULT_FONT_FAMILY,
|
|
||||||
currentItemFontSize: DEFAULT_FONT_SIZE,
|
|
||||||
currentItemLinearStrokeSharpness: "round",
|
|
||||||
currentItemOpacity: 100,
|
|
||||||
currentItemRoughness: 1,
|
|
||||||
currentItemStartArrowhead: null,
|
|
||||||
currentItemStrokeColor: oc.black,
|
|
||||||
currentItemStrokeSharpness: "sharp",
|
|
||||||
currentItemStrokeStyle: "solid",
|
|
||||||
currentItemStrokeWidth: 1,
|
|
||||||
currentItemTextAlign: DEFAULT_TEXT_ALIGN,
|
|
||||||
cursorButton: "up",
|
|
||||||
draggingElement: null,
|
|
||||||
editingElement: null,
|
|
||||||
editingGroupId: null,
|
|
||||||
editingLinearElement: null,
|
|
||||||
elementLocked: false,
|
|
||||||
elementType: "selection",
|
|
||||||
errorMessage: null,
|
errorMessage: null,
|
||||||
|
draggingElement: null,
|
||||||
|
resizingElement: null,
|
||||||
|
multiElement: null,
|
||||||
|
editingElement: null,
|
||||||
|
startBoundElement: null,
|
||||||
|
editingLinearElement: null,
|
||||||
|
elementType: "selection",
|
||||||
|
elementLocked: false,
|
||||||
exportBackground: true,
|
exportBackground: true,
|
||||||
exportEmbedScene: false,
|
exportEmbedScene: false,
|
||||||
fileHandle: null,
|
shouldAddWatermark: false,
|
||||||
gridSize: null,
|
currentItemStrokeColor: oc.black,
|
||||||
height: window.innerHeight,
|
currentItemBackgroundColor: "transparent",
|
||||||
isBindingEnabled: true,
|
currentItemFillStyle: "hachure",
|
||||||
isLibraryOpen: false,
|
currentItemStrokeWidth: 1,
|
||||||
isLoading: false,
|
currentItemStrokeStyle: "solid",
|
||||||
isResizing: false,
|
currentItemRoughness: 1,
|
||||||
isRotating: false,
|
currentItemOpacity: 100,
|
||||||
lastPointerDownWith: "mouse",
|
currentItemFontSize: DEFAULT_FONT_SIZE,
|
||||||
multiElement: null,
|
currentItemFontFamily: DEFAULT_FONT_FAMILY,
|
||||||
// for safety (because TS mostly doesn't distinguish optional types and
|
currentItemTextAlign: DEFAULT_TEXT_ALIGN,
|
||||||
// undefined values), we set `name` to the fallback name, but we cast it to
|
currentItemStrokeSharpness: "sharp",
|
||||||
// `undefined` so that TS forces us to explicitly specify it wherever
|
currentItemLinearStrokeSharpness: "round",
|
||||||
// possible
|
currentItemStartArrowhead: null,
|
||||||
name: (SCENE_NAME_FALLBACK as unknown) as undefined,
|
currentItemEndArrowhead: "arrow",
|
||||||
openMenu: null,
|
viewBackgroundColor: oc.white,
|
||||||
pasteDialog: { shown: false, data: null },
|
|
||||||
previousSelectedElementIds: {},
|
|
||||||
resizingElement: null,
|
|
||||||
scrolledOutside: false,
|
|
||||||
scrollX: 0 as FlooredNumber,
|
scrollX: 0 as FlooredNumber,
|
||||||
scrollY: 0 as FlooredNumber,
|
scrollY: 0 as FlooredNumber,
|
||||||
selectedElementIds: {},
|
cursorButton: "up",
|
||||||
selectedGroupIds: {},
|
scrolledOutside: false,
|
||||||
|
name: "",
|
||||||
|
isBindingEnabled: true,
|
||||||
|
isResizing: false,
|
||||||
|
isRotating: false,
|
||||||
selectionElement: null,
|
selectionElement: null,
|
||||||
shouldAddWatermark: false,
|
zoom: {
|
||||||
|
value: 1 as NormalizedZoomValue,
|
||||||
|
translation: { x: 0, y: 0 },
|
||||||
|
},
|
||||||
|
openMenu: null,
|
||||||
|
lastPointerDownWith: "mouse",
|
||||||
|
selectedElementIds: {},
|
||||||
|
previousSelectedElementIds: {},
|
||||||
shouldCacheIgnoreZoom: false,
|
shouldCacheIgnoreZoom: false,
|
||||||
showShortcutsDialog: false,
|
showShortcutsDialog: false,
|
||||||
showStats: false,
|
|
||||||
startBoundElement: null,
|
|
||||||
suggestedBindings: [],
|
suggestedBindings: [],
|
||||||
viewBackgroundColor: oc.white,
|
|
||||||
width: window.innerWidth,
|
|
||||||
zenModeEnabled: false,
|
zenModeEnabled: false,
|
||||||
zoom: { value: 1 as NormalizedZoomValue, translation: { x: 0, y: 0 } },
|
gridSize: null,
|
||||||
|
editingGroupId: null,
|
||||||
|
selectedGroupIds: {},
|
||||||
|
width: window.innerWidth,
|
||||||
|
height: window.innerHeight,
|
||||||
|
isLibraryOpen: false,
|
||||||
|
fileHandle: null,
|
||||||
|
collaborators: new Map(),
|
||||||
|
showStats: false,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -98,25 +89,24 @@ const APP_STATE_STORAGE_CONF = (<
|
|||||||
config: { [K in keyof T]: K extends keyof AppState ? T[K] : never },
|
config: { [K in keyof T]: K extends keyof AppState ? T[K] : never },
|
||||||
) => config)({
|
) => config)({
|
||||||
appearance: { browser: true, export: false },
|
appearance: { browser: true, export: false },
|
||||||
collaborators: { browser: false, export: false },
|
|
||||||
currentChartType: { browser: true, export: false },
|
|
||||||
currentItemBackgroundColor: { browser: true, export: false },
|
currentItemBackgroundColor: { browser: true, export: false },
|
||||||
currentItemEndArrowhead: { browser: true, export: false },
|
|
||||||
currentItemFillStyle: { browser: true, export: false },
|
currentItemFillStyle: { browser: true, export: false },
|
||||||
currentItemFontFamily: { browser: true, export: false },
|
currentItemFontFamily: { browser: true, export: false },
|
||||||
currentItemFontSize: { browser: true, export: false },
|
currentItemFontSize: { browser: true, export: false },
|
||||||
currentItemLinearStrokeSharpness: { browser: true, export: false },
|
|
||||||
currentItemOpacity: { browser: true, export: false },
|
currentItemOpacity: { browser: true, export: false },
|
||||||
currentItemRoughness: { browser: true, export: false },
|
currentItemRoughness: { browser: true, export: false },
|
||||||
currentItemStartArrowhead: { browser: true, export: false },
|
|
||||||
currentItemStrokeColor: { browser: true, export: false },
|
currentItemStrokeColor: { browser: true, export: false },
|
||||||
currentItemStrokeSharpness: { browser: true, export: false },
|
|
||||||
currentItemStrokeStyle: { browser: true, export: false },
|
currentItemStrokeStyle: { browser: true, export: false },
|
||||||
currentItemStrokeWidth: { browser: true, export: false },
|
currentItemStrokeWidth: { browser: true, export: false },
|
||||||
currentItemTextAlign: { browser: true, export: false },
|
currentItemTextAlign: { browser: true, export: false },
|
||||||
|
currentItemStrokeSharpness: { browser: true, export: false },
|
||||||
|
currentItemLinearStrokeSharpness: { browser: true, export: false },
|
||||||
|
currentItemStartArrowhead: { browser: true, export: false },
|
||||||
|
currentItemEndArrowhead: { browser: true, export: false },
|
||||||
cursorButton: { browser: true, export: false },
|
cursorButton: { browser: true, export: false },
|
||||||
draggingElement: { browser: false, export: false },
|
draggingElement: { browser: false, export: false },
|
||||||
editingElement: { browser: false, export: false },
|
editingElement: { browser: false, export: false },
|
||||||
|
startBoundElement: { browser: false, export: false },
|
||||||
editingGroupId: { browser: true, export: false },
|
editingGroupId: { browser: true, export: false },
|
||||||
editingLinearElement: { browser: false, export: false },
|
editingLinearElement: { browser: false, export: false },
|
||||||
elementLocked: { browser: true, export: false },
|
elementLocked: { browser: true, export: false },
|
||||||
@@ -124,7 +114,6 @@ const APP_STATE_STORAGE_CONF = (<
|
|||||||
errorMessage: { browser: false, export: false },
|
errorMessage: { browser: false, export: false },
|
||||||
exportBackground: { browser: true, export: false },
|
exportBackground: { browser: true, export: false },
|
||||||
exportEmbedScene: { browser: true, export: false },
|
exportEmbedScene: { browser: true, export: false },
|
||||||
fileHandle: { browser: false, export: false },
|
|
||||||
gridSize: { browser: true, export: true },
|
gridSize: { browser: true, export: true },
|
||||||
height: { browser: false, export: false },
|
height: { browser: false, export: false },
|
||||||
isBindingEnabled: { browser: false, export: false },
|
isBindingEnabled: { browser: false, export: false },
|
||||||
@@ -135,10 +124,7 @@ const APP_STATE_STORAGE_CONF = (<
|
|||||||
lastPointerDownWith: { browser: true, export: false },
|
lastPointerDownWith: { browser: true, export: false },
|
||||||
multiElement: { browser: false, export: false },
|
multiElement: { browser: false, export: false },
|
||||||
name: { browser: true, export: false },
|
name: { browser: true, export: false },
|
||||||
offsetLeft: { browser: false, export: false },
|
|
||||||
offsetTop: { browser: false, export: false },
|
|
||||||
openMenu: { browser: true, export: false },
|
openMenu: { browser: true, export: false },
|
||||||
pasteDialog: { browser: false, export: false },
|
|
||||||
previousSelectedElementIds: { browser: true, export: false },
|
previousSelectedElementIds: { browser: true, export: false },
|
||||||
resizingElement: { browser: false, export: false },
|
resizingElement: { browser: false, export: false },
|
||||||
scrolledOutside: { browser: true, export: false },
|
scrolledOutside: { browser: true, export: false },
|
||||||
@@ -150,13 +136,16 @@ const APP_STATE_STORAGE_CONF = (<
|
|||||||
shouldAddWatermark: { browser: true, export: false },
|
shouldAddWatermark: { browser: true, export: false },
|
||||||
shouldCacheIgnoreZoom: { browser: true, export: false },
|
shouldCacheIgnoreZoom: { browser: true, export: false },
|
||||||
showShortcutsDialog: { browser: false, export: false },
|
showShortcutsDialog: { browser: false, export: false },
|
||||||
showStats: { browser: true, export: false },
|
|
||||||
startBoundElement: { browser: false, export: false },
|
|
||||||
suggestedBindings: { browser: false, export: false },
|
suggestedBindings: { browser: false, export: false },
|
||||||
viewBackgroundColor: { browser: true, export: true },
|
viewBackgroundColor: { browser: true, export: true },
|
||||||
width: { browser: false, export: false },
|
width: { browser: false, export: false },
|
||||||
zenModeEnabled: { browser: true, export: false },
|
zenModeEnabled: { browser: true, export: false },
|
||||||
zoom: { browser: true, export: false },
|
zoom: { browser: true, export: false },
|
||||||
|
offsetTop: { browser: false, export: false },
|
||||||
|
offsetLeft: { browser: false, export: false },
|
||||||
|
fileHandle: { browser: false, export: false },
|
||||||
|
collaborators: { browser: false, export: false },
|
||||||
|
showStats: { browser: true, export: false },
|
||||||
});
|
});
|
||||||
|
|
||||||
const _clearAppStateForStorage = <ExportType extends "export" | "browser">(
|
const _clearAppStateForStorage = <ExportType extends "export" | "browser">(
|
||||||
|
|||||||
+75
-271
@@ -1,16 +1,13 @@
|
|||||||
import { EVENT_MAGIC, trackEvent } from "./analytics";
|
import { EVENT_MAGIC, trackEvent } from "./analytics";
|
||||||
import colors from "./colors";
|
import colors from "./colors";
|
||||||
import { DEFAULT_FONT_FAMILY, DEFAULT_FONT_SIZE, ENV } from "./constants";
|
import { DEFAULT_FONT_FAMILY, DEFAULT_FONT_SIZE } from "./constants";
|
||||||
import { newElement, newLinearElement, newTextElement } from "./element";
|
import { newElement, newTextElement, newLinearElement } from "./element";
|
||||||
import { NonDeletedExcalidrawElement } from "./element/types";
|
import { ExcalidrawElement } from "./element/types";
|
||||||
import { randomId } from "./random";
|
import { randomId } from "./random";
|
||||||
|
|
||||||
export type ChartElements = readonly NonDeletedExcalidrawElement[];
|
|
||||||
|
|
||||||
const BAR_WIDTH = 32;
|
const BAR_WIDTH = 32;
|
||||||
const BAR_GAP = 12;
|
const BAR_GAP = 12;
|
||||||
const BAR_HEIGHT = 256;
|
const BAR_HEIGHT = 256;
|
||||||
const GRID_OPACITY = 50;
|
|
||||||
|
|
||||||
export interface Spreadsheet {
|
export interface Spreadsheet {
|
||||||
title: string | null;
|
title: string | null;
|
||||||
@@ -142,72 +139,41 @@ export const tryParseSpreadsheet = (text: string): ParseSpreadsheetResult => {
|
|||||||
return transposedResults;
|
return transposedResults;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
const bgColors = colors.elementBackground.slice(
|
// For the maths behind it https://excalidraw.com/#json=6320864370884608,O_5xfD-Agh32tytHpRJx1g
|
||||||
2,
|
export const renderSpreadsheet = (
|
||||||
colors.elementBackground.length,
|
spreadsheet: Spreadsheet,
|
||||||
);
|
x: number,
|
||||||
|
y: number,
|
||||||
// Put all the common properties here so when the whole chart is selected
|
): ExcalidrawElement[] => {
|
||||||
// the properties dialog shows the correct selected values
|
const values = spreadsheet.values;
|
||||||
const commonProps = {
|
const max = Math.max(...values);
|
||||||
fillStyle: "hachure",
|
|
||||||
fontFamily: DEFAULT_FONT_FAMILY,
|
|
||||||
fontSize: DEFAULT_FONT_SIZE,
|
|
||||||
opacity: 100,
|
|
||||||
roughness: 1,
|
|
||||||
strokeColor: colors.elementStroke[0],
|
|
||||||
strokeSharpness: "sharp",
|
|
||||||
strokeStyle: "solid",
|
|
||||||
strokeWidth: 1,
|
|
||||||
verticalAlign: "middle",
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
const getChartDimentions = (spreadsheet: Spreadsheet) => {
|
|
||||||
const chartWidth =
|
|
||||||
(BAR_WIDTH + BAR_GAP) * spreadsheet.values.length + BAR_GAP;
|
|
||||||
const chartHeight = BAR_HEIGHT + BAR_GAP * 2;
|
const chartHeight = BAR_HEIGHT + BAR_GAP * 2;
|
||||||
return { chartWidth, chartHeight };
|
const chartWidth = (BAR_WIDTH + BAR_GAP) * values.length + BAR_GAP;
|
||||||
};
|
const maxColors = colors.elementBackground.length;
|
||||||
|
const bgColors = colors.elementBackground.slice(2, maxColors);
|
||||||
|
|
||||||
const chartXLabels = (
|
// Put all the common properties here so when the whole chart is selected
|
||||||
spreadsheet: Spreadsheet,
|
// the properties dialog shows the correct selected values
|
||||||
x: number,
|
const commonProps = {
|
||||||
y: number,
|
backgroundColor: bgColors[Math.floor(Math.random() * bgColors.length)],
|
||||||
groupId: string,
|
fillStyle: "hachure",
|
||||||
backgroundColor: string,
|
fontFamily: DEFAULT_FONT_FAMILY,
|
||||||
): ChartElements => {
|
fontSize: DEFAULT_FONT_SIZE,
|
||||||
return (
|
groupIds: [randomId()],
|
||||||
spreadsheet.labels?.map((label, index) => {
|
opacity: 100,
|
||||||
return newTextElement({
|
roughness: 1,
|
||||||
groupIds: [groupId],
|
strokeColor: colors.elementStroke[0],
|
||||||
backgroundColor,
|
strokeSharpness: "sharp",
|
||||||
...commonProps,
|
strokeStyle: "solid",
|
||||||
text: label.length > 8 ? `${label.slice(0, 5)}...` : label,
|
strokeWidth: 1,
|
||||||
x: x + index * (BAR_WIDTH + BAR_GAP) + BAR_GAP * 2,
|
verticalAlign: "middle",
|
||||||
y: y + BAR_GAP / 2,
|
} as const;
|
||||||
width: BAR_WIDTH,
|
|
||||||
angle: 5.87,
|
|
||||||
fontSize: 16,
|
|
||||||
textAlign: "center",
|
|
||||||
verticalAlign: "top",
|
|
||||||
});
|
|
||||||
}) || []
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const chartYLabels = (
|
|
||||||
spreadsheet: Spreadsheet,
|
|
||||||
x: number,
|
|
||||||
y: number,
|
|
||||||
groupId: string,
|
|
||||||
backgroundColor: string,
|
|
||||||
): ChartElements => {
|
|
||||||
const minYLabel = newTextElement({
|
const minYLabel = newTextElement({
|
||||||
groupIds: [groupId],
|
|
||||||
backgroundColor,
|
|
||||||
...commonProps,
|
...commonProps,
|
||||||
x: x - BAR_GAP,
|
x: x - BAR_GAP,
|
||||||
y: y - BAR_GAP,
|
y: y - BAR_GAP,
|
||||||
@@ -216,30 +182,14 @@ const chartYLabels = (
|
|||||||
});
|
});
|
||||||
|
|
||||||
const maxYLabel = newTextElement({
|
const maxYLabel = newTextElement({
|
||||||
groupIds: [groupId],
|
|
||||||
backgroundColor,
|
|
||||||
...commonProps,
|
...commonProps,
|
||||||
x: x - BAR_GAP,
|
x: x - BAR_GAP,
|
||||||
y: y - BAR_HEIGHT - minYLabel.height / 2,
|
y: y - BAR_HEIGHT - minYLabel.height / 2,
|
||||||
text: Math.max(...spreadsheet.values).toLocaleString(),
|
text: max.toLocaleString(),
|
||||||
textAlign: "right",
|
textAlign: "right",
|
||||||
});
|
});
|
||||||
|
|
||||||
return [minYLabel, maxYLabel];
|
const xAxisLine = newLinearElement({
|
||||||
};
|
|
||||||
|
|
||||||
const chartLines = (
|
|
||||||
spreadsheet: Spreadsheet,
|
|
||||||
x: number,
|
|
||||||
y: number,
|
|
||||||
groupId: string,
|
|
||||||
backgroundColor: string,
|
|
||||||
): ChartElements => {
|
|
||||||
const { chartWidth, chartHeight } = getChartDimentions(spreadsheet);
|
|
||||||
const xLine = newLinearElement({
|
|
||||||
backgroundColor,
|
|
||||||
groupIds: [groupId],
|
|
||||||
...commonProps,
|
|
||||||
type: "line",
|
type: "line",
|
||||||
x,
|
x,
|
||||||
y,
|
y,
|
||||||
@@ -250,12 +200,10 @@ const chartLines = (
|
|||||||
[0, 0],
|
[0, 0],
|
||||||
[chartWidth, 0],
|
[chartWidth, 0],
|
||||||
],
|
],
|
||||||
|
...commonProps,
|
||||||
});
|
});
|
||||||
|
|
||||||
const yLine = newLinearElement({
|
const yAxisLine = newLinearElement({
|
||||||
backgroundColor,
|
|
||||||
groupIds: [groupId],
|
|
||||||
...commonProps,
|
|
||||||
type: "line",
|
type: "line",
|
||||||
x,
|
x,
|
||||||
y,
|
y,
|
||||||
@@ -266,93 +214,27 @@ const chartLines = (
|
|||||||
[0, 0],
|
[0, 0],
|
||||||
[0, -chartHeight],
|
[0, -chartHeight],
|
||||||
],
|
],
|
||||||
|
...commonProps,
|
||||||
});
|
});
|
||||||
|
|
||||||
const maxLine = newLinearElement({
|
const maxValueLine = newLinearElement({
|
||||||
backgroundColor,
|
|
||||||
groupIds: [groupId],
|
|
||||||
...commonProps,
|
|
||||||
type: "line",
|
type: "line",
|
||||||
x,
|
x,
|
||||||
y: y - BAR_HEIGHT - BAR_GAP,
|
y: y - BAR_HEIGHT - BAR_GAP,
|
||||||
startArrowhead: null,
|
startArrowhead: null,
|
||||||
endArrowhead: null,
|
endArrowhead: null,
|
||||||
|
...commonProps,
|
||||||
strokeStyle: "dotted",
|
strokeStyle: "dotted",
|
||||||
width: chartWidth,
|
width: chartWidth,
|
||||||
opacity: GRID_OPACITY,
|
|
||||||
points: [
|
points: [
|
||||||
[0, 0],
|
[0, 0],
|
||||||
[chartWidth, 0],
|
[chartWidth, 0],
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
return [xLine, yLine, maxLine];
|
const bars = values.map((value, index) => {
|
||||||
};
|
|
||||||
|
|
||||||
// For the maths behind it https://excalidraw.com/#json=6320864370884608,O_5xfD-Agh32tytHpRJx1g
|
|
||||||
const chartBaseElements = (
|
|
||||||
spreadsheet: Spreadsheet,
|
|
||||||
x: number,
|
|
||||||
y: number,
|
|
||||||
groupId: string,
|
|
||||||
backgroundColor: string,
|
|
||||||
debug?: boolean,
|
|
||||||
): ChartElements => {
|
|
||||||
const { chartWidth, chartHeight } = getChartDimentions(spreadsheet);
|
|
||||||
|
|
||||||
const title = spreadsheet.title
|
|
||||||
? newTextElement({
|
|
||||||
backgroundColor,
|
|
||||||
groupIds: [groupId],
|
|
||||||
...commonProps,
|
|
||||||
text: spreadsheet.title,
|
|
||||||
x: x + chartWidth / 2,
|
|
||||||
y: y - BAR_HEIGHT - BAR_GAP * 2 - DEFAULT_FONT_SIZE,
|
|
||||||
strokeSharpness: "sharp",
|
|
||||||
strokeStyle: "solid",
|
|
||||||
textAlign: "center",
|
|
||||||
})
|
|
||||||
: null;
|
|
||||||
|
|
||||||
const debugRect = debug
|
|
||||||
? newElement({
|
|
||||||
backgroundColor,
|
|
||||||
groupIds: [groupId],
|
|
||||||
...commonProps,
|
|
||||||
type: "rectangle",
|
|
||||||
x,
|
|
||||||
y: y - chartHeight,
|
|
||||||
width: chartWidth,
|
|
||||||
height: chartHeight,
|
|
||||||
strokeColor: colors.elementStroke[0],
|
|
||||||
fillStyle: "solid",
|
|
||||||
opacity: 6,
|
|
||||||
})
|
|
||||||
: null;
|
|
||||||
|
|
||||||
return [
|
|
||||||
...(debugRect ? [debugRect] : []),
|
|
||||||
...(title ? [title] : []),
|
|
||||||
...chartXLabels(spreadsheet, x, y, groupId, backgroundColor),
|
|
||||||
...chartYLabels(spreadsheet, x, y, groupId, backgroundColor),
|
|
||||||
...chartLines(spreadsheet, x, y, groupId, backgroundColor),
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
const chartTypeBar = (
|
|
||||||
spreadsheet: Spreadsheet,
|
|
||||||
x: number,
|
|
||||||
y: number,
|
|
||||||
): ChartElements => {
|
|
||||||
const max = Math.max(...spreadsheet.values);
|
|
||||||
const groupId = randomId();
|
|
||||||
const backgroundColor = bgColors[Math.floor(Math.random() * bgColors.length)];
|
|
||||||
|
|
||||||
const bars = spreadsheet.values.map((value, index) => {
|
|
||||||
const barHeight = (value / max) * BAR_HEIGHT;
|
const barHeight = (value / max) * BAR_HEIGHT;
|
||||||
return newElement({
|
return newElement({
|
||||||
backgroundColor,
|
|
||||||
groupIds: [groupId],
|
|
||||||
...commonProps,
|
...commonProps,
|
||||||
type: "rectangle",
|
type: "rectangle",
|
||||||
x: x + index * (BAR_WIDTH + BAR_GAP) + BAR_GAP,
|
x: x + index * (BAR_WIDTH + BAR_GAP) + BAR_GAP,
|
||||||
@@ -362,120 +244,42 @@ const chartTypeBar = (
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const xLabels =
|
||||||
|
spreadsheet.labels?.map((label, index) => {
|
||||||
|
return newTextElement({
|
||||||
|
...commonProps,
|
||||||
|
text: label.length > 8 ? `${label.slice(0, 5)}...` : label,
|
||||||
|
x: x + index * (BAR_WIDTH + BAR_GAP) + BAR_GAP * 2,
|
||||||
|
y: y + BAR_GAP / 2,
|
||||||
|
width: BAR_WIDTH,
|
||||||
|
angle: 5.87,
|
||||||
|
fontSize: 16,
|
||||||
|
textAlign: "center",
|
||||||
|
verticalAlign: "top",
|
||||||
|
});
|
||||||
|
}) || [];
|
||||||
|
|
||||||
|
const title = spreadsheet.title
|
||||||
|
? newTextElement({
|
||||||
|
...commonProps,
|
||||||
|
text: spreadsheet.title,
|
||||||
|
x: x + chartWidth / 2,
|
||||||
|
y: y - BAR_HEIGHT - BAR_GAP * 2 - maxYLabel.height,
|
||||||
|
strokeSharpness: "sharp",
|
||||||
|
strokeStyle: "solid",
|
||||||
|
textAlign: "center",
|
||||||
|
})
|
||||||
|
: null;
|
||||||
|
|
||||||
|
trackEvent(EVENT_MAGIC, "chart", "bars", bars.length);
|
||||||
return [
|
return [
|
||||||
|
title,
|
||||||
...bars,
|
...bars,
|
||||||
...chartBaseElements(
|
...xLabels,
|
||||||
spreadsheet,
|
xAxisLine,
|
||||||
x,
|
yAxisLine,
|
||||||
y,
|
maxValueLine,
|
||||||
groupId,
|
minYLabel,
|
||||||
backgroundColor,
|
maxYLabel,
|
||||||
process.env.NODE_ENV === ENV.DEVELOPMENT,
|
].filter((element) => element !== null) as ExcalidrawElement[];
|
||||||
),
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
const chartTypeLine = (
|
|
||||||
spreadsheet: Spreadsheet,
|
|
||||||
x: number,
|
|
||||||
y: number,
|
|
||||||
): ChartElements => {
|
|
||||||
const max = Math.max(...spreadsheet.values);
|
|
||||||
const groupId = randomId();
|
|
||||||
const backgroundColor = bgColors[Math.floor(Math.random() * bgColors.length)];
|
|
||||||
|
|
||||||
let index = 0;
|
|
||||||
const points = [];
|
|
||||||
for (const value of spreadsheet.values) {
|
|
||||||
const cx = index * (BAR_WIDTH + BAR_GAP);
|
|
||||||
const cy = -(value / max) * BAR_HEIGHT;
|
|
||||||
points.push([cx, cy]);
|
|
||||||
index++;
|
|
||||||
}
|
|
||||||
|
|
||||||
const maxX = Math.max(...points.map((element) => element[0]));
|
|
||||||
const maxY = Math.max(...points.map((element) => element[1]));
|
|
||||||
const minX = Math.min(...points.map((element) => element[0]));
|
|
||||||
const minY = Math.min(...points.map((element) => element[1]));
|
|
||||||
|
|
||||||
const line = newLinearElement({
|
|
||||||
backgroundColor,
|
|
||||||
groupIds: [groupId],
|
|
||||||
...commonProps,
|
|
||||||
type: "line",
|
|
||||||
x: x + BAR_GAP + BAR_WIDTH / 2,
|
|
||||||
y: y - BAR_GAP,
|
|
||||||
startArrowhead: null,
|
|
||||||
endArrowhead: null,
|
|
||||||
height: maxY - minY,
|
|
||||||
width: maxX - minX,
|
|
||||||
strokeWidth: 2,
|
|
||||||
points: points as any,
|
|
||||||
});
|
|
||||||
|
|
||||||
const dots = spreadsheet.values.map((value, index) => {
|
|
||||||
const cx = index * (BAR_WIDTH + BAR_GAP) + BAR_GAP / 2;
|
|
||||||
const cy = -(value / max) * BAR_HEIGHT + BAR_GAP / 2;
|
|
||||||
return newElement({
|
|
||||||
backgroundColor,
|
|
||||||
groupIds: [groupId],
|
|
||||||
...commonProps,
|
|
||||||
fillStyle: "solid",
|
|
||||||
strokeWidth: 2,
|
|
||||||
type: "ellipse",
|
|
||||||
x: x + cx + BAR_WIDTH / 2,
|
|
||||||
y: y + cy - BAR_GAP * 2,
|
|
||||||
width: BAR_GAP,
|
|
||||||
height: BAR_GAP,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
const lines = spreadsheet.values.map((value, index) => {
|
|
||||||
const cx = index * (BAR_WIDTH + BAR_GAP) + BAR_GAP / 2;
|
|
||||||
const cy = (value / max) * BAR_HEIGHT + BAR_GAP / 2 + BAR_GAP;
|
|
||||||
return newLinearElement({
|
|
||||||
backgroundColor,
|
|
||||||
groupIds: [groupId],
|
|
||||||
...commonProps,
|
|
||||||
type: "line",
|
|
||||||
x: x + cx + BAR_WIDTH / 2 + BAR_GAP / 2,
|
|
||||||
y: y - cy,
|
|
||||||
startArrowhead: null,
|
|
||||||
endArrowhead: null,
|
|
||||||
height: cy,
|
|
||||||
strokeStyle: "dotted",
|
|
||||||
opacity: GRID_OPACITY,
|
|
||||||
points: [
|
|
||||||
[0, 0],
|
|
||||||
[0, cy],
|
|
||||||
],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
return [
|
|
||||||
...chartBaseElements(
|
|
||||||
spreadsheet,
|
|
||||||
x,
|
|
||||||
y,
|
|
||||||
groupId,
|
|
||||||
backgroundColor,
|
|
||||||
process.env.NODE_ENV === ENV.DEVELOPMENT,
|
|
||||||
),
|
|
||||||
line,
|
|
||||||
...lines,
|
|
||||||
...dots,
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
export const renderSpreadsheet = (
|
|
||||||
chartType: string,
|
|
||||||
spreadsheet: Spreadsheet,
|
|
||||||
x: number,
|
|
||||||
y: number,
|
|
||||||
): ChartElements => {
|
|
||||||
trackEvent(EVENT_MAGIC, "chart", chartType, spreadsheet.values.length);
|
|
||||||
if (chartType === "line") {
|
|
||||||
return chartTypeLine(spreadsheet, x, y);
|
|
||||||
}
|
|
||||||
return chartTypeBar(spreadsheet, x, y);
|
|
||||||
};
|
};
|
||||||
|
|||||||
+161
-175
@@ -1,169 +1,182 @@
|
|||||||
import { Point, simplify } from "points-on-curve";
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { RoughCanvas } from "roughjs/bin/canvas";
|
|
||||||
import rough from "roughjs/bin/rough";
|
import rough from "roughjs/bin/rough";
|
||||||
import "../actions";
|
import { RoughCanvas } from "roughjs/bin/canvas";
|
||||||
import { actionDeleteSelected, actionFinalize } from "../actions";
|
import { simplify, Point } from "points-on-curve";
|
||||||
import { createRedoAction, createUndoAction } from "../actions/actionHistory";
|
|
||||||
import { ActionManager } from "../actions/manager";
|
|
||||||
import { actions } from "../actions/register";
|
|
||||||
import { ActionResult } from "../actions/types";
|
|
||||||
import {
|
import {
|
||||||
EVENT_DIALOG,
|
newElement,
|
||||||
EVENT_LIBRARY,
|
newTextElement,
|
||||||
EVENT_SHAPE,
|
duplicateElement,
|
||||||
trackEvent,
|
isInvisiblySmallElement,
|
||||||
} from "../analytics";
|
isTextElement,
|
||||||
|
getCommonBounds,
|
||||||
|
getCursorForResizingElement,
|
||||||
|
getPerfectElementSize,
|
||||||
|
getNormalizedDimensions,
|
||||||
|
newLinearElement,
|
||||||
|
transformElements,
|
||||||
|
getElementWithTransformHandleType,
|
||||||
|
getResizeOffsetXY,
|
||||||
|
getResizeArrowDirection,
|
||||||
|
getTransformHandleTypeFromCoords,
|
||||||
|
isNonDeletedElement,
|
||||||
|
updateTextElement,
|
||||||
|
dragSelectedElements,
|
||||||
|
getDragOffsetXY,
|
||||||
|
dragNewElement,
|
||||||
|
hitTest,
|
||||||
|
isHittingElementBoundingBoxWithoutHittingElement,
|
||||||
|
getNonDeletedElements,
|
||||||
|
} from "../element";
|
||||||
|
import {
|
||||||
|
getElementsWithinSelection,
|
||||||
|
isOverScrollBars,
|
||||||
|
getElementsAtPosition,
|
||||||
|
getElementContainingPosition,
|
||||||
|
getNormalizedZoom,
|
||||||
|
getSelectedElements,
|
||||||
|
isSomeElementSelected,
|
||||||
|
calculateScrollCenter,
|
||||||
|
} from "../scene";
|
||||||
|
import { loadFromBlob, exportCanvas } from "../data";
|
||||||
|
|
||||||
|
import { renderScene } from "../renderer";
|
||||||
|
import {
|
||||||
|
AppState,
|
||||||
|
GestureEvent,
|
||||||
|
Gesture,
|
||||||
|
ExcalidrawProps,
|
||||||
|
SceneData,
|
||||||
|
} from "../types";
|
||||||
|
import {
|
||||||
|
ExcalidrawElement,
|
||||||
|
ExcalidrawTextElement,
|
||||||
|
NonDeleted,
|
||||||
|
ExcalidrawGenericElement,
|
||||||
|
ExcalidrawLinearElement,
|
||||||
|
ExcalidrawBindableElement,
|
||||||
|
} from "../element/types";
|
||||||
|
|
||||||
|
import { distance2d, isPathALoop, getGridPoint } from "../math";
|
||||||
|
|
||||||
|
import {
|
||||||
|
isWritableElement,
|
||||||
|
isInputLike,
|
||||||
|
isToolIcon,
|
||||||
|
debounce,
|
||||||
|
distance,
|
||||||
|
resetCursor,
|
||||||
|
viewportCoordsToSceneCoords,
|
||||||
|
sceneCoordsToViewportCoords,
|
||||||
|
setCursorForShape,
|
||||||
|
tupleToCoors,
|
||||||
|
ResolvablePromise,
|
||||||
|
resolvablePromise,
|
||||||
|
withBatchedUpdates,
|
||||||
|
} from "../utils";
|
||||||
|
import {
|
||||||
|
KEYS,
|
||||||
|
isArrowKey,
|
||||||
|
getResizeCenterPointKey,
|
||||||
|
getResizeWithSidesSameLengthKey,
|
||||||
|
getRotateWithDiscreteAngleKey,
|
||||||
|
CODES,
|
||||||
|
} from "../keys";
|
||||||
|
|
||||||
|
import { findShapeByKey } from "../shapes";
|
||||||
|
import { createHistory, SceneHistory } from "../history";
|
||||||
|
|
||||||
|
import ContextMenu from "./ContextMenu";
|
||||||
|
|
||||||
|
import { ActionManager } from "../actions/manager";
|
||||||
|
import "../actions";
|
||||||
|
import { actions } from "../actions/register";
|
||||||
|
|
||||||
|
import { ActionResult } from "../actions/types";
|
||||||
import { getDefaultAppState } from "../appState";
|
import { getDefaultAppState } from "../appState";
|
||||||
|
import { t, getLanguage } from "../i18n";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
copyToClipboard,
|
copyToClipboard,
|
||||||
parseClipboard,
|
parseClipboard,
|
||||||
probablySupportsClipboardBlob,
|
probablySupportsClipboardBlob,
|
||||||
probablySupportsClipboardWriteText,
|
probablySupportsClipboardWriteText,
|
||||||
} from "../clipboard";
|
} from "../clipboard";
|
||||||
|
import { normalizeScroll } from "../scene";
|
||||||
|
import { getCenter, getDistance } from "../gesture";
|
||||||
|
import { createUndoAction, createRedoAction } from "../actions/actionHistory";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
APP_NAME,
|
|
||||||
CANVAS_ONLY_ACTIONS,
|
|
||||||
CURSOR_TYPE,
|
CURSOR_TYPE,
|
||||||
DEFAULT_VERTICAL_ALIGN,
|
|
||||||
DRAGGING_THRESHOLD,
|
|
||||||
ELEMENT_SHIFT_TRANSLATE_AMOUNT,
|
ELEMENT_SHIFT_TRANSLATE_AMOUNT,
|
||||||
ELEMENT_TRANSLATE_AMOUNT,
|
ELEMENT_TRANSLATE_AMOUNT,
|
||||||
ENV,
|
|
||||||
EVENT,
|
|
||||||
GRID_SIZE,
|
|
||||||
LINE_CONFIRM_THRESHOLD,
|
|
||||||
MIME_TYPES,
|
|
||||||
POINTER_BUTTON,
|
POINTER_BUTTON,
|
||||||
TAP_TWICE_TIMEOUT,
|
DRAGGING_THRESHOLD,
|
||||||
TEXT_TO_CENTER_SNAP_THRESHOLD,
|
TEXT_TO_CENTER_SNAP_THRESHOLD,
|
||||||
|
LINE_CONFIRM_THRESHOLD,
|
||||||
|
EVENT,
|
||||||
|
ENV,
|
||||||
|
CANVAS_ONLY_ACTIONS,
|
||||||
|
DEFAULT_VERTICAL_ALIGN,
|
||||||
|
GRID_SIZE,
|
||||||
|
MIME_TYPES,
|
||||||
|
TAP_TWICE_TIMEOUT,
|
||||||
TOUCH_CTX_MENU_TIMEOUT,
|
TOUCH_CTX_MENU_TIMEOUT,
|
||||||
|
APP_NAME,
|
||||||
} from "../constants";
|
} from "../constants";
|
||||||
import { exportCanvas, loadFromBlob } from "../data";
|
|
||||||
import { isValidLibrary } from "../data/json";
|
import LayerUI from "./LayerUI";
|
||||||
import { Library } from "../data/library";
|
import { ScrollBars, SceneState } from "../scene/types";
|
||||||
import { restore } from "../data/restore";
|
|
||||||
import {
|
|
||||||
dragNewElement,
|
|
||||||
dragSelectedElements,
|
|
||||||
duplicateElement,
|
|
||||||
getCommonBounds,
|
|
||||||
getCursorForResizingElement,
|
|
||||||
getDragOffsetXY,
|
|
||||||
getElementWithTransformHandleType,
|
|
||||||
getNonDeletedElements,
|
|
||||||
getNormalizedDimensions,
|
|
||||||
getPerfectElementSize,
|
|
||||||
getResizeArrowDirection,
|
|
||||||
getResizeOffsetXY,
|
|
||||||
getTransformHandleTypeFromCoords,
|
|
||||||
hitTest,
|
|
||||||
isHittingElementBoundingBoxWithoutHittingElement,
|
|
||||||
isInvisiblySmallElement,
|
|
||||||
isNonDeletedElement,
|
|
||||||
isTextElement,
|
|
||||||
newElement,
|
|
||||||
newLinearElement,
|
|
||||||
newTextElement,
|
|
||||||
textWysiwyg,
|
|
||||||
transformElements,
|
|
||||||
updateTextElement,
|
|
||||||
} from "../element";
|
|
||||||
import {
|
|
||||||
bindOrUnbindSelectedElements,
|
|
||||||
fixBindingsAfterDeletion,
|
|
||||||
fixBindingsAfterDuplication,
|
|
||||||
getEligibleElementsForBinding,
|
|
||||||
getHoveredElementForBinding,
|
|
||||||
isBindingEnabled,
|
|
||||||
isLinearElementSimpleAndAlreadyBound,
|
|
||||||
maybeBindLinearElement,
|
|
||||||
shouldEnableBindingForPointerEvent,
|
|
||||||
unbindLinearElements,
|
|
||||||
updateBoundElements,
|
|
||||||
} from "../element/binding";
|
|
||||||
import { LinearElementEditor } from "../element/linearElementEditor";
|
|
||||||
import { mutateElement } from "../element/mutateElement";
|
import { mutateElement } from "../element/mutateElement";
|
||||||
import { deepCopyElement } from "../element/newElement";
|
|
||||||
import { MaybeTransformHandleType } from "../element/transformHandles";
|
|
||||||
import {
|
|
||||||
isBindingElement,
|
|
||||||
isBindingElementType,
|
|
||||||
isLinearElement,
|
|
||||||
isLinearElementType,
|
|
||||||
} from "../element/typeChecks";
|
|
||||||
import {
|
|
||||||
ExcalidrawBindableElement,
|
|
||||||
ExcalidrawElement,
|
|
||||||
ExcalidrawGenericElement,
|
|
||||||
ExcalidrawLinearElement,
|
|
||||||
ExcalidrawTextElement,
|
|
||||||
NonDeleted,
|
|
||||||
} from "../element/types";
|
|
||||||
import { getCenter, getDistance } from "../gesture";
|
|
||||||
import {
|
|
||||||
editGroupForSelectedElement,
|
|
||||||
getElementsInGroup,
|
|
||||||
getSelectedGroupIdForElement,
|
|
||||||
getSelectedGroupIds,
|
|
||||||
isElementInGroup,
|
|
||||||
isSelectedViaGroup,
|
|
||||||
selectGroupsForSelectedElements,
|
|
||||||
} from "../groups";
|
|
||||||
import { createHistory, SceneHistory } from "../history";
|
|
||||||
import { t, getLanguage, setLanguage, languages, defaultLang } from "../i18n";
|
|
||||||
import {
|
|
||||||
CODES,
|
|
||||||
getResizeCenterPointKey,
|
|
||||||
getResizeWithSidesSameLengthKey,
|
|
||||||
getRotateWithDiscreteAngleKey,
|
|
||||||
isArrowKey,
|
|
||||||
KEYS,
|
|
||||||
} from "../keys";
|
|
||||||
import { distance2d, getGridPoint, isPathALoop } from "../math";
|
|
||||||
import { renderScene } from "../renderer";
|
|
||||||
import { invalidateShapeForElement } from "../renderer/renderElement";
|
import { invalidateShapeForElement } from "../renderer/renderElement";
|
||||||
import {
|
import {
|
||||||
calculateScrollCenter,
|
isLinearElement,
|
||||||
getElementContainingPosition,
|
isLinearElementType,
|
||||||
getElementsAtPosition,
|
isBindingElement,
|
||||||
getElementsWithinSelection,
|
isBindingElementType,
|
||||||
getNormalizedZoom,
|
} from "../element/typeChecks";
|
||||||
getSelectedElements,
|
import { actionFinalize, actionDeleteSelected } from "../actions";
|
||||||
isOverScrollBars,
|
|
||||||
isSomeElementSelected,
|
import { LinearElementEditor } from "../element/linearElementEditor";
|
||||||
normalizeScroll,
|
import {
|
||||||
} from "../scene";
|
getSelectedGroupIds,
|
||||||
|
isSelectedViaGroup,
|
||||||
|
selectGroupsForSelectedElements,
|
||||||
|
isElementInGroup,
|
||||||
|
getSelectedGroupIdForElement,
|
||||||
|
getElementsInGroup,
|
||||||
|
editGroupForSelectedElement,
|
||||||
|
} from "../groups";
|
||||||
|
import { Library } from "../data/library";
|
||||||
import Scene from "../scene/Scene";
|
import Scene from "../scene/Scene";
|
||||||
import { SceneState, ScrollBars } from "../scene/types";
|
import {
|
||||||
|
getHoveredElementForBinding,
|
||||||
|
maybeBindLinearElement,
|
||||||
|
getEligibleElementsForBinding,
|
||||||
|
bindOrUnbindSelectedElements,
|
||||||
|
unbindLinearElements,
|
||||||
|
fixBindingsAfterDuplication,
|
||||||
|
fixBindingsAfterDeletion,
|
||||||
|
isLinearElementSimpleAndAlreadyBound,
|
||||||
|
isBindingEnabled,
|
||||||
|
updateBoundElements,
|
||||||
|
shouldEnableBindingForPointerEvent,
|
||||||
|
} from "../element/binding";
|
||||||
|
import { MaybeTransformHandleType } from "../element/transformHandles";
|
||||||
|
import { deepCopyElement } from "../element/newElement";
|
||||||
|
import { renderSpreadsheet } from "../charts";
|
||||||
|
import { isValidLibrary } from "../data/json";
|
||||||
import { getNewZoom } from "../scene/zoom";
|
import { getNewZoom } from "../scene/zoom";
|
||||||
import { findShapeByKey } from "../shapes";
|
import { restore } from "../data/restore";
|
||||||
import {
|
import {
|
||||||
AppState,
|
EVENT_DIALOG,
|
||||||
ExcalidrawProps,
|
EVENT_LIBRARY,
|
||||||
Gesture,
|
EVENT_SHAPE,
|
||||||
GestureEvent,
|
trackEvent,
|
||||||
SceneData,
|
} from "../analytics";
|
||||||
} from "../types";
|
|
||||||
import {
|
|
||||||
debounce,
|
|
||||||
distance,
|
|
||||||
getNewSceneName,
|
|
||||||
isInputLike,
|
|
||||||
isToolIcon,
|
|
||||||
isWritableElement,
|
|
||||||
resetCursor,
|
|
||||||
ResolvablePromise,
|
|
||||||
resolvablePromise,
|
|
||||||
sceneCoordsToViewportCoords,
|
|
||||||
setCursorForShape,
|
|
||||||
tupleToCoors,
|
|
||||||
viewportCoordsToSceneCoords,
|
|
||||||
withBatchedUpdates,
|
|
||||||
} from "../utils";
|
|
||||||
import ContextMenu from "./ContextMenu";
|
|
||||||
import LayerUI from "./LayerUI";
|
|
||||||
import { Stats } from "./Stats";
|
import { Stats } from "./Stats";
|
||||||
|
import { textWysiwyg } from "../element/textWysiwyg";
|
||||||
|
|
||||||
const { history } = createHistory();
|
const { history } = createHistory();
|
||||||
|
|
||||||
@@ -282,7 +295,6 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
|||||||
} = props;
|
} = props;
|
||||||
this.state = {
|
this.state = {
|
||||||
...defaultAppState,
|
...defaultAppState,
|
||||||
name: getNewSceneName(),
|
|
||||||
isLoading: true,
|
isLoading: true,
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
@@ -334,7 +346,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
|||||||
offsetLeft,
|
offsetLeft,
|
||||||
} = this.state;
|
} = this.state;
|
||||||
|
|
||||||
const { onCollabButtonClick, onExportToBackend, renderFooter } = this.props;
|
const { onCollabButtonClick, onExportToBackend } = this.props;
|
||||||
const canvasScale = window.devicePixelRatio;
|
const canvasScale = window.devicePixelRatio;
|
||||||
|
|
||||||
const canvasWidth = canvasDOMWidth * canvasScale;
|
const canvasWidth = canvasDOMWidth * canvasScale;
|
||||||
@@ -362,7 +374,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
|||||||
elements={this.scene.getElements()}
|
elements={this.scene.getElements()}
|
||||||
onCollabButtonClick={onCollabButtonClick}
|
onCollabButtonClick={onCollabButtonClick}
|
||||||
onLockToggle={this.toggleLock}
|
onLockToggle={this.toggleLock}
|
||||||
onInsertElements={(elements) =>
|
onInsertShape={(elements) =>
|
||||||
this.addElementsFromPasteOrLibrary(
|
this.addElementsFromPasteOrLibrary(
|
||||||
elements,
|
elements,
|
||||||
DEFAULT_PASTE_X,
|
DEFAULT_PASTE_X,
|
||||||
@@ -371,10 +383,9 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
|||||||
}
|
}
|
||||||
zenModeEnabled={zenModeEnabled}
|
zenModeEnabled={zenModeEnabled}
|
||||||
toggleZenMode={this.toggleZenMode}
|
toggleZenMode={this.toggleZenMode}
|
||||||
langCode={getLanguage().code}
|
lng={getLanguage().lng}
|
||||||
isCollaborating={this.props.isCollaborating || false}
|
isCollaborating={this.props.isCollaborating || false}
|
||||||
onExportToBackend={onExportToBackend}
|
onExportToBackend={onExportToBackend}
|
||||||
renderCustomFooter={renderFooter}
|
|
||||||
/>
|
/>
|
||||||
{this.state.showStats && (
|
{this.state.showStats && (
|
||||||
<Stats
|
<Stats
|
||||||
@@ -530,7 +541,6 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
|||||||
this.scene.replaceAllElements([]);
|
this.scene.replaceAllElements([]);
|
||||||
this.setState((state) => ({
|
this.setState((state) => ({
|
||||||
...getDefaultAppState(),
|
...getDefaultAppState(),
|
||||||
name: getNewSceneName(),
|
|
||||||
isLoading: opts?.resetLoadingState ? false : state.isLoading,
|
isLoading: opts?.resetLoadingState ? false : state.isLoading,
|
||||||
appearance: this.state.appearance,
|
appearance: this.state.appearance,
|
||||||
}));
|
}));
|
||||||
@@ -742,10 +752,6 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate(prevProps: ExcalidrawProps, prevState: AppState) {
|
componentDidUpdate(prevProps: ExcalidrawProps, prevState: AppState) {
|
||||||
if (prevProps.langCode !== this.props.langCode) {
|
|
||||||
this.updateLanguage();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
prevProps.width !== this.props.width ||
|
prevProps.width !== this.props.width ||
|
||||||
prevProps.height !== this.props.height ||
|
prevProps.height !== this.props.height ||
|
||||||
@@ -869,16 +875,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
|||||||
|
|
||||||
history.record(this.state, this.scene.getElementsIncludingDeleted());
|
history.record(this.state, this.scene.getElementsIncludingDeleted());
|
||||||
|
|
||||||
// Do not notify consumers if we're still loading the scene. Among other
|
this.props.onChange?.(this.scene.getElementsIncludingDeleted(), this.state);
|
||||||
// potential issues, this fixes a case where the tab isn't focused during
|
|
||||||
// init, which would trigger onChange with empty elements, which would then
|
|
||||||
// override whatever is in localStorage currently.
|
|
||||||
if (!this.state.isLoading) {
|
|
||||||
this.props.onChange?.(
|
|
||||||
this.scene.getElementsIncludingDeleted(),
|
|
||||||
this.state,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy/paste
|
// Copy/paste
|
||||||
@@ -1007,12 +1004,9 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
|||||||
if (data.errorMessage) {
|
if (data.errorMessage) {
|
||||||
this.setState({ errorMessage: data.errorMessage });
|
this.setState({ errorMessage: data.errorMessage });
|
||||||
} else if (data.spreadsheet) {
|
} else if (data.spreadsheet) {
|
||||||
this.setState({
|
this.addElementsFromPasteOrLibrary(
|
||||||
pasteDialog: {
|
renderSpreadsheet(data.spreadsheet, cursorX, cursorY),
|
||||||
data: data.spreadsheet,
|
);
|
||||||
shown: true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
} else if (data.elements) {
|
} else if (data.elements) {
|
||||||
this.addElementsFromPasteOrLibrary(data.elements);
|
this.addElementsFromPasteOrLibrary(data.elements);
|
||||||
} else if (data.text) {
|
} else if (data.text) {
|
||||||
@@ -3857,14 +3851,6 @@ class App extends React.Component<ExcalidrawProps, AppState> {
|
|||||||
offsetTop: typeof offsets?.offsetTop === "number" ? offsets.offsetTop : 0,
|
offsetTop: typeof offsets?.offsetTop === "number" ? offsets.offsetTop : 0,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private async updateLanguage() {
|
|
||||||
const currentLang =
|
|
||||||
languages.find((lang) => lang.code === this.props.langCode) ||
|
|
||||||
defaultLang;
|
|
||||||
await setLanguage(currentLang);
|
|
||||||
this.setAppState({});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -218,7 +218,7 @@
|
|||||||
left: 2px;
|
left: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media #{$is-mobile-query} {
|
@media #{$media-query} {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
@import "../css/_variables";
|
@import "open-color/open-color.scss";
|
||||||
|
|
||||||
.excalidraw {
|
.excalidraw {
|
||||||
.context-menu {
|
.context-menu {
|
||||||
@@ -42,16 +42,16 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&.dangerous {
|
&.dangerous {
|
||||||
.context-menu-option__label {
|
div:nth-child(1) {
|
||||||
color: $oc-red-7;
|
color: $oc-red-7;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.context-menu-option__label {
|
div:nth-child(1) {
|
||||||
justify-self: start;
|
justify-self: start;
|
||||||
margin-inline-end: 20px;
|
margin-inline-end: 20px;
|
||||||
}
|
}
|
||||||
.context-menu-option__shortcut {
|
div:nth-child(2) {
|
||||||
justify-self: end;
|
justify-self: end;
|
||||||
opacity: 0.6;
|
opacity: 0.6;
|
||||||
font-size: 0.7rem;
|
font-size: 0.7rem;
|
||||||
@@ -63,7 +63,7 @@
|
|||||||
background-color: var(--select-highlight-color);
|
background-color: var(--select-highlight-color);
|
||||||
|
|
||||||
&.dangerous {
|
&.dangerous {
|
||||||
.context-menu-option__label {
|
div:nth-child(1) {
|
||||||
color: var(--popup-background-color);
|
color: var(--popup-background-color);
|
||||||
}
|
}
|
||||||
background-color: $oc-red-6;
|
background-color: $oc-red-6;
|
||||||
@@ -73,18 +73,4 @@
|
|||||||
.context-menu-option:focus {
|
.context-menu-option:focus {
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media #{$is-mobile-query} {
|
|
||||||
.context-menu-option {
|
|
||||||
display: block;
|
|
||||||
|
|
||||||
.context-menu-option__label {
|
|
||||||
margin-inline-end: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.context-menu-option__shortcut {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,8 +51,8 @@ const ContextMenu = ({ options, onCloseRequest, top, left }: Props) => {
|
|||||||
${checked ? "checkmark" : ""}`}
|
${checked ? "checkmark" : ""}`}
|
||||||
onClick={action}
|
onClick={action}
|
||||||
>
|
>
|
||||||
<div className="context-menu-option__label">{label}</div>
|
<div>{label}</div>
|
||||||
<div className="context-menu-option__shortcut">
|
<div>
|
||||||
{shortcutName
|
{shortcutName
|
||||||
? getShortcutFromShortcutName(shortcutName)
|
? getShortcutFromShortcutName(shortcutName)
|
||||||
: ""}
|
: ""}
|
||||||
|
|||||||
@@ -7,9 +7,6 @@
|
|||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
grid-template-columns: 1fr calc(var(--space-factor) * 7);
|
grid-template-columns: 1fr calc(var(--space-factor) * 7);
|
||||||
grid-gap: var(--metric);
|
grid-gap: var(--metric);
|
||||||
padding: calc(var(--space-factor) * 2);
|
|
||||||
text-align: center;
|
|
||||||
font-variant: small-caps;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.Dialog__titleContent {
|
.Dialog__titleContent {
|
||||||
@@ -21,11 +18,7 @@
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.Dialog__content {
|
@media #{$media-query} {
|
||||||
padding: 0 16px 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media #{$is-mobile-query} {
|
|
||||||
.Dialog {
|
.Dialog {
|
||||||
--metric: calc(var(--space-factor) * 4);
|
--metric: calc(var(--space-factor) * 4);
|
||||||
--inset-left: #{"max(var(--metric), var(--sal))"};
|
--inset-left: #{"max(var(--metric), var(--sal))"};
|
||||||
@@ -37,8 +30,13 @@
|
|||||||
var(--space-factor) * 7
|
var(--space-factor) * 7
|
||||||
);
|
);
|
||||||
position: sticky;
|
position: sticky;
|
||||||
top: 0;
|
top: calc(-1 * var(--metric));
|
||||||
|
margin: calc(-1 * var(--inset-right));
|
||||||
|
margin-top: calc(-1 * var(--metric));
|
||||||
|
margin-bottom: var(--metric);
|
||||||
padding: calc(var(--space-factor) * 2);
|
padding: calc(var(--space-factor) * 2);
|
||||||
|
padding-left: var(--inset-left);
|
||||||
|
padding-right: var(--inset-right);
|
||||||
background: var(--bg-color-island);
|
background: var(--bg-color-island);
|
||||||
font-size: 1.25em;
|
font-size: 1.25em;
|
||||||
|
|
||||||
|
|||||||
+14
-14
@@ -1,12 +1,13 @@
|
|||||||
import clsx from "clsx";
|
|
||||||
import React, { useCallback, useEffect, useState } from "react";
|
import React, { useCallback, useEffect, useState } from "react";
|
||||||
|
import clsx from "clsx";
|
||||||
|
import { Modal } from "./Modal";
|
||||||
|
import { Island } from "./Island";
|
||||||
import { t } from "../i18n";
|
import { t } from "../i18n";
|
||||||
import useIsMobile from "../is-mobile";
|
import useIsMobile from "../is-mobile";
|
||||||
import { KEYS } from "../keys";
|
|
||||||
import "./Dialog.scss";
|
|
||||||
import { back, close } from "./icons";
|
import { back, close } from "./icons";
|
||||||
import { Island } from "./Island";
|
import { KEYS } from "../keys";
|
||||||
import { Modal } from "./Modal";
|
|
||||||
|
import "./Dialog.scss";
|
||||||
|
|
||||||
const useRefState = <T,>() => {
|
const useRefState = <T,>() => {
|
||||||
const [refValue, setRefValue] = useState<T | null>(null);
|
const [refValue, setRefValue] = useState<T | null>(null);
|
||||||
@@ -19,10 +20,9 @@ const useRefState = <T,>() => {
|
|||||||
export const Dialog = (props: {
|
export const Dialog = (props: {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
className?: string;
|
className?: string;
|
||||||
small?: boolean;
|
maxWidth?: number;
|
||||||
onCloseRequest(): void;
|
onCloseRequest(): void;
|
||||||
title: React.ReactNode;
|
title: React.ReactNode;
|
||||||
autofocus?: boolean;
|
|
||||||
}) => {
|
}) => {
|
||||||
const [islandNode, setIslandNode] = useRefState<HTMLDivElement>();
|
const [islandNode, setIslandNode] = useRefState<HTMLDivElement>();
|
||||||
|
|
||||||
@@ -33,7 +33,7 @@ export const Dialog = (props: {
|
|||||||
|
|
||||||
const focusableElements = queryFocusableElements(islandNode);
|
const focusableElements = queryFocusableElements(islandNode);
|
||||||
|
|
||||||
if (focusableElements.length > 0 && props.autofocus !== false) {
|
if (focusableElements.length > 0) {
|
||||||
// If there's an element other than close, focus it.
|
// If there's an element other than close, focus it.
|
||||||
(focusableElements[1] || focusableElements[0]).focus();
|
(focusableElements[1] || focusableElements[0]).focus();
|
||||||
}
|
}
|
||||||
@@ -62,7 +62,7 @@ export const Dialog = (props: {
|
|||||||
islandNode.addEventListener("keydown", handleKeyDown);
|
islandNode.addEventListener("keydown", handleKeyDown);
|
||||||
|
|
||||||
return () => islandNode.removeEventListener("keydown", handleKeyDown);
|
return () => islandNode.removeEventListener("keydown", handleKeyDown);
|
||||||
}, [islandNode, props.autofocus]);
|
}, [islandNode]);
|
||||||
|
|
||||||
const queryFocusableElements = (node: HTMLElement) => {
|
const queryFocusableElements = (node: HTMLElement) => {
|
||||||
const focusableElements = node.querySelectorAll<HTMLElement>(
|
const focusableElements = node.querySelectorAll<HTMLElement>(
|
||||||
@@ -76,11 +76,11 @@ export const Dialog = (props: {
|
|||||||
<Modal
|
<Modal
|
||||||
className={clsx("Dialog", props.className)}
|
className={clsx("Dialog", props.className)}
|
||||||
labelledBy="dialog-title"
|
labelledBy="dialog-title"
|
||||||
maxWidth={props.small ? 550 : 800}
|
maxWidth={props.maxWidth}
|
||||||
onCloseRequest={props.onCloseRequest}
|
onCloseRequest={props.onCloseRequest}
|
||||||
>
|
>
|
||||||
<Island ref={setIslandNode}>
|
<Island padding={4} ref={setIslandNode}>
|
||||||
<h3 id="dialog-title" className="Dialog__title">
|
<h2 id="dialog-title" className="Dialog__title">
|
||||||
<span className="Dialog__titleContent">{props.title}</span>
|
<span className="Dialog__titleContent">{props.title}</span>
|
||||||
<button
|
<button
|
||||||
className="Modal__close"
|
className="Modal__close"
|
||||||
@@ -89,8 +89,8 @@ export const Dialog = (props: {
|
|||||||
>
|
>
|
||||||
{useIsMobile() ? back : close}
|
{useIsMobile() ? back : close}
|
||||||
</button>
|
</button>
|
||||||
</h3>
|
</h2>
|
||||||
<div className="Dialog__content">{props.children}</div>
|
{props.children}
|
||||||
</Island>
|
</Island>
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ export const ErrorDialog = ({
|
|||||||
<>
|
<>
|
||||||
{modalIsShown && (
|
{modalIsShown && (
|
||||||
<Dialog
|
<Dialog
|
||||||
small
|
maxWidth={500}
|
||||||
onCloseRequest={handleClose}
|
onCloseRequest={handleClose}
|
||||||
title={t("errorDialog.title")}
|
title={t("errorDialog.title")}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -37,7 +37,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media #{$is-mobile-query} {
|
@media (max-width: 550px) {
|
||||||
.ExportDialog {
|
.ExportDialog {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@@ -51,7 +51,9 @@
|
|||||||
.ExportDialog__actions > * {
|
.ExportDialog__actions > * {
|
||||||
margin-bottom: calc(var(--space-factor) * 3);
|
margin-bottom: calc(var(--space-factor) * 3);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media #{$media-query} {
|
||||||
.ExportDialog__preview canvas {
|
.ExportDialog__preview canvas {
|
||||||
max-height: 30vh;
|
max-height: 30vh;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -100,6 +100,7 @@ const ExportModal = ({
|
|||||||
exportPadding,
|
exportPadding,
|
||||||
scale,
|
scale,
|
||||||
shouldAddWatermark,
|
shouldAddWatermark,
|
||||||
|
watermarkText: t("labels.madeWithExcalidraw"),
|
||||||
});
|
});
|
||||||
|
|
||||||
// if converting to blob fails, there's some problem that will
|
// if converting to blob fails, there's some problem that will
|
||||||
@@ -175,6 +176,7 @@ const ExportModal = ({
|
|||||||
exportPadding,
|
exportPadding,
|
||||||
shouldAddWatermark,
|
shouldAddWatermark,
|
||||||
s,
|
s,
|
||||||
|
t("labels.madeWithExcalidraw"),
|
||||||
);
|
);
|
||||||
|
|
||||||
const scaleButtonTitle = `${t(
|
const scaleButtonTitle = `${t(
|
||||||
@@ -262,7 +264,11 @@ export const ExportDialog = ({
|
|||||||
ref={triggerButton}
|
ref={triggerButton}
|
||||||
/>
|
/>
|
||||||
{modalIsShown && (
|
{modalIsShown && (
|
||||||
<Dialog onCloseRequest={handleClose} title={t("buttons.export")}>
|
<Dialog
|
||||||
|
maxWidth={800}
|
||||||
|
onCloseRequest={handleClose}
|
||||||
|
title={t("buttons.export")}
|
||||||
|
>
|
||||||
<ExportModal
|
<ExportModal
|
||||||
elements={elements}
|
elements={elements}
|
||||||
appState={appState}
|
appState={appState}
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
@import "../css/_variables";
|
@import "../css/_variables";
|
||||||
|
|
||||||
// this is loosely based on the longest hint text
|
|
||||||
$wide-viewport-width: 1000px;
|
|
||||||
|
|
||||||
.excalidraw {
|
.excalidraw {
|
||||||
.HintViewer {
|
.HintViewer {
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
@@ -19,9 +16,12 @@ $wide-viewport-width: 1000px;
|
|||||||
color: $oc-gray-6;
|
color: $oc-gray-6;
|
||||||
font-size: 0.8rem;
|
font-size: 0.8rem;
|
||||||
|
|
||||||
@media #{$is-mobile-query} {
|
@media (min-width: 1200px) {
|
||||||
|
white-space: pre;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media #{$media-query} {
|
||||||
position: static;
|
position: static;
|
||||||
padding-right: 2em;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
> span {
|
> span {
|
||||||
|
|||||||
@@ -110,7 +110,7 @@
|
|||||||
:root[dir="rtl"] & {
|
:root[dir="rtl"] & {
|
||||||
left: 2px;
|
left: 2px;
|
||||||
}
|
}
|
||||||
@media #{$is-mobile-query} {
|
@media #{$media-query} {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,29 +1,18 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
import { LoadingMessage } from "./LoadingMessage";
|
import { LoadingMessage } from "./LoadingMessage";
|
||||||
import {
|
import { setLanguageFirstTime } from "../i18n";
|
||||||
defaultLang,
|
|
||||||
Language,
|
|
||||||
languages,
|
|
||||||
setLanguageFirstTime,
|
|
||||||
} from "../i18n";
|
|
||||||
|
|
||||||
interface Props {
|
export class InitializeApp extends React.Component<
|
||||||
langCode: Language["code"];
|
any,
|
||||||
}
|
{ isLoading: boolean }
|
||||||
interface State {
|
> {
|
||||||
isLoading: boolean;
|
|
||||||
}
|
|
||||||
export class InitializeApp extends React.Component<Props, State> {
|
|
||||||
public state: { isLoading: boolean } = {
|
public state: { isLoading: boolean } = {
|
||||||
isLoading: true,
|
isLoading: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
async componentDidMount() {
|
async componentDidMount() {
|
||||||
const currentLang =
|
await setLanguageFirstTime();
|
||||||
languages.find((lang) => lang.code === this.props.langCode) ||
|
|
||||||
defaultLang;
|
|
||||||
await setLanguageFirstTime(currentLang);
|
|
||||||
this.setState({
|
this.setState({
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
background-color: var(--bg-color-island);
|
background-color: var(--bg-color-island);
|
||||||
backdrop-filter: saturate(100%) blur(10px);
|
backdrop-filter: saturate(100%) blur(10px);
|
||||||
box-shadow: var(--shadow-island);
|
box-shadow: var(--shadow-island);
|
||||||
border-radius: 4px;
|
border-radius: var(--border-radius-m);
|
||||||
padding: calc(var(--padding) * var(--space-factor));
|
padding: calc(var(--padding) * var(--space-factor));
|
||||||
position: relative;
|
position: relative;
|
||||||
transition: box-shadow 0.5s ease-in-out;
|
transition: box-shadow 0.5s ease-in-out;
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
import * as i18n from "../../i18n";
|
import * as i18n from "../i18n";
|
||||||
|
|
||||||
export const LanguageList = ({
|
export const LanguageList = ({
|
||||||
onChange,
|
onChange,
|
||||||
languages = i18n.languages,
|
languages = i18n.languages,
|
||||||
currentLangCode = i18n.getLanguage().code,
|
currentLanguage = i18n.getLanguage().lng,
|
||||||
floating,
|
floating,
|
||||||
}: {
|
}: {
|
||||||
languages?: { code: string; label: string }[];
|
languages?: { lng: string; label: string }[];
|
||||||
onChange: (langCode: i18n.Language["code"]) => void;
|
onChange: (value: string) => void;
|
||||||
currentLangCode?: i18n.Language["code"];
|
currentLanguage?: string;
|
||||||
floating?: boolean;
|
floating?: boolean;
|
||||||
}) => (
|
}) => (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
@@ -19,12 +19,12 @@ export const LanguageList = ({
|
|||||||
"dropdown-select--floating": floating,
|
"dropdown-select--floating": floating,
|
||||||
})}
|
})}
|
||||||
onChange={({ target }) => onChange(target.value)}
|
onChange={({ target }) => onChange(target.value)}
|
||||||
value={currentLangCode}
|
value={currentLanguage}
|
||||||
aria-label={i18n.t("buttons.selectLanguage")}
|
aria-label={i18n.t("buttons.selectLanguage")}
|
||||||
>
|
>
|
||||||
{languages.map((lang) => (
|
{languages.map((language) => (
|
||||||
<option key={lang.code} value={lang.code}>
|
<option key={language.lng} value={language.lng}>
|
||||||
{lang.label}
|
{language.label}
|
||||||
</option>
|
</option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
@@ -7,23 +7,11 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|
||||||
.layer-ui__library-header {
|
.browse-libraries {
|
||||||
display: flex;
|
position: absolute;
|
||||||
align-items: center;
|
right: 12px;
|
||||||
width: 100%;
|
top: 16px;
|
||||||
margin: 2px 0;
|
white-space: nowrap;
|
||||||
|
|
||||||
button {
|
|
||||||
// 2px from the left to account for focus border of left-most button
|
|
||||||
margin: 0 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
margin-left: auto;
|
|
||||||
// 17px for scrollbar (needed for overlay scrollbars on Big Sur?) + 1px extra
|
|
||||||
padding-right: 18px;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+77
-85
@@ -19,7 +19,8 @@ import { FixedSideContainer } from "./FixedSideContainer";
|
|||||||
import { UserList } from "./UserList";
|
import { UserList } from "./UserList";
|
||||||
import { LockIcon } from "./LockIcon";
|
import { LockIcon } from "./LockIcon";
|
||||||
import { ExportDialog, ExportCB } from "./ExportDialog";
|
import { ExportDialog, ExportCB } from "./ExportDialog";
|
||||||
import { Language, t } from "../i18n";
|
import { LanguageList } from "./LanguageList";
|
||||||
|
import { t, languages, setLanguage } from "../i18n";
|
||||||
import { HintViewer } from "./HintViewer";
|
import { HintViewer } from "./HintViewer";
|
||||||
import useIsMobile from "../is-mobile";
|
import useIsMobile from "../is-mobile";
|
||||||
|
|
||||||
@@ -40,7 +41,7 @@ import "./LayerUI.scss";
|
|||||||
import { LibraryUnit } from "./LibraryUnit";
|
import { LibraryUnit } from "./LibraryUnit";
|
||||||
import { ToolButton } from "./ToolButton";
|
import { ToolButton } from "./ToolButton";
|
||||||
import { saveLibraryAsJSON, importLibraryFromJSON } from "../data/json";
|
import { saveLibraryAsJSON, importLibraryFromJSON } from "../data/json";
|
||||||
import { muteFSAbortError } from "../utils";
|
import { getDateTime, muteFSAbortError } from "../utils";
|
||||||
import { BackgroundPickerAndDarkModeToggle } from "./BackgroundPickerAndDarkModeToggle";
|
import { BackgroundPickerAndDarkModeToggle } from "./BackgroundPickerAndDarkModeToggle";
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
import { Library } from "../data/library";
|
import { Library } from "../data/library";
|
||||||
@@ -50,7 +51,6 @@ import {
|
|||||||
EVENT_LIBRARY,
|
EVENT_LIBRARY,
|
||||||
trackEvent,
|
trackEvent,
|
||||||
} from "../analytics";
|
} from "../analytics";
|
||||||
import { PasteChartDialog } from "./PasteChartDialog";
|
|
||||||
|
|
||||||
interface LayerUIProps {
|
interface LayerUIProps {
|
||||||
actionManager: ActionManager;
|
actionManager: ActionManager;
|
||||||
@@ -60,17 +60,16 @@ interface LayerUIProps {
|
|||||||
elements: readonly NonDeletedExcalidrawElement[];
|
elements: readonly NonDeletedExcalidrawElement[];
|
||||||
onCollabButtonClick?: () => void;
|
onCollabButtonClick?: () => void;
|
||||||
onLockToggle: () => void;
|
onLockToggle: () => void;
|
||||||
onInsertElements: (elements: readonly NonDeletedExcalidrawElement[]) => void;
|
onInsertShape: (elements: LibraryItem) => void;
|
||||||
zenModeEnabled: boolean;
|
zenModeEnabled: boolean;
|
||||||
toggleZenMode: () => void;
|
toggleZenMode: () => void;
|
||||||
langCode: Language["code"];
|
lng: string;
|
||||||
isCollaborating: boolean;
|
isCollaborating: boolean;
|
||||||
onExportToBackend?: (
|
onExportToBackend?: (
|
||||||
exportedElements: readonly NonDeletedExcalidrawElement[],
|
exportedElements: readonly NonDeletedExcalidrawElement[],
|
||||||
appState: AppState,
|
appState: AppState,
|
||||||
canvas: HTMLCanvasElement | null,
|
canvas: HTMLCanvasElement | null,
|
||||||
) => void;
|
) => void;
|
||||||
renderCustomFooter?: (isMobile: boolean) => JSX.Element;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const useOnClickOutside = (
|
const useOnClickOutside = (
|
||||||
@@ -124,42 +123,9 @@ const LibraryMenuItems = ({
|
|||||||
let addedPendingElements = false;
|
let addedPendingElements = false;
|
||||||
|
|
||||||
rows.push(
|
rows.push(
|
||||||
<div className="layer-ui__library-header">
|
<>
|
||||||
<ToolButton
|
|
||||||
key="import"
|
|
||||||
type="button"
|
|
||||||
title={t("buttons.load")}
|
|
||||||
aria-label={t("buttons.load")}
|
|
||||||
icon={load}
|
|
||||||
onClick={() => {
|
|
||||||
importLibraryFromJSON()
|
|
||||||
.then(() => {
|
|
||||||
// Maybe we should close and open the menu so that the items get updated.
|
|
||||||
// But for now we just close the menu.
|
|
||||||
setAppState({ isLibraryOpen: false });
|
|
||||||
})
|
|
||||||
.catch(muteFSAbortError)
|
|
||||||
.catch((error) => {
|
|
||||||
setAppState({ errorMessage: error.message });
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<ToolButton
|
|
||||||
key="export"
|
|
||||||
type="button"
|
|
||||||
title={t("buttons.export")}
|
|
||||||
aria-label={t("buttons.export")}
|
|
||||||
icon={exportFile}
|
|
||||||
onClick={() => {
|
|
||||||
saveLibraryAsJSON()
|
|
||||||
.catch(muteFSAbortError)
|
|
||||||
.catch((error) => {
|
|
||||||
setAppState({ errorMessage: error.message });
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<a
|
<a
|
||||||
|
className="browse-libraries"
|
||||||
href="https://libraries.excalidraw.com"
|
href="https://libraries.excalidraw.com"
|
||||||
target="_excalidraw_libraries"
|
target="_excalidraw_libraries"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
@@ -168,7 +134,48 @@ const LibraryMenuItems = ({
|
|||||||
>
|
>
|
||||||
{t("labels.libraries")}
|
{t("labels.libraries")}
|
||||||
</a>
|
</a>
|
||||||
</div>,
|
|
||||||
|
<Stack.Row
|
||||||
|
align="center"
|
||||||
|
gap={1}
|
||||||
|
key={"actions"}
|
||||||
|
style={{ padding: "2px" }}
|
||||||
|
>
|
||||||
|
<ToolButton
|
||||||
|
key="import"
|
||||||
|
type="button"
|
||||||
|
title={t("buttons.load")}
|
||||||
|
aria-label={t("buttons.load")}
|
||||||
|
icon={load}
|
||||||
|
onClick={() => {
|
||||||
|
importLibraryFromJSON()
|
||||||
|
.then(() => {
|
||||||
|
// Maybe we should close and open the menu so that the items get updated.
|
||||||
|
// But for now we just close the menu.
|
||||||
|
setAppState({ isLibraryOpen: false });
|
||||||
|
})
|
||||||
|
.catch(muteFSAbortError)
|
||||||
|
.catch((error) => {
|
||||||
|
setAppState({ errorMessage: error.message });
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<ToolButton
|
||||||
|
key="export"
|
||||||
|
type="button"
|
||||||
|
title={t("buttons.export")}
|
||||||
|
aria-label={t("buttons.export")}
|
||||||
|
icon={exportFile}
|
||||||
|
onClick={() => {
|
||||||
|
saveLibraryAsJSON()
|
||||||
|
.catch(muteFSAbortError)
|
||||||
|
.catch((error) => {
|
||||||
|
setAppState({ errorMessage: error.message });
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Stack.Row>
|
||||||
|
</>,
|
||||||
);
|
);
|
||||||
|
|
||||||
for (let row = 0; row < numRows; row++) {
|
for (let row = 0; row < numRows; row++) {
|
||||||
@@ -311,12 +318,11 @@ const LayerUI = ({
|
|||||||
elements,
|
elements,
|
||||||
onCollabButtonClick,
|
onCollabButtonClick,
|
||||||
onLockToggle,
|
onLockToggle,
|
||||||
onInsertElements,
|
onInsertShape,
|
||||||
zenModeEnabled,
|
zenModeEnabled,
|
||||||
toggleZenMode,
|
toggleZenMode,
|
||||||
isCollaborating,
|
isCollaborating,
|
||||||
onExportToBackend,
|
onExportToBackend,
|
||||||
renderCustomFooter,
|
|
||||||
}: LayerUIProps) => {
|
}: LayerUIProps) => {
|
||||||
const isMobile = useIsMobile();
|
const isMobile = useIsMobile();
|
||||||
|
|
||||||
@@ -346,7 +352,7 @@ const LayerUI = ({
|
|||||||
if (canvas) {
|
if (canvas) {
|
||||||
await exportCanvas(type, exportedElements, appState, canvas, {
|
await exportCanvas(type, exportedElements, appState, canvas, {
|
||||||
exportBackground: appState.exportBackground,
|
exportBackground: appState.exportBackground,
|
||||||
name: appState.name,
|
name: appState.name || `${t("labels.untitled")}-${getDateTime()}`,
|
||||||
viewBackgroundColor: appState.viewBackgroundColor,
|
viewBackgroundColor: appState.viewBackgroundColor,
|
||||||
scale,
|
scale,
|
||||||
shouldAddWatermark: appState.shouldAddWatermark,
|
shouldAddWatermark: appState.shouldAddWatermark,
|
||||||
@@ -450,7 +456,7 @@ const LayerUI = ({
|
|||||||
<LibraryMenu
|
<LibraryMenu
|
||||||
pendingElements={getSelectedElements(elements, appState)}
|
pendingElements={getSelectedElements(elements, appState)}
|
||||||
onClickOutside={closeLibrary}
|
onClickOutside={closeLibrary}
|
||||||
onInsertShape={onInsertElements}
|
onInsertShape={onInsertShape}
|
||||||
onAddToLibrary={deselectItems}
|
onAddToLibrary={deselectItems}
|
||||||
setAppState={setAppState}
|
setAppState={setAppState}
|
||||||
/>
|
/>
|
||||||
@@ -552,7 +558,14 @@ const LayerUI = ({
|
|||||||
"transition-right disable-pointerEvents": zenModeEnabled,
|
"transition-right disable-pointerEvents": zenModeEnabled,
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
{renderCustomFooter?.(false)}
|
<LanguageList
|
||||||
|
onChange={async (lng) => {
|
||||||
|
await setLanguage(lng);
|
||||||
|
setAppState({});
|
||||||
|
}}
|
||||||
|
languages={languages}
|
||||||
|
floating
|
||||||
|
/>
|
||||||
{actionManager.renderAction("toggleShortcuts")}
|
{actionManager.renderAction("toggleShortcuts")}
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
@@ -579,8 +592,21 @@ const LayerUI = ({
|
|||||||
</footer>
|
</footer>
|
||||||
);
|
);
|
||||||
|
|
||||||
const dialogs = (
|
return isMobile ? (
|
||||||
<>
|
<MobileMenu
|
||||||
|
appState={appState}
|
||||||
|
elements={elements}
|
||||||
|
actionManager={actionManager}
|
||||||
|
libraryMenu={libraryMenu}
|
||||||
|
exportButton={renderExportDialog()}
|
||||||
|
setAppState={setAppState}
|
||||||
|
onCollabButtonClick={onCollabButtonClick}
|
||||||
|
onLockToggle={onLockToggle}
|
||||||
|
canvas={canvas}
|
||||||
|
isCollaborating={isCollaborating}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<div className="layer-ui__wrapper">
|
||||||
{appState.isLoading && <LoadingMessage />}
|
{appState.isLoading && <LoadingMessage />}
|
||||||
{appState.errorMessage && (
|
{appState.errorMessage && (
|
||||||
<ErrorDialog
|
<ErrorDialog
|
||||||
@@ -593,41 +619,6 @@ const LayerUI = ({
|
|||||||
onClose={() => setAppState({ showShortcutsDialog: false })}
|
onClose={() => setAppState({ showShortcutsDialog: false })}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{appState.pasteDialog.shown && (
|
|
||||||
<PasteChartDialog
|
|
||||||
setAppState={setAppState}
|
|
||||||
appState={appState}
|
|
||||||
onInsertChart={onInsertElements}
|
|
||||||
onClose={() =>
|
|
||||||
setAppState({
|
|
||||||
pasteDialog: { shown: false, data: null },
|
|
||||||
})
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
|
|
||||||
return isMobile ? (
|
|
||||||
<>
|
|
||||||
{dialogs}
|
|
||||||
<MobileMenu
|
|
||||||
appState={appState}
|
|
||||||
elements={elements}
|
|
||||||
actionManager={actionManager}
|
|
||||||
libraryMenu={libraryMenu}
|
|
||||||
exportButton={renderExportDialog()}
|
|
||||||
setAppState={setAppState}
|
|
||||||
onCollabButtonClick={onCollabButtonClick}
|
|
||||||
onLockToggle={onLockToggle}
|
|
||||||
canvas={canvas}
|
|
||||||
isCollaborating={isCollaborating}
|
|
||||||
renderCustomFooter={renderCustomFooter}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<div className="layer-ui__wrapper">
|
|
||||||
{dialogs}
|
|
||||||
{renderFixedSideContainer()}
|
{renderFixedSideContainer()}
|
||||||
{renderBottomAppMenu()}
|
{renderBottomAppMenu()}
|
||||||
{
|
{
|
||||||
@@ -660,8 +651,9 @@ const areEqual = (prev: LayerUIProps, next: LayerUIProps) => {
|
|||||||
const nextAppState = getNecessaryObj(next.appState);
|
const nextAppState = getNecessaryObj(next.appState);
|
||||||
|
|
||||||
const keys = Object.keys(prevAppState) as (keyof Partial<AppState>)[];
|
const keys = Object.keys(prevAppState) as (keyof Partial<AppState>)[];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
prev.langCode === next.langCode &&
|
prev.lng === next.lng &&
|
||||||
prev.elements === next.elements &&
|
prev.elements === next.elements &&
|
||||||
keys.every((key) => prevAppState[key] === nextAppState[key])
|
keys.every((key) => prevAppState[key] === nextAppState[key])
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
|
import React, { useRef, useEffect, useState } from "react";
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
import oc from "open-color";
|
import { exportToSvg } from "../scene/export";
|
||||||
import React, { useEffect, useRef, useState } from "react";
|
|
||||||
import { close } from "../components/icons";
|
import { close } from "../components/icons";
|
||||||
import { MIME_TYPES } from "../constants";
|
|
||||||
|
import "./LibraryUnit.scss";
|
||||||
import { t } from "../i18n";
|
import { t } from "../i18n";
|
||||||
import useIsMobile from "../is-mobile";
|
import useIsMobile from "../is-mobile";
|
||||||
import { exportToSvg } from "../scene/export";
|
|
||||||
import { LibraryItem } from "../types";
|
import { LibraryItem } from "../types";
|
||||||
import "./LibraryUnit.scss";
|
import { MIME_TYPES } from "../constants";
|
||||||
|
|
||||||
// fa-plus
|
// fa-plus
|
||||||
const PLUS_ICON = (
|
const PLUS_ICON = (
|
||||||
@@ -38,7 +38,7 @@ export const LibraryUnit = ({
|
|||||||
}
|
}
|
||||||
const svg = exportToSvg(elementsToRender, {
|
const svg = exportToSvg(elementsToRender, {
|
||||||
exportBackground: false,
|
exportBackground: false,
|
||||||
viewBackgroundColor: oc.white,
|
viewBackgroundColor: "#fff",
|
||||||
shouldAddWatermark: false,
|
shouldAddWatermark: false,
|
||||||
});
|
});
|
||||||
for (const child of ref.current!.children) {
|
for (const child of ref.current!.children) {
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { AppState } from "../types";
|
import { AppState } from "../types";
|
||||||
import { ActionManager } from "../actions/manager";
|
import { ActionManager } from "../actions/manager";
|
||||||
import { t } from "../i18n";
|
import { t, setLanguage } from "../i18n";
|
||||||
import Stack from "./Stack";
|
import Stack from "./Stack";
|
||||||
|
import { LanguageList } from "./LanguageList";
|
||||||
import { showSelectedShapeActions } from "../element";
|
import { showSelectedShapeActions } from "../element";
|
||||||
import { NonDeletedExcalidrawElement } from "../element/types";
|
import { NonDeletedExcalidrawElement } from "../element/types";
|
||||||
import { FixedSideContainer } from "./FixedSideContainer";
|
import { FixedSideContainer } from "./FixedSideContainer";
|
||||||
@@ -14,6 +15,7 @@ import { Section } from "./Section";
|
|||||||
import CollabButton from "./CollabButton";
|
import CollabButton from "./CollabButton";
|
||||||
import { SCROLLBAR_WIDTH, SCROLLBAR_MARGIN } from "../scene/scrollbars";
|
import { SCROLLBAR_WIDTH, SCROLLBAR_MARGIN } from "../scene/scrollbars";
|
||||||
import { LockIcon } from "./LockIcon";
|
import { LockIcon } from "./LockIcon";
|
||||||
|
import { LoadingMessage } from "./LoadingMessage";
|
||||||
import { UserList } from "./UserList";
|
import { UserList } from "./UserList";
|
||||||
import { BackgroundPickerAndDarkModeToggle } from "./BackgroundPickerAndDarkModeToggle";
|
import { BackgroundPickerAndDarkModeToggle } from "./BackgroundPickerAndDarkModeToggle";
|
||||||
import { EVENT_ACTION, trackEvent } from "../analytics";
|
import { EVENT_ACTION, trackEvent } from "../analytics";
|
||||||
@@ -29,7 +31,6 @@ type MobileMenuProps = {
|
|||||||
onLockToggle: () => void;
|
onLockToggle: () => void;
|
||||||
canvas: HTMLCanvasElement | null;
|
canvas: HTMLCanvasElement | null;
|
||||||
isCollaborating: boolean;
|
isCollaborating: boolean;
|
||||||
renderCustomFooter?: (isMobile: boolean) => JSX.Element;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const MobileMenu = ({
|
export const MobileMenu = ({
|
||||||
@@ -43,9 +44,9 @@ export const MobileMenu = ({
|
|||||||
onLockToggle,
|
onLockToggle,
|
||||||
canvas,
|
canvas,
|
||||||
isCollaborating,
|
isCollaborating,
|
||||||
renderCustomFooter,
|
|
||||||
}: MobileMenuProps) => (
|
}: MobileMenuProps) => (
|
||||||
<>
|
<>
|
||||||
|
{appState.isLoading && <LoadingMessage />}
|
||||||
<FixedSideContainer side="top">
|
<FixedSideContainer side="top">
|
||||||
<Section heading="shapes">
|
<Section heading="shapes">
|
||||||
{(heading) => (
|
{(heading) => (
|
||||||
@@ -103,7 +104,15 @@ export const MobileMenu = ({
|
|||||||
appState={appState}
|
appState={appState}
|
||||||
setAppState={setAppState}
|
setAppState={setAppState}
|
||||||
/>
|
/>
|
||||||
{renderCustomFooter?.(true)}
|
<fieldset>
|
||||||
|
<legend>{t("labels.language")}</legend>
|
||||||
|
<LanguageList
|
||||||
|
onChange={async (lng) => {
|
||||||
|
await setLanguage(lng);
|
||||||
|
setAppState({});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</fieldset>
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>{t("labels.collaborators")}</legend>
|
<legend>{t("labels.collaborators")}</legend>
|
||||||
<UserList mobile>
|
<UserList mobile>
|
||||||
|
|||||||
@@ -30,26 +30,18 @@
|
|||||||
z-index: 2;
|
z-index: 2;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: var(--max-width);
|
max-width: var(--max-width);
|
||||||
max-height: 100%;
|
|
||||||
|
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transform: translateY(10px);
|
transform: translateY(10px);
|
||||||
animation: Modal__content_fade-in 0.1s ease-out 0.05s forwards;
|
animation: Modal__content_fade-in 0.1s ease-out 0.05s forwards;
|
||||||
position: relative;
|
position: relative;
|
||||||
overflow-y: auto;
|
|
||||||
|
|
||||||
// for modals, reset blurry bg
|
// for modals, reset blurry bg
|
||||||
background: var(--bg-color-island);
|
background: var(--bg-color-island);
|
||||||
backdrop-filter: none;
|
backdrop-filter: none;
|
||||||
|
|
||||||
border: 1px solid var(--dialog-border);
|
@media #{$media-query} {
|
||||||
box-shadow: 0 2px 10px transparentize($oc-black, 0.75);
|
|
||||||
border-radius: 6px;
|
|
||||||
|
|
||||||
@media #{$is-mobile-query} {
|
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
border: 0;
|
|
||||||
border-radius: 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,7 +68,13 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media #{$is-mobile-query} {
|
.Modal__close--floating {
|
||||||
|
position: absolute;
|
||||||
|
right: calc(var(--space-factor) * 5);
|
||||||
|
top: calc(var(--space-factor) * 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media #{$media-query} {
|
||||||
.Modal {
|
.Modal {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,7 +36,11 @@ export const Modal = (props: {
|
|||||||
<div className="Modal__background" onClick={props.onCloseRequest}></div>
|
<div className="Modal__background" onClick={props.onCloseRequest}></div>
|
||||||
<div
|
<div
|
||||||
className="Modal__content"
|
className="Modal__content"
|
||||||
style={{ "--max-width": `${props.maxWidth}px` }}
|
style={{
|
||||||
|
"--max-width": `${props.maxWidth}px`,
|
||||||
|
maxHeight: "100%",
|
||||||
|
overflowY: "scroll",
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{props.children}
|
{props.children}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,46 +0,0 @@
|
|||||||
@import "../css/_variables";
|
|
||||||
|
|
||||||
.excalidraw {
|
|
||||||
.PasteChartDialog {
|
|
||||||
@media #{$is-mobile-query} {
|
|
||||||
.Island {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.container {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-around;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
@media #{$is-mobile-query} {
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.ChartPreview {
|
|
||||||
margin: 8px;
|
|
||||||
text-align: center;
|
|
||||||
width: 192px;
|
|
||||||
height: 128px;
|
|
||||||
border-radius: 2px;
|
|
||||||
padding: 1px;
|
|
||||||
border: 1px solid $oc-gray-4;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
background: transparent;
|
|
||||||
div {
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
svg {
|
|
||||||
max-height: 120px;
|
|
||||||
max-width: 186px;
|
|
||||||
}
|
|
||||||
&:hover {
|
|
||||||
padding: 0;
|
|
||||||
border: 2px solid $oc-blue-5;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,122 +0,0 @@
|
|||||||
import oc from "open-color";
|
|
||||||
import React, { useLayoutEffect, useRef, useState } from "react";
|
|
||||||
import { ChartElements, renderSpreadsheet, Spreadsheet } from "../charts";
|
|
||||||
import { ChartType } from "../element/types";
|
|
||||||
import { t } from "../i18n";
|
|
||||||
import { exportToSvg } from "../scene/export";
|
|
||||||
import { AppState, LibraryItem } from "../types";
|
|
||||||
import { Dialog } from "./Dialog";
|
|
||||||
import "./PasteChartDialog.scss";
|
|
||||||
|
|
||||||
type OnInsertChart = (chartType: ChartType, elements: ChartElements) => void;
|
|
||||||
|
|
||||||
const ChartPreviewBtn = (props: {
|
|
||||||
spreadsheet: Spreadsheet | null;
|
|
||||||
chartType: ChartType;
|
|
||||||
selected: boolean;
|
|
||||||
onClick: OnInsertChart;
|
|
||||||
}) => {
|
|
||||||
const previewRef = useRef<HTMLDivElement | null>(null);
|
|
||||||
const [chartElements, setChartElements] = useState<ChartElements | null>(
|
|
||||||
null,
|
|
||||||
);
|
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
|
||||||
if (!props.spreadsheet) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const elements = renderSpreadsheet(
|
|
||||||
props.chartType,
|
|
||||||
props.spreadsheet,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
);
|
|
||||||
setChartElements(elements);
|
|
||||||
|
|
||||||
const svg = exportToSvg(elements, {
|
|
||||||
exportBackground: false,
|
|
||||||
viewBackgroundColor: oc.white,
|
|
||||||
shouldAddWatermark: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
const previewNode = previewRef.current!;
|
|
||||||
|
|
||||||
previewNode.appendChild(svg);
|
|
||||||
|
|
||||||
if (props.selected) {
|
|
||||||
(previewNode.parentNode as HTMLDivElement).focus();
|
|
||||||
}
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
previewNode.removeChild(svg);
|
|
||||||
};
|
|
||||||
}, [props.spreadsheet, props.chartType, props.selected]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<button
|
|
||||||
className="ChartPreview"
|
|
||||||
onClick={() => {
|
|
||||||
if (chartElements) {
|
|
||||||
props.onClick(props.chartType, chartElements);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div ref={previewRef} />
|
|
||||||
</button>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const PasteChartDialog = ({
|
|
||||||
setAppState,
|
|
||||||
appState,
|
|
||||||
onClose,
|
|
||||||
onInsertChart,
|
|
||||||
}: {
|
|
||||||
appState: AppState;
|
|
||||||
onClose: () => void;
|
|
||||||
setAppState: React.Component<any, AppState>["setState"];
|
|
||||||
onInsertChart: (elements: LibraryItem) => void;
|
|
||||||
}) => {
|
|
||||||
const handleClose = React.useCallback(() => {
|
|
||||||
if (onClose) {
|
|
||||||
onClose();
|
|
||||||
}
|
|
||||||
}, [onClose]);
|
|
||||||
|
|
||||||
const handleChartClick = (chartType: ChartType, elements: ChartElements) => {
|
|
||||||
onInsertChart(elements);
|
|
||||||
setAppState({
|
|
||||||
currentChartType: chartType,
|
|
||||||
pasteDialog: {
|
|
||||||
shown: false,
|
|
||||||
data: null,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Dialog
|
|
||||||
small
|
|
||||||
onCloseRequest={handleClose}
|
|
||||||
title={t("labels.pasteCharts")}
|
|
||||||
className={"PasteChartDialog"}
|
|
||||||
autofocus={false}
|
|
||||||
>
|
|
||||||
<div className={"container"}>
|
|
||||||
<ChartPreviewBtn
|
|
||||||
chartType="bar"
|
|
||||||
spreadsheet={appState.pasteDialog.data}
|
|
||||||
selected={appState.currentChartType === "bar"}
|
|
||||||
onClick={handleChartClick}
|
|
||||||
/>
|
|
||||||
<ChartPreviewBtn
|
|
||||||
chartType="line"
|
|
||||||
spreadsheet={appState.pasteDialog.data}
|
|
||||||
selected={appState.currentChartType === "line"}
|
|
||||||
onClick={handleChartClick}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</Dialog>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -130,7 +130,11 @@ export const ShortcutsDialog = ({ onClose }: { onClose?: () => void }) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Dialog onCloseRequest={handleClose} title={t("shortcutsDialog.title")}>
|
<Dialog
|
||||||
|
maxWidth={900}
|
||||||
|
onCloseRequest={handleClose}
|
||||||
|
title={t("shortcutsDialog.title")}
|
||||||
|
>
|
||||||
<Columns>
|
<Columns>
|
||||||
<Column>
|
<Column>
|
||||||
<ShortcutIsland caption={t("shortcutsDialog.shapes")}>
|
<ShortcutIsland caption={t("shortcutsDialog.shapes")}>
|
||||||
|
|||||||
@@ -85,6 +85,7 @@ export const Stats = (props: {
|
|||||||
<td>{t("stats.total")}</td>
|
<td>{t("stats.total")}</td>
|
||||||
<td>{nFormatter(storageSizes.total, 1)}</td>
|
<td>{nFormatter(storageSizes.total, 1)}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
{selectedElements.length === 1 && (
|
{selectedElements.length === 1 && (
|
||||||
<tr>
|
<tr>
|
||||||
<th colSpan={2}>{t("stats.element")}</th>
|
<th colSpan={2}>{t("stats.element")}</th>
|
||||||
|
|||||||
@@ -142,7 +142,6 @@
|
|||||||
user-select: none;
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
// shrink shape icons on small viewports to make them fit
|
|
||||||
@media (max-width: 425px) {
|
@media (max-width: 425px) {
|
||||||
.Shape .ToolIcon__icon {
|
.Shape .ToolIcon__icon {
|
||||||
width: 2rem;
|
width: 2rem;
|
||||||
@@ -154,8 +153,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// move the lock button out of the way on small viewports
|
|
||||||
// it begins to collide with the GitHub icon before we switch to mobile mode
|
|
||||||
@media (max-width: 760px) {
|
@media (max-width: 760px) {
|
||||||
.ToolIcon.ToolIcon__lock {
|
.ToolIcon.ToolIcon__lock {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
@@ -165,7 +162,6 @@
|
|||||||
|
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
border-radius: 20px 0 0 20px;
|
border-radius: 20px 0 0 20px;
|
||||||
z-index: 1;
|
|
||||||
|
|
||||||
background-color: var(--button-gray-1);
|
background-color: var(--button-gray-1);
|
||||||
|
|
||||||
@@ -193,7 +189,7 @@
|
|||||||
margin-left: 5px;
|
margin-left: 5px;
|
||||||
margin-top: 1px;
|
margin-top: 1px;
|
||||||
|
|
||||||
@media #{$is-mobile-query} {
|
@media #{$media-query} {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -88,5 +88,3 @@ export const STORAGE_KEYS = {
|
|||||||
export const TAP_TWICE_TIMEOUT = 300;
|
export const TAP_TWICE_TIMEOUT = 300;
|
||||||
export const TOUCH_CTX_MENU_TIMEOUT = 500;
|
export const TOUCH_CTX_MENU_TIMEOUT = 500;
|
||||||
export const TITLE_TIMEOUT = 10000;
|
export const TITLE_TIMEOUT = 10000;
|
||||||
|
|
||||||
export const SCENE_NAME_FALLBACK = "Untitled";
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
@import "open-color/open-color.scss";
|
@import "open-color/open-color.scss";
|
||||||
|
|
||||||
// keep up to date with is-mobile.tsx
|
$media-query: "(max-width: 600px), (max-height: 500px) and (max-width: 1000px)";
|
||||||
$is-mobile-query: "(max-width: 600px), (max-height: 500px) and (max-width: 1000px)";
|
|
||||||
|
|||||||
+1
-1
@@ -441,7 +441,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media #{$is-mobile-query} {
|
@media #{$media-query} {
|
||||||
aside {
|
aside {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-2
@@ -3,6 +3,7 @@
|
|||||||
:root {
|
:root {
|
||||||
--bg-color-island: rgba(255, 255, 255, 0.9);
|
--bg-color-island: rgba(255, 255, 255, 0.9);
|
||||||
--popup-background-color: #{$oc-white};
|
--popup-background-color: #{$oc-white};
|
||||||
|
--border-radius-m: 4px;
|
||||||
--space-factor: 0.25rem;
|
--space-factor: 0.25rem;
|
||||||
--button-gray-1: #{$oc-gray-2};
|
--button-gray-1: #{$oc-gray-2};
|
||||||
--button-gray-2: #{$oc-gray-4};
|
--button-gray-2: #{$oc-gray-4};
|
||||||
@@ -14,6 +15,7 @@
|
|||||||
--icon-fill-color: #{$oc-black};
|
--icon-fill-color: #{$oc-black};
|
||||||
--icon-green-fill-color: #{$oc-green-9};
|
--icon-green-fill-color: #{$oc-green-9};
|
||||||
--keybinding-color: #{$oc-gray-5};
|
--keybinding-color: #{$oc-gray-5};
|
||||||
|
--color-overlay-text-color: #ccc;
|
||||||
--sat: env(safe-area-inset-top);
|
--sat: env(safe-area-inset-top);
|
||||||
--sab: env(safe-area-inset-bottom);
|
--sab: env(safe-area-inset-bottom);
|
||||||
--sal: env(safe-area-inset-left);
|
--sal: env(safe-area-inset-left);
|
||||||
@@ -21,6 +23,8 @@
|
|||||||
--text-color-primary: #{$oc-gray-8};
|
--text-color-primary: #{$oc-gray-8};
|
||||||
--shadow-island: 0 1px 5px #{transparentize($oc-black, 0.85)};
|
--shadow-island: 0 1px 5px #{transparentize($oc-black, 0.85)};
|
||||||
--overlay-background-color: #{transparentize($oc-white, 0.12)};
|
--overlay-background-color: #{transparentize($oc-white, 0.12)};
|
||||||
|
--border-radius-m: 4px;
|
||||||
|
--space-factor: 0.25rem;
|
||||||
--dropdown-icon: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="292.4" height="292.4" viewBox="0 0 292 292"><path d="M287 197L159 69c-4-3-8-5-13-5s-9 2-13 5L5 197c-3 4-5 8-5 13s2 9 5 13c4 4 8 5 13 5h256c5 0 9-1 13-5s5-8 5-13-1-9-5-13z"/></svg>');
|
--dropdown-icon: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="292.4" height="292.4" viewBox="0 0 292 292"><path d="M287 197L159 69c-4-3-8-5-13-5s-9 2-13 5L5 197c-3 4-5 8-5 13s2 9 5 13c4 4 8 5 13 5h256c5 0 9-1 13-5s5-8 5-13-1-9-5-13z"/></svg>');
|
||||||
--focus-highlight-color: #{$oc-blue-2};
|
--focus-highlight-color: #{$oc-blue-2};
|
||||||
--select-highlight-color: #{$oc-blue-5};
|
--select-highlight-color: #{$oc-blue-5};
|
||||||
@@ -31,7 +35,6 @@
|
|||||||
--popup-secondary-background-color: #{$oc-gray-1};
|
--popup-secondary-background-color: #{$oc-gray-1};
|
||||||
--popup-text-color: #{$oc-black};
|
--popup-text-color: #{$oc-black};
|
||||||
--popup-text-inverted-color: #{$oc-white};
|
--popup-text-inverted-color: #{$oc-white};
|
||||||
--dialog-border: #{$oc-gray-6};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.excalidraw {
|
.excalidraw {
|
||||||
@@ -57,8 +60,10 @@
|
|||||||
--icon-fill-color: #{$oc-gray-4};
|
--icon-fill-color: #{$oc-gray-4};
|
||||||
--icon-green-fill-color: #{$oc-green-4};
|
--icon-green-fill-color: #{$oc-green-4};
|
||||||
--keybinding-color: #{$oc-gray-6};
|
--keybinding-color: #{$oc-gray-6};
|
||||||
|
--color-overlay-text-color: #bbb;
|
||||||
--shadow-island: 0 1px 5px #{transparentize($oc-black, 0.7)};
|
--shadow-island: 0 1px 5px #{transparentize($oc-black, 0.7)};
|
||||||
--overlay-background-color: rgba(30, 30, 30, 0.88);
|
--overlay-background-color: rgba(30, 30, 30, 0.88);
|
||||||
|
// #{$oc-gray-4}; inlined
|
||||||
--dropdown-icon: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="292.4" height="292.4" viewBox="0 0 292 292"><path fill="%23ced4da" d="M287 197L159 69c-4-3-8-5-13-5s-9 2-13 5L5 197c-3 4-5 8-5 13s2 9 5 13c4 4 8 5 13 5h256c5 0 9-1 13-5s5-8 5-13-1-9-5-13z"/></svg>');
|
--dropdown-icon: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="292.4" height="292.4" viewBox="0 0 292 292"><path fill="%23ced4da" d="M287 197L159 69c-4-3-8-5-13-5s-9 2-13 5L5 197c-3 4-5 8-5 13s2 9 5 13c4 4 8 5 13 5h256c5 0 9-1 13-5s5-8 5-13-1-9-5-13z"/></svg>');
|
||||||
--focus-highlight-color: #{$oc-blue-6};
|
--focus-highlight-color: #{$oc-blue-6};
|
||||||
--select-highlight-color: #{$oc-blue-4};
|
--select-highlight-color: #{$oc-blue-4};
|
||||||
@@ -69,6 +74,5 @@
|
|||||||
--popup-secondary-background-color: #222;
|
--popup-secondary-background-color: #222;
|
||||||
--popup-text-color: #{$oc-gray-4};
|
--popup-text-color: #{$oc-gray-4};
|
||||||
--popup-text-inverted-color: #2c2c2c;
|
--popup-text-inverted-color: #2c2c2c;
|
||||||
--dialog-border: #{$oc-gray-9};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -75,6 +75,7 @@ export const exportCanvas = async (
|
|||||||
exportPadding,
|
exportPadding,
|
||||||
scale,
|
scale,
|
||||||
shouldAddWatermark,
|
shouldAddWatermark,
|
||||||
|
watermarkText: t("labels.madeWithExcalidraw"),
|
||||||
});
|
});
|
||||||
tempCanvas.style.display = "none";
|
tempCanvas.style.display = "none";
|
||||||
document.body.appendChild(tempCanvas);
|
document.body.appendChild(tempCanvas);
|
||||||
|
|||||||
+3
-2
@@ -8,6 +8,8 @@ import { Library } from "./library";
|
|||||||
import { MIME_TYPES } from "../constants";
|
import { MIME_TYPES } from "../constants";
|
||||||
import { clearElementsForExport } from "../element";
|
import { clearElementsForExport } from "../element";
|
||||||
import { EVENT_LIBRARY, trackEvent } from "../analytics";
|
import { EVENT_LIBRARY, trackEvent } from "../analytics";
|
||||||
|
import { t } from "../i18n";
|
||||||
|
import { getDateTime } from "../utils";
|
||||||
|
|
||||||
export const serializeAsJSON = (
|
export const serializeAsJSON = (
|
||||||
elements: readonly ExcalidrawElement[],
|
elements: readonly ExcalidrawElement[],
|
||||||
@@ -33,11 +35,10 @@ export const saveAsJSON = async (
|
|||||||
const blob = new Blob([serialized], {
|
const blob = new Blob([serialized], {
|
||||||
type: "application/json",
|
type: "application/json",
|
||||||
});
|
});
|
||||||
|
|
||||||
const fileHandle = await fileSave(
|
const fileHandle = await fileSave(
|
||||||
blob,
|
blob,
|
||||||
{
|
{
|
||||||
fileName: appState.name,
|
fileName: appState.name || `${t("labels.untitled")}-${getDateTime()}`,
|
||||||
description: "Excalidraw file",
|
description: "Excalidraw file",
|
||||||
extensions: [".excalidraw"],
|
extensions: [".excalidraw"],
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ import {
|
|||||||
DEFAULT_VERTICAL_ALIGN,
|
DEFAULT_VERTICAL_ALIGN,
|
||||||
} from "../constants";
|
} from "../constants";
|
||||||
import { getDefaultAppState } from "../appState";
|
import { getDefaultAppState } from "../appState";
|
||||||
import { getNewSceneName } from "../utils";
|
|
||||||
|
|
||||||
const getFontFamilyByName = (fontFamilyName: string): FontFamily => {
|
const getFontFamilyByName = (fontFamilyName: string): FontFamily => {
|
||||||
for (const [id, fontFamilyString] of Object.entries(FONT_FAMILY)) {
|
for (const [id, fontFamilyString] of Object.entries(FONT_FAMILY)) {
|
||||||
@@ -167,7 +166,6 @@ const restoreAppState = (
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
...nextAppState,
|
...nextAppState,
|
||||||
name: appState.name ?? localAppState?.name ?? getNewSceneName(),
|
|
||||||
offsetLeft: appState.offsetLeft || 0,
|
offsetLeft: appState.offsetLeft || 0,
|
||||||
offsetTop: appState.offsetTop || 0,
|
offsetTop: appState.offsetTop || 0,
|
||||||
// Migrates from previous version where appState.zoom was a number
|
// Migrates from previous version where appState.zoom was a number
|
||||||
|
|||||||
@@ -49,7 +49,6 @@ export {
|
|||||||
dragNewElement,
|
dragNewElement,
|
||||||
} from "./dragElements";
|
} from "./dragElements";
|
||||||
export { isTextElement, isExcalidrawElement } from "./typeChecks";
|
export { isTextElement, isExcalidrawElement } from "./typeChecks";
|
||||||
export { textWysiwyg } from "./textWysiwyg";
|
|
||||||
export { redrawTextBoundingBox } from "./textElement";
|
export { redrawTextBoundingBox } from "./textElement";
|
||||||
export {
|
export {
|
||||||
getPerfectElementSize,
|
getPerfectElementSize,
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { Point } from "../types";
|
import { Point } from "../types";
|
||||||
import { FONT_FAMILY } from "../constants";
|
import { FONT_FAMILY } from "../constants";
|
||||||
|
|
||||||
export type ChartType = "bar" | "line";
|
|
||||||
export type FillStyle = "hachure" | "cross-hatch" | "solid";
|
export type FillStyle = "hachure" | "cross-hatch" | "solid";
|
||||||
export type FontFamily = keyof typeof FONT_FAMILY;
|
export type FontFamily = keyof typeof FONT_FAMILY;
|
||||||
export type FontString = string & { _brand: "fontString" };
|
export type FontString = string & { _brand: "fontString" };
|
||||||
@@ -27,21 +26,11 @@ type _ExcalidrawElementBase = Readonly<{
|
|||||||
width: number;
|
width: number;
|
||||||
height: number;
|
height: number;
|
||||||
angle: number;
|
angle: number;
|
||||||
/** Random integer used to seed shape generation so that the roughjs shape
|
|
||||||
doesn't differ across renders. */
|
|
||||||
seed: number;
|
seed: number;
|
||||||
/** Integer that is sequentially incremented on each change. Used to reconcile
|
|
||||||
elements during collaboration or when saving to server. */
|
|
||||||
version: number;
|
version: number;
|
||||||
/** Random integer that is regenerated on each change.
|
|
||||||
Used for deterministic reconciliation of updates during collaboration,
|
|
||||||
in case the versions (see above) are identical. */
|
|
||||||
versionNonce: number;
|
versionNonce: number;
|
||||||
isDeleted: boolean;
|
isDeleted: boolean;
|
||||||
/** List of groups the element belongs to.
|
|
||||||
Ordered from deepest to shallowest. */
|
|
||||||
groupIds: readonly GroupId[];
|
groupIds: readonly GroupId[];
|
||||||
/** Ids of (linear) elements that are bound to this element. */
|
|
||||||
boundElementIds: readonly ExcalidrawLinearElement["id"][] | null;
|
boundElementIds: readonly ExcalidrawLinearElement["id"][] | null;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
|
|||||||
@@ -123,7 +123,11 @@ const RoomDialog = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<Dialog small onCloseRequest={handleClose} title={t("labels.createRoom")}>
|
<Dialog
|
||||||
|
maxWidth={800}
|
||||||
|
onCloseRequest={handleClose}
|
||||||
|
title={t("labels.createRoom")}
|
||||||
|
>
|
||||||
{renderRoomDialog()}
|
{renderRoomDialog()}
|
||||||
</Dialog>
|
</Dialog>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import {
|
|||||||
} from "../../appState";
|
} from "../../appState";
|
||||||
import { clearElementsForLocalStorage } from "../../element";
|
import { clearElementsForLocalStorage } from "../../element";
|
||||||
import { STORAGE_KEYS as APP_STORAGE_KEYS } from "../../constants";
|
import { STORAGE_KEYS as APP_STORAGE_KEYS } from "../../constants";
|
||||||
import { ImportedDataState } from "../../data/types";
|
|
||||||
|
|
||||||
export const STORAGE_KEYS = {
|
export const STORAGE_KEYS = {
|
||||||
LOCAL_STORAGE_ELEMENTS: "excalidraw",
|
LOCAL_STORAGE_ELEMENTS: "excalidraw",
|
||||||
@@ -82,7 +81,7 @@ export const importFromLocalStorage = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let appState: ImportedDataState["appState"] = null;
|
let appState = null;
|
||||||
if (savedState) {
|
if (savedState) {
|
||||||
try {
|
try {
|
||||||
appState = {
|
appState = {
|
||||||
|
|||||||
@@ -1,16 +1,6 @@
|
|||||||
import React, {
|
import React, { useState, useLayoutEffect, useEffect, useRef } from "react";
|
||||||
useState,
|
|
||||||
useLayoutEffect,
|
|
||||||
useEffect,
|
|
||||||
useRef,
|
|
||||||
useCallback,
|
|
||||||
} from "react";
|
|
||||||
import LanguageDetector from "i18next-browser-languagedetector";
|
|
||||||
|
|
||||||
import Excalidraw, {
|
import Excalidraw from "../packages/excalidraw/index";
|
||||||
languages,
|
|
||||||
defaultLang,
|
|
||||||
} from "../packages/excalidraw/index";
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
getTotalStorageSize,
|
getTotalStorageSize,
|
||||||
@@ -22,7 +12,7 @@ import {
|
|||||||
import { ImportedDataState } from "../data/types";
|
import { ImportedDataState } from "../data/types";
|
||||||
import CollabWrapper, { CollabAPI } from "./collab/CollabWrapper";
|
import CollabWrapper, { CollabAPI } from "./collab/CollabWrapper";
|
||||||
import { TopErrorBoundary } from "../components/TopErrorBoundary";
|
import { TopErrorBoundary } from "../components/TopErrorBoundary";
|
||||||
import { Language, t } from "../i18n";
|
import { t } from "../i18n";
|
||||||
import { exportToBackend, loadScene } from "./data";
|
import { exportToBackend, loadScene } from "./data";
|
||||||
import { getCollaborationLinkData } from "./data";
|
import { getCollaborationLinkData } from "./data";
|
||||||
import { EVENT } from "../constants";
|
import { EVENT } from "../constants";
|
||||||
@@ -39,16 +29,6 @@ import { EVENT_LOAD, EVENT_SHARE, trackEvent } from "../analytics";
|
|||||||
import { ErrorDialog } from "../components/ErrorDialog";
|
import { ErrorDialog } from "../components/ErrorDialog";
|
||||||
import { getDefaultAppState } from "../appState";
|
import { getDefaultAppState } from "../appState";
|
||||||
import { APP_NAME, TITLE_TIMEOUT } from "../constants";
|
import { APP_NAME, TITLE_TIMEOUT } from "../constants";
|
||||||
import { LanguageList } from "./components/LanguageList";
|
|
||||||
|
|
||||||
const languageDetector = new LanguageDetector();
|
|
||||||
languageDetector.init({
|
|
||||||
languageUtils: {
|
|
||||||
formatLanguageCode: (langCode: Language["code"]) => langCode,
|
|
||||||
isWhitelisted: () => true,
|
|
||||||
},
|
|
||||||
checkWhitelist: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
const excalidrawRef: React.MutableRefObject<
|
const excalidrawRef: React.MutableRefObject<
|
||||||
MarkRequired<ExcalidrawAPIRefValue, "ready" | "readyPromise">
|
MarkRequired<ExcalidrawAPIRefValue, "ready" | "readyPromise">
|
||||||
@@ -113,6 +93,7 @@ type Scene = ImportedDataState & { commitToHistory: boolean };
|
|||||||
const initializeScene = async (opts: {
|
const initializeScene = async (opts: {
|
||||||
resetScene: ExcalidrawImperativeAPI["resetScene"];
|
resetScene: ExcalidrawImperativeAPI["resetScene"];
|
||||||
initializeSocketClient: CollabAPI["initializeSocketClient"];
|
initializeSocketClient: CollabAPI["initializeSocketClient"];
|
||||||
|
onLateInitialization?: (scene: Scene) => void;
|
||||||
}): Promise<Scene | null> => {
|
}): Promise<Scene | null> => {
|
||||||
const searchParams = new URLSearchParams(window.location.search);
|
const searchParams = new URLSearchParams(window.location.search);
|
||||||
const id = searchParams.get("id");
|
const id = searchParams.get("id");
|
||||||
@@ -143,15 +124,17 @@ const initializeScene = async (opts: {
|
|||||||
} else {
|
} else {
|
||||||
// https://github.com/excalidraw/excalidraw/issues/1919
|
// https://github.com/excalidraw/excalidraw/issues/1919
|
||||||
if (document.hidden) {
|
if (document.hidden) {
|
||||||
return new Promise((resolve, reject) => {
|
window.addEventListener(
|
||||||
window.addEventListener(
|
"focus",
|
||||||
"focus",
|
() =>
|
||||||
() => initializeScene(opts).then(resolve).catch(reject),
|
initializeScene(opts).then((_scene) => {
|
||||||
{
|
opts?.onLateInitialization?.(_scene || scene);
|
||||||
once: true,
|
}),
|
||||||
},
|
{
|
||||||
);
|
once: true,
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
isCollabScene = false;
|
isCollabScene = false;
|
||||||
@@ -202,8 +185,6 @@ function ExcalidrawWrapper(props: { collab: CollabAPI }) {
|
|||||||
height: window.innerHeight,
|
height: window.innerHeight,
|
||||||
});
|
});
|
||||||
const [errorMessage, setErrorMessage] = useState("");
|
const [errorMessage, setErrorMessage] = useState("");
|
||||||
const currentLangCode = languageDetector.detect() || defaultLang.code;
|
|
||||||
const [langCode, setLangCode] = useState(currentLangCode);
|
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
const onResize = () => {
|
const onResize = () => {
|
||||||
@@ -241,6 +222,9 @@ function ExcalidrawWrapper(props: { collab: CollabAPI }) {
|
|||||||
initializeScene({
|
initializeScene({
|
||||||
resetScene: excalidrawApi.resetScene,
|
resetScene: excalidrawApi.resetScene,
|
||||||
initializeSocketClient: collab.initializeSocketClient,
|
initializeSocketClient: collab.initializeSocketClient,
|
||||||
|
onLateInitialization: (scene) => {
|
||||||
|
initialStatePromiseRef.current.promise.resolve(scene);
|
||||||
|
},
|
||||||
}).then((scene) => {
|
}).then((scene) => {
|
||||||
initialStatePromiseRef.current.promise.resolve(scene);
|
initialStatePromiseRef.current.promise.resolve(scene);
|
||||||
});
|
});
|
||||||
@@ -278,10 +262,6 @@ function ExcalidrawWrapper(props: { collab: CollabAPI }) {
|
|||||||
};
|
};
|
||||||
}, [collab.initializeSocketClient]);
|
}, [collab.initializeSocketClient]);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
languageDetector.cacheUserLanguage(langCode);
|
|
||||||
}, [langCode]);
|
|
||||||
|
|
||||||
const onChange = (
|
const onChange = (
|
||||||
elements: readonly ExcalidrawElement[],
|
elements: readonly ExcalidrawElement[],
|
||||||
appState: AppState,
|
appState: AppState,
|
||||||
@@ -317,32 +297,6 @@ function ExcalidrawWrapper(props: { collab: CollabAPI }) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderFooter = useCallback(
|
|
||||||
(isMobile: boolean) => {
|
|
||||||
const renderLanguageList = () => (
|
|
||||||
<LanguageList
|
|
||||||
onChange={(langCode) => {
|
|
||||||
setLangCode(langCode);
|
|
||||||
}}
|
|
||||||
languages={languages}
|
|
||||||
floating={!isMobile}
|
|
||||||
currentLangCode={langCode}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
if (isMobile) {
|
|
||||||
return (
|
|
||||||
<fieldset>
|
|
||||||
<legend>{t("labels.language")}</legend>
|
|
||||||
{renderLanguageList()}
|
|
||||||
</fieldset>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return renderLanguageList();
|
|
||||||
},
|
|
||||||
[langCode],
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Excalidraw
|
<Excalidraw
|
||||||
@@ -356,8 +310,6 @@ function ExcalidrawWrapper(props: { collab: CollabAPI }) {
|
|||||||
isCollaborating={collab.isCollaborating}
|
isCollaborating={collab.isCollaborating}
|
||||||
onPointerUpdate={collab.onPointerUpdate}
|
onPointerUpdate={collab.onPointerUpdate}
|
||||||
onExportToBackend={onExportToBackend}
|
onExportToBackend={onExportToBackend}
|
||||||
renderFooter={renderFooter}
|
|
||||||
langCode={langCode}
|
|
||||||
/>
|
/>
|
||||||
{errorMessage && (
|
{errorMessage && (
|
||||||
<ErrorDialog
|
<ErrorDialog
|
||||||
|
|||||||
+3
-1
@@ -2,6 +2,8 @@ import { AppState } from "./types";
|
|||||||
import { ExcalidrawElement } from "./element/types";
|
import { ExcalidrawElement } from "./element/types";
|
||||||
import { isLinearElement } from "./element/typeChecks";
|
import { isLinearElement } from "./element/typeChecks";
|
||||||
import { deepCopyElement } from "./element/newElement";
|
import { deepCopyElement } from "./element/newElement";
|
||||||
|
import { getDateTime } from "./utils";
|
||||||
|
import { t } from "./i18n";
|
||||||
|
|
||||||
export interface HistoryEntry {
|
export interface HistoryEntry {
|
||||||
appState: ReturnType<typeof clearAppStatePropertiesForHistory>;
|
appState: ReturnType<typeof clearAppStatePropertiesForHistory>;
|
||||||
@@ -24,7 +26,7 @@ const clearAppStatePropertiesForHistory = (appState: AppState) => {
|
|||||||
viewBackgroundColor: appState.viewBackgroundColor,
|
viewBackgroundColor: appState.viewBackgroundColor,
|
||||||
editingLinearElement: appState.editingLinearElement,
|
editingLinearElement: appState.editingLinearElement,
|
||||||
editingGroupId: appState.editingGroupId,
|
editingGroupId: appState.editingGroupId,
|
||||||
name: appState.name,
|
name: appState.name || `${t("labels.untitled")}-${getDateTime()}`,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+72
-55
@@ -1,85 +1,93 @@
|
|||||||
|
import LanguageDetector from "i18next-browser-languagedetector";
|
||||||
import { EVENT_CHANGE, trackEvent } from "./analytics";
|
import { EVENT_CHANGE, trackEvent } from "./analytics";
|
||||||
|
|
||||||
import fallbackLangData from "./locales/en.json";
|
import fallbackLanguageData from "./locales/en.json";
|
||||||
import percentages from "./locales/percentages.json";
|
import percentages from "./locales/percentages.json";
|
||||||
|
|
||||||
const COMPLETION_THRESHOLD = 85;
|
const COMPLETION_THRESHOLD = 85;
|
||||||
|
|
||||||
export interface Language {
|
interface Language {
|
||||||
code: string;
|
lng: string;
|
||||||
label: string;
|
label: string;
|
||||||
rtl?: boolean;
|
rtl?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const allLanguages: Language[] = [
|
const allLanguages: Language[] = [
|
||||||
{ code: "ar-SA", label: "العربية", rtl: true },
|
{ lng: "ar-SA", label: "العربية", rtl: true },
|
||||||
{ code: "bg-BG", label: "Български" },
|
{ lng: "bg-BG", label: "Български" },
|
||||||
{ code: "ca-ES", label: "Catalan" },
|
{ lng: "ca-ES", label: "Catalan" },
|
||||||
{ code: "de-DE", label: "Deutsch" },
|
{ lng: "de-DE", label: "Deutsch" },
|
||||||
{ code: "el-GR", label: "Ελληνικά" },
|
{ lng: "el-GR", label: "Ελληνικά" },
|
||||||
{ code: "es-ES", label: "Español" },
|
{ lng: "es-ES", label: "Español" },
|
||||||
{ code: "fa-IR", label: "فارسی", rtl: true },
|
{ lng: "fa-IR", label: "فارسی", rtl: true },
|
||||||
{ code: "fi-FI", label: "Suomi" },
|
{ lng: "fi-FI", label: "Suomi" },
|
||||||
{ code: "fr-FR", label: "Français" },
|
{ lng: "fr-FR", label: "Français" },
|
||||||
{ code: "he-IL", label: "עברית", rtl: true },
|
{ lng: "he-IL", label: "עברית", rtl: true },
|
||||||
{ code: "hi-IN", label: "हिन्दी" },
|
{ lng: "hi-IN", label: "हिन्दी" },
|
||||||
{ code: "hu-HU", label: "Magyar" },
|
{ lng: "hu-HU", label: "Magyar" },
|
||||||
{ code: "id-ID", label: "Bahasa Indonesia" },
|
{ lng: "id-ID", label: "Bahasa Indonesia" },
|
||||||
{ code: "it-IT", label: "Italiano" },
|
{ lng: "it-IT", label: "Italiano" },
|
||||||
{ code: "ja-JP", label: "日本語" },
|
{ lng: "ja-JP", label: "日本語" },
|
||||||
{ code: "ko-KR", label: "한국어" },
|
{ lng: "ko-KR", label: "한국어" },
|
||||||
{ code: "my-MM", label: "Burmese" },
|
{ lng: "my-MM", label: "Burmese" },
|
||||||
{ code: "nb-NO", label: "Norsk bokmål" },
|
{ lng: "nb-NO", label: "Norsk bokmål" },
|
||||||
{ code: "nl-NL", label: "Nederlands" },
|
{ lng: "nl-NL", label: "Nederlands" },
|
||||||
{ code: "nn-NO", label: "Norsk nynorsk" },
|
{ lng: "nn-NO", label: "Norsk nynorsk" },
|
||||||
{ code: "pl-PL", label: "Polski" },
|
{ lng: "pl-PL", label: "Polski" },
|
||||||
{ code: "pt-BR", label: "Português Brasileiro" },
|
{ lng: "pt-BR", label: "Português Brasileiro" },
|
||||||
{ code: "pt-PT", label: "Português" },
|
{ lng: "pt-PT", label: "Português" },
|
||||||
{ code: "ro-RO", label: "Română" },
|
{ lng: "ro-RO", label: "Română" },
|
||||||
{ code: "ru-RU", label: "Русский" },
|
{ lng: "ru-RU", label: "Русский" },
|
||||||
{ code: "sk-SK", label: "Slovenčina" },
|
{ lng: "sk-SK", label: "Slovenčina" },
|
||||||
{ code: "sv-SE", label: "Svenska" },
|
{ lng: "sv-SE", label: "Svenska" },
|
||||||
{ code: "tr-TR", label: "Türkçe" },
|
{ lng: "tr-TR", label: "Türkçe" },
|
||||||
{ code: "uk-UA", label: "Українська" },
|
{ lng: "uk-UA", label: "Українська" },
|
||||||
{ code: "zh-CN", label: "简体中文" },
|
{ lng: "zh-CN", label: "简体中文" },
|
||||||
{ code: "zh-TW", label: "繁體中文" },
|
{ lng: "zh-TW", label: "繁體中文" },
|
||||||
];
|
];
|
||||||
|
|
||||||
export const defaultLang = { code: "en", label: "English" };
|
export const languages: Language[] = [{ lng: "en", label: "English" }]
|
||||||
|
|
||||||
export const languages: Language[] = [defaultLang]
|
|
||||||
.concat(
|
.concat(
|
||||||
allLanguages.sort((left, right) => (left.label > right.label ? 1 : -1)),
|
allLanguages.sort((left, right) => (left.label > right.label ? 1 : -1)),
|
||||||
)
|
)
|
||||||
.filter(
|
.filter(
|
||||||
(lang) =>
|
(lang) =>
|
||||||
(percentages as Record<string, number>)[lang.code] >=
|
(percentages as Record<string, number>)[lang.lng] >= COMPLETION_THRESHOLD,
|
||||||
COMPLETION_THRESHOLD,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
let currentLang: Language = defaultLang;
|
let currentLanguage = languages[0];
|
||||||
let currentLangData = {};
|
let currentLanguageData = {};
|
||||||
|
const fallbackLanguage = languages[0];
|
||||||
|
|
||||||
export const setLanguage = async (lang: Language) => {
|
export const setLanguage = async (newLng: string | undefined) => {
|
||||||
currentLang = lang;
|
currentLanguage =
|
||||||
document.documentElement.dir = currentLang.rtl ? "rtl" : "ltr";
|
languages.find((language) => language.lng === newLng) || fallbackLanguage;
|
||||||
|
|
||||||
currentLangData = await import(
|
document.documentElement.dir = currentLanguage.rtl ? "rtl" : "ltr";
|
||||||
/* webpackChunkName: "i18n-[request]" */ `./locales/${currentLang.code}.json`
|
|
||||||
|
currentLanguageData = await import(
|
||||||
|
/* webpackChunkName: "i18n-[request]" */ `./locales/${currentLanguage.lng}.json`
|
||||||
);
|
);
|
||||||
trackEvent(EVENT_CHANGE, "language", currentLang.code);
|
languageDetector.cacheUserLanguage(currentLanguage.lng);
|
||||||
|
trackEvent(EVENT_CHANGE, "language", currentLanguage.lng);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const setLanguageFirstTime = async (lang: Language) => {
|
export const setLanguageFirstTime = async () => {
|
||||||
currentLang = lang;
|
const newLng: string | undefined = languageDetector.detect();
|
||||||
document.documentElement.dir = currentLang.rtl ? "rtl" : "ltr";
|
|
||||||
|
|
||||||
currentLangData = await import(
|
currentLanguage =
|
||||||
/* webpackChunkName: "i18n-[request]" */ `./locales/${currentLang.code}.json`
|
languages.find((language) => language.lng === newLng) || fallbackLanguage;
|
||||||
|
|
||||||
|
document.documentElement.dir = currentLanguage.rtl ? "rtl" : "ltr";
|
||||||
|
|
||||||
|
currentLanguageData = await import(
|
||||||
|
/* webpackChunkName: "i18n-[request]" */ `./locales/${currentLanguage.lng}.json`
|
||||||
);
|
);
|
||||||
|
|
||||||
|
languageDetector.cacheUserLanguage(currentLanguage.lng);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getLanguage = () => currentLang;
|
export const getLanguage = () => currentLanguage;
|
||||||
|
|
||||||
const findPartsForData = (data: any, parts: string[]) => {
|
const findPartsForData = (data: any, parts: string[]) => {
|
||||||
for (let index = 0; index < parts.length; ++index) {
|
for (let index = 0; index < parts.length; ++index) {
|
||||||
@@ -98,8 +106,8 @@ const findPartsForData = (data: any, parts: string[]) => {
|
|||||||
export const t = (path: string, replacement?: { [key: string]: string }) => {
|
export const t = (path: string, replacement?: { [key: string]: string }) => {
|
||||||
const parts = path.split(".");
|
const parts = path.split(".");
|
||||||
let translation =
|
let translation =
|
||||||
findPartsForData(currentLangData, parts) ||
|
findPartsForData(currentLanguageData, parts) ||
|
||||||
findPartsForData(fallbackLangData, parts);
|
findPartsForData(fallbackLanguageData, parts);
|
||||||
if (translation === undefined) {
|
if (translation === undefined) {
|
||||||
throw new Error(`Can't find translation for ${path}`);
|
throw new Error(`Can't find translation for ${path}`);
|
||||||
}
|
}
|
||||||
@@ -111,3 +119,12 @@ export const t = (path: string, replacement?: { [key: string]: string }) => {
|
|||||||
}
|
}
|
||||||
return translation;
|
return translation;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const languageDetector = new LanguageDetector();
|
||||||
|
languageDetector.init({
|
||||||
|
languageUtils: {
|
||||||
|
formatLanguageCode: (lng: string) => lng,
|
||||||
|
isWhitelisted: () => true,
|
||||||
|
},
|
||||||
|
checkWhitelist: false,
|
||||||
|
});
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { exportToCanvas } from "./scene/export";
|
import { exportToCanvas } from "./scene/export";
|
||||||
import { getDefaultAppState } from "./appState";
|
import { getDefaultAppState } from "./appState";
|
||||||
import { SCENE_NAME_FALLBACK } from "./constants";
|
|
||||||
|
|
||||||
const { registerFont, createCanvas } = require("canvas");
|
const { registerFont, createCanvas } = require("canvas");
|
||||||
|
|
||||||
@@ -62,7 +61,6 @@ const canvas = exportToCanvas(
|
|||||||
elements as any,
|
elements as any,
|
||||||
{
|
{
|
||||||
...getDefaultAppState(),
|
...getDefaultAppState(),
|
||||||
name: SCENE_NAME_FALLBACK,
|
|
||||||
offsetTop: 0,
|
offsetTop: 0,
|
||||||
offsetLeft: 0,
|
offsetLeft: 0,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ export const IsMobileProvider = ({
|
|||||||
if (!query.current) {
|
if (!query.current) {
|
||||||
query.current = window.matchMedia
|
query.current = window.matchMedia
|
||||||
? window.matchMedia(
|
? window.matchMedia(
|
||||||
// keep up to date with _variables.scss
|
|
||||||
"(max-width: 640px), (max-height: 500px) and (max-width: 1000px)",
|
"(max-width: 640px), (max-height: 500px) and (max-width: 1000px)",
|
||||||
)
|
)
|
||||||
: (({
|
: (({
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
{
|
{
|
||||||
"labels": {
|
"labels": {
|
||||||
"paste": "Paste",
|
"paste": "Paste",
|
||||||
"pasteCharts": "Paste charts",
|
|
||||||
"selectAll": "Select all",
|
"selectAll": "Select all",
|
||||||
"multiSelect": "Add element to selection",
|
"multiSelect": "Add element to selection",
|
||||||
"moveCanvas": "Move canvas",
|
"moveCanvas": "Move canvas",
|
||||||
|
|||||||
@@ -16,9 +16,6 @@ Please add the latest change on the top under the correct section.
|
|||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
- Remove language picker, and add `langCode`, `renderFooter` [#2644](https://github.com/excalidraw/excalidraw/pull/2644):
|
|
||||||
- BREAKING: removed the language picker from UI. It is now the host app's responsibility to implement a language picker if desirable, using the newly added [`renderFooter`](https://github.com/excalidraw/excalidraw/blob/master/src/packages/excalidraw/README.md#renderFooter) prop. The reasoning is that the i18n should be controlled by the app itself, not by the nested Excalidraw component.
|
|
||||||
- Added [`langCode`](https://github.com/excalidraw/excalidraw/blob/master/src/packages/excalidraw/README.md#langCode) prop to control the UI language.
|
|
||||||
- Add support for `exportToBackend` prop to allow host apps to implement shareable links [#2612](https://github.com/excalidraw/excalidraw/pull/2612/files)
|
- Add support for `exportToBackend` prop to allow host apps to implement shareable links [#2612](https://github.com/excalidraw/excalidraw/pull/2612/files)
|
||||||
- Add zoom to selection [#2522](https://github.com/excalidraw/excalidraw/pull/2522)
|
- Add zoom to selection [#2522](https://github.com/excalidraw/excalidraw/pull/2522)
|
||||||
- Insert Library items in the middle of the screen [#2527](https://github.com/excalidraw/excalidraw/pull/2527)
|
- Insert Library items in the middle of the screen [#2527](https://github.com/excalidraw/excalidraw/pull/2527)
|
||||||
@@ -30,7 +27,6 @@ Please add the latest change on the top under the correct section.
|
|||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
|
|
||||||
- Fix initialization when browser tab not focused [#2677](https://github.com/excalidraw/excalidraw/pull/2677)
|
|
||||||
- Consistent case for export locale strings [#2622](https://github.com/excalidraw/excalidraw/pull/2622)
|
- Consistent case for export locale strings [#2622](https://github.com/excalidraw/excalidraw/pull/2622)
|
||||||
- Remove unnecessary console.error as it was polluting Sentry [#2637](https://github.com/excalidraw/excalidraw/pull/2637)
|
- Remove unnecessary console.error as it was polluting Sentry [#2637](https://github.com/excalidraw/excalidraw/pull/2637)
|
||||||
- Fix scroll-to-center on init for non-zero canvas offsets [#2445](https://github.com/excalidraw/excalidraw/pull/2445)
|
- Fix scroll-to-center on init for non-zero canvas offsets [#2445](https://github.com/excalidraw/excalidraw/pull/2445)
|
||||||
@@ -55,10 +51,6 @@ Please add the latest change on the top under the correct section.
|
|||||||
|
|
||||||
- Bump ini from 1.3.5 to 1.3.7 in /src/packages/excalidraw [#2500](https://github.com/excalidraw/excalidraw/pull/2500)
|
- Bump ini from 1.3.5 to 1.3.7 in /src/packages/excalidraw [#2500](https://github.com/excalidraw/excalidraw/pull/2500)
|
||||||
|
|
||||||
### Docs
|
|
||||||
|
|
||||||
- Document some of the more exotic element props [#2673](https://github.com/excalidraw/excalidraw/pull/2673)
|
|
||||||
|
|
||||||
## 0.1.1
|
## 0.1.1
|
||||||
|
|
||||||
#### Fix
|
#### Fix
|
||||||
|
|||||||
@@ -139,8 +139,6 @@ export default function App() {
|
|||||||
| [`isCollaborating`](#isCollaborating) | `boolean` | | This implies if the app is in collaboration mode |
|
| [`isCollaborating`](#isCollaborating) | `boolean` | | This implies if the app is in collaboration mode |
|
||||||
| [`onPointerUpdate`](#onPointerUpdate) | Function | | Callback triggered when mouse pointer is updated. |
|
| [`onPointerUpdate`](#onPointerUpdate) | Function | | Callback triggered when mouse pointer is updated. |
|
||||||
| [`onExportToBackend`](#onExportToBackend) | Function | | Callback triggered when link button is clicked on export dialog |
|
| [`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 |
|
|
||||||
|
|
||||||
#### `width`
|
#### `width`
|
||||||
|
|
||||||
@@ -272,21 +270,3 @@ This callback is triggered when the shareable-link button is clicked in the expo
|
|||||||
1. `exportedElements`: An array of [non deleted elements](https://github.com/excalidraw/excalidraw/blob/6e45cb95dbd7a8be1859c7055b06957298e3097c/src/element/types.ts#L76) which needs to be exported.
|
1. `exportedElements`: An array of [non deleted elements](https://github.com/excalidraw/excalidraw/blob/6e45cb95dbd7a8be1859c7055b06957298e3097c/src/element/types.ts#L76) which needs to be exported.
|
||||||
2. `appState`: [AppState](https://github.com/excalidraw/excalidraw/blob/4c90ea5667d29effe8ec4a115e49efc7c340cdb3/src/types.ts#L33) of the scene.
|
2. `appState`: [AppState](https://github.com/excalidraw/excalidraw/blob/4c90ea5667d29effe8ec4a115e49efc7c340cdb3/src/types.ts#L33) of the scene.
|
||||||
3. `canvas`: The `HTMLCanvasElement` 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/d337c8b15f6c1085287b12ecbe59c96e2c4e0ff4/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/c35d983fef8a83ba842dd892c0f461111a3e8589/src/i18n.ts#L9) |
|
|
||||||
|
|
||||||
#### `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).
|
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import "../../css/styles.scss";
|
|||||||
|
|
||||||
import { ExcalidrawAPIRefValue, ExcalidrawProps } from "../../types";
|
import { ExcalidrawAPIRefValue, ExcalidrawProps } from "../../types";
|
||||||
import { IsMobileProvider } from "../../is-mobile";
|
import { IsMobileProvider } from "../../is-mobile";
|
||||||
import { defaultLang } from "../../i18n";
|
|
||||||
|
|
||||||
const Excalidraw = (props: ExcalidrawProps) => {
|
const Excalidraw = (props: ExcalidrawProps) => {
|
||||||
const {
|
const {
|
||||||
@@ -24,8 +23,6 @@ const Excalidraw = (props: ExcalidrawProps) => {
|
|||||||
isCollaborating,
|
isCollaborating,
|
||||||
onPointerUpdate,
|
onPointerUpdate,
|
||||||
onExportToBackend,
|
onExportToBackend,
|
||||||
renderFooter,
|
|
||||||
langCode = defaultLang.code,
|
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -47,7 +44,7 @@ const Excalidraw = (props: ExcalidrawProps) => {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<InitializeApp langCode={langCode}>
|
<InitializeApp>
|
||||||
<IsMobileProvider>
|
<IsMobileProvider>
|
||||||
<App
|
<App
|
||||||
width={width}
|
width={width}
|
||||||
@@ -62,8 +59,6 @@ const Excalidraw = (props: ExcalidrawProps) => {
|
|||||||
isCollaborating={isCollaborating}
|
isCollaborating={isCollaborating}
|
||||||
onPointerUpdate={onPointerUpdate}
|
onPointerUpdate={onPointerUpdate}
|
||||||
onExportToBackend={onExportToBackend}
|
onExportToBackend={onExportToBackend}
|
||||||
renderFooter={renderFooter}
|
|
||||||
langCode={langCode}
|
|
||||||
/>
|
/>
|
||||||
</IsMobileProvider>
|
</IsMobileProvider>
|
||||||
</InitializeApp>
|
</InitializeApp>
|
||||||
@@ -99,4 +94,3 @@ export {
|
|||||||
getSyncableElements,
|
getSyncableElements,
|
||||||
getElementMap,
|
getElementMap,
|
||||||
} from "../../element";
|
} from "../../element";
|
||||||
export { defaultLang, languages } from "../../i18n";
|
|
||||||
|
|||||||
+199
-75
@@ -1149,12 +1149,6 @@
|
|||||||
"to-fast-properties": "^2.0.0"
|
"to-fast-properties": "^2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@discoveryjs/json-ext": {
|
|
||||||
"version": "0.5.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.2.tgz",
|
|
||||||
"integrity": "sha512-HyYEUDeIj5rRQU2Hk5HTB2uHsbRQpF70nvMhVzi+VJR0X+xNEhjPui4/kBf3VeH/wqD28PT4sVOm8qqLjBrSZg==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"@polka/url": {
|
"@polka/url": {
|
||||||
"version": "1.0.0-next.11",
|
"version": "1.0.0-next.11",
|
||||||
"resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.11.tgz",
|
"resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.11.tgz",
|
||||||
@@ -1375,18 +1369,18 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@webpack-cli/info": {
|
"@webpack-cli/info": {
|
||||||
"version": "1.2.1",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.1.0.tgz",
|
||||||
"integrity": "sha512-fLnDML5HZ5AEKzHul8xLAksoKN2cibu6MgonkUj8R9V7bbeVRkd1XbGEGWrAUNYHbX1jcqCsDEpBviE5StPMzQ==",
|
"integrity": "sha512-uNWSdaYHc+f3LdIZNwhdhkjjLDDl3jP2+XBqAq9H8DjrJUvlOKdP8TNruy1yEaDfgpAIgbSAN7pye4FEHg9tYQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"envinfo": "^7.7.3"
|
"envinfo": "^7.7.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@webpack-cli/serve": {
|
"@webpack-cli/serve": {
|
||||||
"version": "1.2.1",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.1.0.tgz",
|
||||||
"integrity": "sha512-Zj1z6AyS+vqV6Hfi7ngCjFGdHV5EwZNIHo6QfFTNe9PyW+zBU1zJ9BiOW1pmUEq950RC4+Dym6flyA/61/vhyw==",
|
"integrity": "sha512-7RfnMXCpJ/NThrhq4gYQYILB18xWyoQcBey81oIyVbmgbc6m5ZHHyFK+DyH7pLHJf0p14MxL4mTsoPAgBSTpIg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"@xtuc/ieee754": {
|
"@xtuc/ieee754": {
|
||||||
@@ -1452,6 +1446,12 @@
|
|||||||
"color-convert": "^1.9.0"
|
"color-convert": "^1.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"array-back": {
|
||||||
|
"version": "4.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/array-back/-/array-back-4.0.1.tgz",
|
||||||
|
"integrity": "sha512-Z/JnaVEXv+A9xabHzN43FiiiWEE7gPCRXMrVmRm00tWbjZRul1iHm7ECzlyNq1p4a4ATXz+G9FJ3GqGOkOV3fg==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"babel-code-frame": {
|
"babel-code-frame": {
|
||||||
"version": "6.26.0",
|
"version": "6.26.0",
|
||||||
"resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz",
|
"resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz",
|
||||||
@@ -1761,6 +1761,18 @@
|
|||||||
"integrity": "sha512-puCDz0CzydiSYOrnXpz/PKd69zRrribezjtE9yd4zvytoRc8+RY/KJPvtPFKZS3E3wP6neGyMe0vOTlHO5L3Pw==",
|
"integrity": "sha512-puCDz0CzydiSYOrnXpz/PKd69zRrribezjtE9yd4zvytoRc8+RY/KJPvtPFKZS3E3wP6neGyMe0vOTlHO5L3Pw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"command-line-usage": {
|
||||||
|
"version": "6.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/command-line-usage/-/command-line-usage-6.1.1.tgz",
|
||||||
|
"integrity": "sha512-F59pEuAR9o1SF/bD0dQBDluhpT4jJQNWUHEuVBqpDmCUo6gPjCi+m9fCWnWZVR/oG6cMTUms4h+3NPl74wGXvA==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"array-back": "^4.0.1",
|
||||||
|
"chalk": "^2.4.2",
|
||||||
|
"table-layout": "^1.0.1",
|
||||||
|
"typical": "^5.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"commander": {
|
"commander": {
|
||||||
"version": "2.20.3",
|
"version": "2.20.3",
|
||||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
|
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
|
||||||
@@ -1900,6 +1912,12 @@
|
|||||||
"ms": "2.1.2"
|
"ms": "2.1.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"deep-extend": {
|
||||||
|
"version": "0.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
|
||||||
|
"integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"define-properties": {
|
"define-properties": {
|
||||||
"version": "1.1.3",
|
"version": "1.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
|
||||||
@@ -1927,6 +1945,15 @@
|
|||||||
"integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==",
|
"integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"end-of-stream": {
|
||||||
|
"version": "1.4.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
|
||||||
|
"integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"once": "^1.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"enhanced-resolve": {
|
"enhanced-resolve": {
|
||||||
"version": "4.3.0",
|
"version": "4.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.3.0.tgz",
|
||||||
@@ -1954,9 +1981,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"errno": {
|
"errno": {
|
||||||
"version": "0.1.8",
|
"version": "0.1.7",
|
||||||
"resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz",
|
"resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz",
|
||||||
"integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==",
|
"integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"prr": "~1.0.1"
|
"prr": "~1.0.1"
|
||||||
@@ -2020,19 +2047,19 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"execa": {
|
"execa": {
|
||||||
"version": "5.0.0",
|
"version": "4.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/execa/-/execa-5.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz",
|
||||||
"integrity": "sha512-ov6w/2LCiuyO4RLYGdpFGjkcs0wMTgGE8PrkTHikeUy5iJekXyPIKUjifk5CsE0pt7sMCrMZ3YNqoCj6idQOnQ==",
|
"integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"cross-spawn": "^7.0.3",
|
"cross-spawn": "^7.0.0",
|
||||||
"get-stream": "^6.0.0",
|
"get-stream": "^5.0.0",
|
||||||
"human-signals": "^2.1.0",
|
"human-signals": "^1.1.1",
|
||||||
"is-stream": "^2.0.0",
|
"is-stream": "^2.0.0",
|
||||||
"merge-stream": "^2.0.0",
|
"merge-stream": "^2.0.0",
|
||||||
"npm-run-path": "^4.0.1",
|
"npm-run-path": "^4.0.0",
|
||||||
"onetime": "^5.1.2",
|
"onetime": "^5.1.0",
|
||||||
"signal-exit": "^3.0.3",
|
"signal-exit": "^3.0.2",
|
||||||
"strip-final-newline": "^2.0.0"
|
"strip-final-newline": "^2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2048,12 +2075,6 @@
|
|||||||
"integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
|
"integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"fastest-levenshtein": {
|
|
||||||
"version": "1.0.12",
|
|
||||||
"resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz",
|
|
||||||
"integrity": "sha512-On2N+BpYJ15xIC974QNVuYGMOlEVt4s0EOI3wwMqOmK1fdDY+FN/zltPV8vosq4ad4c/gJ1KHScUn/6AWIgiow==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"file-loader": {
|
"file-loader": {
|
||||||
"version": "6.2.0",
|
"version": "6.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz",
|
||||||
@@ -2142,10 +2163,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"get-stream": {
|
"get-stream": {
|
||||||
"version": "6.0.0",
|
"version": "5.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
|
||||||
"integrity": "sha512-A1B3Bh1UmL0bidM/YX2NsCOTnGJePL9rO/M+Mw3m9f2gUpfokS0hi5Eah0WSUEWZdZhIZtMjkIYS7mDfOqNHbg==",
|
"integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"pump": "^3.0.0"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"glob-to-regexp": {
|
"glob-to-regexp": {
|
||||||
"version": "0.4.1",
|
"version": "0.4.1",
|
||||||
@@ -2205,9 +2229,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"human-signals": {
|
"human-signals": {
|
||||||
"version": "2.1.0",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz",
|
||||||
"integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==",
|
"integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"icss-utils": {
|
"icss-utils": {
|
||||||
@@ -2224,6 +2248,51 @@
|
|||||||
"requires": {
|
"requires": {
|
||||||
"pkg-dir": "^4.2.0",
|
"pkg-dir": "^4.2.0",
|
||||||
"resolve-cwd": "^3.0.0"
|
"resolve-cwd": "^3.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"find-up": {
|
||||||
|
"version": "4.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
|
||||||
|
"integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"locate-path": "^5.0.0",
|
||||||
|
"path-exists": "^4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"locate-path": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"p-locate": "^4.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"p-locate": {
|
||||||
|
"version": "4.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
|
||||||
|
"integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"p-limit": "^2.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"path-exists": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"pkg-dir": {
|
||||||
|
"version": "4.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
|
||||||
|
"integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"find-up": "^4.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"indexes-of": {
|
"indexes-of": {
|
||||||
@@ -2253,15 +2322,6 @@
|
|||||||
"loose-envify": "^1.0.0"
|
"loose-envify": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"is-core-module": {
|
|
||||||
"version": "2.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.2.0.tgz",
|
|
||||||
"integrity": "sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"has": "^1.0.3"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"is-number": {
|
"is-number": {
|
||||||
"version": "7.0.0",
|
"version": "7.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
|
||||||
@@ -2353,6 +2413,12 @@
|
|||||||
"integrity": "sha512-ZRbnvdg/NxqzC7L9Uyqzf4psi1OM4Cuc+sJAkQPjO6XkQIJTNbfK2Rsmbw8fx1p2mkZdp2FZYo2+LwXYY/uwIA==",
|
"integrity": "sha512-ZRbnvdg/NxqzC7L9Uyqzf4psi1OM4Cuc+sJAkQPjO6XkQIJTNbfK2Rsmbw8fx1p2mkZdp2FZYo2+LwXYY/uwIA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"leven": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"loader-runner": {
|
"loader-runner": {
|
||||||
"version": "4.1.0",
|
"version": "4.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.1.0.tgz",
|
||||||
@@ -2464,18 +2530,18 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"mime-db": {
|
"mime-db": {
|
||||||
"version": "1.45.0",
|
"version": "1.44.0",
|
||||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.45.0.tgz",
|
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz",
|
||||||
"integrity": "sha512-CkqLUxUk15hofLoLyljJSrukZi8mAtgd+yE5uO4tqRZsdsAJKv0O+rFMhVDRJgozy+yG6md5KwuXhD4ocIoP+w==",
|
"integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"mime-types": {
|
"mime-types": {
|
||||||
"version": "2.1.28",
|
"version": "2.1.27",
|
||||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.28.tgz",
|
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz",
|
||||||
"integrity": "sha512-0TO2yJ5YHYr7M2zzT7gDU1tbwHxEUWBCLt0lscSNpcdAfFyJOVEpRYNS7EXVcTLNj/25QO8gulHC5JtTzSE2UQ==",
|
"integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"mime-db": "1.45.0"
|
"mime-db": "1.44.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"mimic-fn": {
|
"mimic-fn": {
|
||||||
@@ -2576,6 +2642,15 @@
|
|||||||
"object-keys": "^1.1.1"
|
"object-keys": "^1.1.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"once": {
|
||||||
|
"version": "1.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||||
|
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"wrappy": "1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"onetime": {
|
"onetime": {
|
||||||
"version": "5.1.2",
|
"version": "5.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
|
||||||
@@ -2732,6 +2807,16 @@
|
|||||||
"integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=",
|
"integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"pump": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"end-of-stream": "^1.1.0",
|
||||||
|
"once": "^1.3.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"punycode": {
|
"punycode": {
|
||||||
"version": "2.1.1",
|
"version": "2.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
|
||||||
@@ -2771,6 +2856,12 @@
|
|||||||
"resolve": "^1.9.0"
|
"resolve": "^1.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"reduce-flatten": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/reduce-flatten/-/reduce-flatten-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-EJ4UNY/U1t2P/2k6oqotuX2Cc3T6nxJwsM0N0asT7dhrtH1ltUxDn4NalSYmPE2rCkVpcf/X6R0wDwcFpzhd4w==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"regenerate": {
|
"regenerate": {
|
||||||
"version": "1.4.2",
|
"version": "1.4.2",
|
||||||
"resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz",
|
"resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz",
|
||||||
@@ -2839,12 +2930,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"resolve": {
|
"resolve": {
|
||||||
"version": "1.19.0",
|
"version": "1.17.0",
|
||||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz",
|
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz",
|
||||||
"integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==",
|
"integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"is-core-module": "^2.1.0",
|
|
||||||
"path-parse": "^1.0.6"
|
"path-parse": "^1.0.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -3036,6 +3126,18 @@
|
|||||||
"has-flag": "^3.0.0"
|
"has-flag": "^3.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"table-layout": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/table-layout/-/table-layout-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-dEquqYNJiGwY7iPfZ3wbXDI944iqanTSchrACLL2nOB+1r+h1Nzu2eH+DuPPvWvm5Ry7iAPeFlgEtP5bIp5U7Q==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"array-back": "^4.0.1",
|
||||||
|
"deep-extend": "~0.6.0",
|
||||||
|
"typical": "^5.2.0",
|
||||||
|
"wordwrapjs": "^4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"tapable": {
|
"tapable": {
|
||||||
"version": "1.1.3",
|
"version": "1.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz",
|
||||||
@@ -3125,9 +3227,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"ts-loader": {
|
"ts-loader": {
|
||||||
"version": "8.0.13",
|
"version": "8.0.12",
|
||||||
"resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-8.0.13.tgz",
|
"resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-8.0.12.tgz",
|
||||||
"integrity": "sha512-1o1nO6aqouA23d2nlcMSEyPMAWRhnYUU0EQUJSc60E0TUyBNX792RHFYUN1ZM29vhMUNayrsbj2UVdZwKhXCDA==",
|
"integrity": "sha512-UIivVfGVJDdwwjgSrbtcL9Nf10c1BWnL1mxAQUVcnhNIn/P9W3nP5v60Z0aBMtc7ZrE11lMmU6+5jSgAXmGaYw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"chalk": "^2.3.0",
|
"chalk": "^2.3.0",
|
||||||
@@ -3151,6 +3253,12 @@
|
|||||||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
|
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"typical": {
|
||||||
|
"version": "5.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/typical/-/typical-5.2.0.tgz",
|
||||||
|
"integrity": "sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"unicode-canonical-property-names-ecmascript": {
|
"unicode-canonical-property-names-ecmascript": {
|
||||||
"version": "1.0.4",
|
"version": "1.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz",
|
||||||
@@ -3217,9 +3325,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"webpack": {
|
"webpack": {
|
||||||
"version": "5.11.1",
|
"version": "5.11.0",
|
||||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.11.1.tgz",
|
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.11.0.tgz",
|
||||||
"integrity": "sha512-tNUIdAmYJv+nupRs/U/gqmADm6fgrf5xE+rSlSsf2PgsGO7j2WG7ccU6AWNlOJlHFl+HnmXlBmHIkiLf+XA9mQ==",
|
"integrity": "sha512-ubWv7iP54RqAC/VjixgpnLLogCFbAfSOREcSWnnOlZEU8GICC5eKmJSu6YEnph2N2amKqY9rvxSwgyHxVqpaRw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@types/eslint-scope": "^3.7.0",
|
"@types/eslint-scope": "^3.7.0",
|
||||||
@@ -3249,13 +3357,13 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"enhanced-resolve": {
|
"enhanced-resolve": {
|
||||||
"version": "5.4.1",
|
"version": "5.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.4.0.tgz",
|
||||||
"integrity": "sha512-4GbyIMzYktTFoRSmkbgZ1LU+RXwf4AQ8Z+rSuuh1dC8plp0PPeaWvx6+G4hh4KnUJ48VoxKbNyA1QQQIUpXjYA==",
|
"integrity": "sha512-ZmqfWURB2lConOBM1JdCVfPyMRv5RdKWktLXO6123p97ovVm2CLBgw9t5MBj3jJWA6eHyOeIws9iJQoGFR4euQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"graceful-fs": "^4.2.4",
|
"graceful-fs": "^4.2.4",
|
||||||
"tapable": "^2.2.0"
|
"tapable": "^2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"find-up": {
|
"find-up": {
|
||||||
@@ -3414,21 +3522,21 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"webpack-cli": {
|
"webpack-cli": {
|
||||||
"version": "4.3.1",
|
"version": "4.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.2.0.tgz",
|
||||||
"integrity": "sha512-/F4+9QNZM/qKzzL9/06Am8NXIkGV+/NqQ62Dx7DSqudxxpAgBqYn6V7+zp+0Y7JuWksKUbczRY3wMTd+7Uj6OA==",
|
"integrity": "sha512-EIl3k88vaF4fSxWSgtAQR+VwicfLMTZ9amQtqS4o+TDPW9HGaEpbFBbAZ4A3ZOT5SOnMxNOzROsSTPiE8tBJPA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@discoveryjs/json-ext": "^0.5.0",
|
"@webpack-cli/info": "^1.1.0",
|
||||||
"@webpack-cli/info": "^1.2.1",
|
"@webpack-cli/serve": "^1.1.0",
|
||||||
"@webpack-cli/serve": "^1.2.1",
|
|
||||||
"colorette": "^1.2.1",
|
"colorette": "^1.2.1",
|
||||||
|
"command-line-usage": "^6.1.0",
|
||||||
"commander": "^6.2.0",
|
"commander": "^6.2.0",
|
||||||
"enquirer": "^2.3.6",
|
"enquirer": "^2.3.6",
|
||||||
"execa": "^5.0.0",
|
"execa": "^4.1.0",
|
||||||
"fastest-levenshtein": "^1.0.12",
|
|
||||||
"import-local": "^3.0.2",
|
"import-local": "^3.0.2",
|
||||||
"interpret": "^2.2.0",
|
"interpret": "^2.2.0",
|
||||||
|
"leven": "^3.1.0",
|
||||||
"rechoir": "^0.7.0",
|
"rechoir": "^0.7.0",
|
||||||
"v8-compile-cache": "^2.2.0",
|
"v8-compile-cache": "^2.2.0",
|
||||||
"webpack-merge": "^4.2.2"
|
"webpack-merge": "^4.2.2"
|
||||||
@@ -3478,6 +3586,22 @@
|
|||||||
"isexe": "^2.0.0"
|
"isexe": "^2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"wordwrapjs": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/wordwrapjs/-/wordwrapjs-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-Svqw723a3R34KvsMgpjFBYCgNOSdcW3mQFK4wIfhGQhtaFVOJmdYoXgi63ne3dTlWgatVcUc7t4HtQ/+bUVIzQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"reduce-flatten": "^2.0.0",
|
||||||
|
"typical": "^5.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"wrappy": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||||
|
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"ws": {
|
"ws": {
|
||||||
"version": "7.4.1",
|
"version": "7.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/ws/-/ws-7.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/ws/-/ws-7.4.1.tgz",
|
||||||
|
|||||||
@@ -57,10 +57,10 @@
|
|||||||
"mini-css-extract-plugin": "1.3.3",
|
"mini-css-extract-plugin": "1.3.3",
|
||||||
"sass-loader": "10.1.0",
|
"sass-loader": "10.1.0",
|
||||||
"terser-webpack-plugin": "5.0.3",
|
"terser-webpack-plugin": "5.0.3",
|
||||||
"ts-loader": "8.0.13",
|
"ts-loader": "8.0.12",
|
||||||
"webpack": "5.11.1",
|
"webpack": "5.11.0",
|
||||||
"webpack-bundle-analyzer": "4.3.0",
|
"webpack-bundle-analyzer": "4.3.0",
|
||||||
"webpack-cli": "4.3.1"
|
"webpack-cli": "4.2.0"
|
||||||
},
|
},
|
||||||
"bugs": "https://github.com/excalidraw/excalidraw/issues",
|
"bugs": "https://github.com/excalidraw/excalidraw/issues",
|
||||||
"repository": "https://github.com/excalidraw/excalidraw",
|
"repository": "https://github.com/excalidraw/excalidraw",
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
export { exportToBlob, exportToSvg, exportToCanvas } from "../utils.ts";
|
export { exportToBlob, exportToSvg, exportToCanvas } from "./utils";
|
||||||
|
|||||||
Generated
+154
-65
@@ -1064,12 +1064,6 @@
|
|||||||
"to-fast-properties": "^2.0.0"
|
"to-fast-properties": "^2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@discoveryjs/json-ext": {
|
|
||||||
"version": "0.5.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.2.tgz",
|
|
||||||
"integrity": "sha512-HyYEUDeIj5rRQU2Hk5HTB2uHsbRQpF70nvMhVzi+VJR0X+xNEhjPui4/kBf3VeH/wqD28PT4sVOm8qqLjBrSZg==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"@polka/url": {
|
"@polka/url": {
|
||||||
"version": "1.0.0-next.11",
|
"version": "1.0.0-next.11",
|
||||||
"resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.11.tgz",
|
"resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.11.tgz",
|
||||||
@@ -1109,9 +1103,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"@types/node": {
|
"@types/node": {
|
||||||
"version": "14.14.19",
|
"version": "14.14.14",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.19.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.14.tgz",
|
||||||
"integrity": "sha512-4nhBPStMK04rruRVtVc6cDqhu7S9GZai0fpXgPXrFpcPX6Xul8xnrjSdGB4KPBVYG/R5+fXWdCM8qBoiULWGPQ==",
|
"integrity": "sha512-UHnOPWVWV1z+VV8k6L1HhG7UbGBgIdghqF3l9Ny9ApPghbjICXkUJSd/b9gOgQfjM1r+37cipdw/HJ3F6ICEnQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"@webassemblyjs/ast": {
|
"@webassemblyjs/ast": {
|
||||||
@@ -1290,18 +1284,18 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@webpack-cli/info": {
|
"@webpack-cli/info": {
|
||||||
"version": "1.2.1",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.1.0.tgz",
|
||||||
"integrity": "sha512-fLnDML5HZ5AEKzHul8xLAksoKN2cibu6MgonkUj8R9V7bbeVRkd1XbGEGWrAUNYHbX1jcqCsDEpBviE5StPMzQ==",
|
"integrity": "sha512-uNWSdaYHc+f3LdIZNwhdhkjjLDDl3jP2+XBqAq9H8DjrJUvlOKdP8TNruy1yEaDfgpAIgbSAN7pye4FEHg9tYQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"envinfo": "^7.7.3"
|
"envinfo": "^7.7.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@webpack-cli/serve": {
|
"@webpack-cli/serve": {
|
||||||
"version": "1.2.1",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.1.0.tgz",
|
||||||
"integrity": "sha512-Zj1z6AyS+vqV6Hfi7ngCjFGdHV5EwZNIHo6QfFTNe9PyW+zBU1zJ9BiOW1pmUEq950RC4+Dym6flyA/61/vhyw==",
|
"integrity": "sha512-7RfnMXCpJ/NThrhq4gYQYILB18xWyoQcBey81oIyVbmgbc6m5ZHHyFK+DyH7pLHJf0p14MxL4mTsoPAgBSTpIg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"@xtuc/ieee754": {
|
"@xtuc/ieee754": {
|
||||||
@@ -1367,6 +1361,12 @@
|
|||||||
"color-convert": "^1.9.0"
|
"color-convert": "^1.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"array-back": {
|
||||||
|
"version": "4.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/array-back/-/array-back-4.0.1.tgz",
|
||||||
|
"integrity": "sha512-Z/JnaVEXv+A9xabHzN43FiiiWEE7gPCRXMrVmRm00tWbjZRul1iHm7ECzlyNq1p4a4ATXz+G9FJ3GqGOkOV3fg==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"babel-code-frame": {
|
"babel-code-frame": {
|
||||||
"version": "6.26.0",
|
"version": "6.26.0",
|
||||||
"resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz",
|
"resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz",
|
||||||
@@ -1670,6 +1670,18 @@
|
|||||||
"integrity": "sha512-puCDz0CzydiSYOrnXpz/PKd69zRrribezjtE9yd4zvytoRc8+RY/KJPvtPFKZS3E3wP6neGyMe0vOTlHO5L3Pw==",
|
"integrity": "sha512-puCDz0CzydiSYOrnXpz/PKd69zRrribezjtE9yd4zvytoRc8+RY/KJPvtPFKZS3E3wP6neGyMe0vOTlHO5L3Pw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"command-line-usage": {
|
||||||
|
"version": "6.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/command-line-usage/-/command-line-usage-6.1.1.tgz",
|
||||||
|
"integrity": "sha512-F59pEuAR9o1SF/bD0dQBDluhpT4jJQNWUHEuVBqpDmCUo6gPjCi+m9fCWnWZVR/oG6cMTUms4h+3NPl74wGXvA==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"array-back": "^4.0.1",
|
||||||
|
"chalk": "^2.4.2",
|
||||||
|
"table-layout": "^1.0.1",
|
||||||
|
"typical": "^5.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"commander": {
|
"commander": {
|
||||||
"version": "2.20.3",
|
"version": "2.20.3",
|
||||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
|
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
|
||||||
@@ -1750,6 +1762,12 @@
|
|||||||
"ms": "2.1.2"
|
"ms": "2.1.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"deep-extend": {
|
||||||
|
"version": "0.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
|
||||||
|
"integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"define-properties": {
|
"define-properties": {
|
||||||
"version": "1.1.3",
|
"version": "1.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
|
||||||
@@ -1777,6 +1795,15 @@
|
|||||||
"integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==",
|
"integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"end-of-stream": {
|
||||||
|
"version": "1.4.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
|
||||||
|
"integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"once": "^1.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"enhanced-resolve": {
|
"enhanced-resolve": {
|
||||||
"version": "4.3.0",
|
"version": "4.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.3.0.tgz",
|
||||||
@@ -1804,9 +1831,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"errno": {
|
"errno": {
|
||||||
"version": "0.1.8",
|
"version": "0.1.7",
|
||||||
"resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz",
|
"resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz",
|
||||||
"integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==",
|
"integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"prr": "~1.0.1"
|
"prr": "~1.0.1"
|
||||||
@@ -1870,19 +1897,19 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"execa": {
|
"execa": {
|
||||||
"version": "5.0.0",
|
"version": "4.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/execa/-/execa-5.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz",
|
||||||
"integrity": "sha512-ov6w/2LCiuyO4RLYGdpFGjkcs0wMTgGE8PrkTHikeUy5iJekXyPIKUjifk5CsE0pt7sMCrMZ3YNqoCj6idQOnQ==",
|
"integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"cross-spawn": "^7.0.3",
|
"cross-spawn": "^7.0.0",
|
||||||
"get-stream": "^6.0.0",
|
"get-stream": "^5.0.0",
|
||||||
"human-signals": "^2.1.0",
|
"human-signals": "^1.1.1",
|
||||||
"is-stream": "^2.0.0",
|
"is-stream": "^2.0.0",
|
||||||
"merge-stream": "^2.0.0",
|
"merge-stream": "^2.0.0",
|
||||||
"npm-run-path": "^4.0.1",
|
"npm-run-path": "^4.0.0",
|
||||||
"onetime": "^5.1.2",
|
"onetime": "^5.1.0",
|
||||||
"signal-exit": "^3.0.3",
|
"signal-exit": "^3.0.2",
|
||||||
"strip-final-newline": "^2.0.0"
|
"strip-final-newline": "^2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -1898,12 +1925,6 @@
|
|||||||
"integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
|
"integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"fastest-levenshtein": {
|
|
||||||
"version": "1.0.12",
|
|
||||||
"resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz",
|
|
||||||
"integrity": "sha512-On2N+BpYJ15xIC974QNVuYGMOlEVt4s0EOI3wwMqOmK1fdDY+FN/zltPV8vosq4ad4c/gJ1KHScUn/6AWIgiow==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"file-loader": {
|
"file-loader": {
|
||||||
"version": "6.2.0",
|
"version": "6.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz",
|
||||||
@@ -1992,10 +2013,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"get-stream": {
|
"get-stream": {
|
||||||
"version": "6.0.0",
|
"version": "5.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
|
||||||
"integrity": "sha512-A1B3Bh1UmL0bidM/YX2NsCOTnGJePL9rO/M+Mw3m9f2gUpfokS0hi5Eah0WSUEWZdZhIZtMjkIYS7mDfOqNHbg==",
|
"integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"pump": "^3.0.0"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"glob-to-regexp": {
|
"glob-to-regexp": {
|
||||||
"version": "0.4.1",
|
"version": "0.4.1",
|
||||||
@@ -2055,9 +2079,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"human-signals": {
|
"human-signals": {
|
||||||
"version": "2.1.0",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz",
|
||||||
"integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==",
|
"integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"import-local": {
|
"import-local": {
|
||||||
@@ -2185,6 +2209,12 @@
|
|||||||
"minimist": "^1.2.5"
|
"minimist": "^1.2.5"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"leven": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"loader-runner": {
|
"loader-runner": {
|
||||||
"version": "4.1.0",
|
"version": "4.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.1.0.tgz",
|
||||||
@@ -2287,18 +2317,18 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"mime-db": {
|
"mime-db": {
|
||||||
"version": "1.45.0",
|
"version": "1.44.0",
|
||||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.45.0.tgz",
|
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz",
|
||||||
"integrity": "sha512-CkqLUxUk15hofLoLyljJSrukZi8mAtgd+yE5uO4tqRZsdsAJKv0O+rFMhVDRJgozy+yG6md5KwuXhD4ocIoP+w==",
|
"integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"mime-types": {
|
"mime-types": {
|
||||||
"version": "2.1.28",
|
"version": "2.1.27",
|
||||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.28.tgz",
|
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz",
|
||||||
"integrity": "sha512-0TO2yJ5YHYr7M2zzT7gDU1tbwHxEUWBCLt0lscSNpcdAfFyJOVEpRYNS7EXVcTLNj/25QO8gulHC5JtTzSE2UQ==",
|
"integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"mime-db": "1.45.0"
|
"mime-db": "1.44.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"mimic-fn": {
|
"mimic-fn": {
|
||||||
@@ -2358,6 +2388,15 @@
|
|||||||
"object-keys": "^1.1.1"
|
"object-keys": "^1.1.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"once": {
|
||||||
|
"version": "1.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||||
|
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"wrappy": "1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"onetime": {
|
"onetime": {
|
||||||
"version": "5.1.2",
|
"version": "5.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
|
||||||
@@ -2442,6 +2481,16 @@
|
|||||||
"integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=",
|
"integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"pump": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"end-of-stream": "^1.1.0",
|
||||||
|
"once": "^1.3.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"punycode": {
|
"punycode": {
|
||||||
"version": "2.1.1",
|
"version": "2.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
|
||||||
@@ -2481,6 +2530,12 @@
|
|||||||
"resolve": "^1.9.0"
|
"resolve": "^1.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"reduce-flatten": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/reduce-flatten/-/reduce-flatten-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-EJ4UNY/U1t2P/2k6oqotuX2Cc3T6nxJwsM0N0asT7dhrtH1ltUxDn4NalSYmPE2rCkVpcf/X6R0wDwcFpzhd4w==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"regenerate": {
|
"regenerate": {
|
||||||
"version": "1.4.2",
|
"version": "1.4.2",
|
||||||
"resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz",
|
"resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz",
|
||||||
@@ -2700,6 +2755,18 @@
|
|||||||
"has-flag": "^3.0.0"
|
"has-flag": "^3.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"table-layout": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/table-layout/-/table-layout-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-dEquqYNJiGwY7iPfZ3wbXDI944iqanTSchrACLL2nOB+1r+h1Nzu2eH+DuPPvWvm5Ry7iAPeFlgEtP5bIp5U7Q==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"array-back": "^4.0.1",
|
||||||
|
"deep-extend": "~0.6.0",
|
||||||
|
"typical": "^5.2.0",
|
||||||
|
"wordwrapjs": "^4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"tapable": {
|
"tapable": {
|
||||||
"version": "1.1.3",
|
"version": "1.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz",
|
||||||
@@ -2789,9 +2856,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"ts-loader": {
|
"ts-loader": {
|
||||||
"version": "8.0.13",
|
"version": "8.0.12",
|
||||||
"resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-8.0.13.tgz",
|
"resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-8.0.12.tgz",
|
||||||
"integrity": "sha512-1o1nO6aqouA23d2nlcMSEyPMAWRhnYUU0EQUJSc60E0TUyBNX792RHFYUN1ZM29vhMUNayrsbj2UVdZwKhXCDA==",
|
"integrity": "sha512-UIivVfGVJDdwwjgSrbtcL9Nf10c1BWnL1mxAQUVcnhNIn/P9W3nP5v60Z0aBMtc7ZrE11lMmU6+5jSgAXmGaYw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"chalk": "^2.3.0",
|
"chalk": "^2.3.0",
|
||||||
@@ -2815,6 +2882,12 @@
|
|||||||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
|
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"typical": {
|
||||||
|
"version": "5.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/typical/-/typical-5.2.0.tgz",
|
||||||
|
"integrity": "sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"unicode-canonical-property-names-ecmascript": {
|
"unicode-canonical-property-names-ecmascript": {
|
||||||
"version": "1.0.4",
|
"version": "1.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz",
|
||||||
@@ -2875,9 +2948,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"webpack": {
|
"webpack": {
|
||||||
"version": "5.11.1",
|
"version": "5.11.0",
|
||||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.11.1.tgz",
|
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.11.0.tgz",
|
||||||
"integrity": "sha512-tNUIdAmYJv+nupRs/U/gqmADm6fgrf5xE+rSlSsf2PgsGO7j2WG7ccU6AWNlOJlHFl+HnmXlBmHIkiLf+XA9mQ==",
|
"integrity": "sha512-ubWv7iP54RqAC/VjixgpnLLogCFbAfSOREcSWnnOlZEU8GICC5eKmJSu6YEnph2N2amKqY9rvxSwgyHxVqpaRw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@types/eslint-scope": "^3.7.0",
|
"@types/eslint-scope": "^3.7.0",
|
||||||
@@ -2907,13 +2980,13 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"enhanced-resolve": {
|
"enhanced-resolve": {
|
||||||
"version": "5.4.1",
|
"version": "5.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.4.0.tgz",
|
||||||
"integrity": "sha512-4GbyIMzYktTFoRSmkbgZ1LU+RXwf4AQ8Z+rSuuh1dC8plp0PPeaWvx6+G4hh4KnUJ48VoxKbNyA1QQQIUpXjYA==",
|
"integrity": "sha512-ZmqfWURB2lConOBM1JdCVfPyMRv5RdKWktLXO6123p97ovVm2CLBgw9t5MBj3jJWA6eHyOeIws9iJQoGFR4euQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"graceful-fs": "^4.2.4",
|
"graceful-fs": "^4.2.4",
|
||||||
"tapable": "^2.2.0"
|
"tapable": "^2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"find-up": {
|
"find-up": {
|
||||||
@@ -3056,21 +3129,21 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"webpack-cli": {
|
"webpack-cli": {
|
||||||
"version": "4.3.1",
|
"version": "4.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.2.0.tgz",
|
||||||
"integrity": "sha512-/F4+9QNZM/qKzzL9/06Am8NXIkGV+/NqQ62Dx7DSqudxxpAgBqYn6V7+zp+0Y7JuWksKUbczRY3wMTd+7Uj6OA==",
|
"integrity": "sha512-EIl3k88vaF4fSxWSgtAQR+VwicfLMTZ9amQtqS4o+TDPW9HGaEpbFBbAZ4A3ZOT5SOnMxNOzROsSTPiE8tBJPA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@discoveryjs/json-ext": "^0.5.0",
|
"@webpack-cli/info": "^1.1.0",
|
||||||
"@webpack-cli/info": "^1.2.1",
|
"@webpack-cli/serve": "^1.1.0",
|
||||||
"@webpack-cli/serve": "^1.2.1",
|
|
||||||
"colorette": "^1.2.1",
|
"colorette": "^1.2.1",
|
||||||
|
"command-line-usage": "^6.1.0",
|
||||||
"commander": "^6.2.0",
|
"commander": "^6.2.0",
|
||||||
"enquirer": "^2.3.6",
|
"enquirer": "^2.3.6",
|
||||||
"execa": "^5.0.0",
|
"execa": "^4.1.0",
|
||||||
"fastest-levenshtein": "^1.0.12",
|
|
||||||
"import-local": "^3.0.2",
|
"import-local": "^3.0.2",
|
||||||
"interpret": "^2.2.0",
|
"interpret": "^2.2.0",
|
||||||
|
"leven": "^3.1.0",
|
||||||
"rechoir": "^0.7.0",
|
"rechoir": "^0.7.0",
|
||||||
"v8-compile-cache": "^2.2.0",
|
"v8-compile-cache": "^2.2.0",
|
||||||
"webpack-merge": "^4.2.2"
|
"webpack-merge": "^4.2.2"
|
||||||
@@ -3120,6 +3193,22 @@
|
|||||||
"isexe": "^2.0.0"
|
"isexe": "^2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"wordwrapjs": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/wordwrapjs/-/wordwrapjs-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-Svqw723a3R34KvsMgpjFBYCgNOSdcW3mQFK4wIfhGQhtaFVOJmdYoXgi63ne3dTlWgatVcUc7t4HtQ/+bUVIzQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"reduce-flatten": "^2.0.0",
|
||||||
|
"typical": "^5.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"wrappy": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||||
|
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"ws": {
|
"ws": {
|
||||||
"version": "7.4.1",
|
"version": "7.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/ws/-/ws-7.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/ws/-/ws-7.4.1.tgz",
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@excalidraw/utils",
|
"name": "@excalidraw/utils",
|
||||||
|
"sideEffects": false,
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"main": "dist/excalidraw-utils.min.js",
|
"main": "dist/excalidraw-utils.min.js",
|
||||||
"files": [
|
"files": [
|
||||||
@@ -45,10 +46,10 @@
|
|||||||
"babel-plugin-transform-class-properties": "6.24.1",
|
"babel-plugin-transform-class-properties": "6.24.1",
|
||||||
"cross-env": "7.0.3",
|
"cross-env": "7.0.3",
|
||||||
"file-loader": "6.2.0",
|
"file-loader": "6.2.0",
|
||||||
"ts-loader": "8.0.13",
|
"ts-loader": "8.0.12",
|
||||||
"webpack": "5.11.1",
|
"webpack": "5.11.0",
|
||||||
"webpack-bundle-analyzer": "4.3.0",
|
"webpack-bundle-analyzer": "4.3.0",
|
||||||
"webpack-cli": "4.3.1"
|
"webpack-cli": "4.2.0"
|
||||||
},
|
},
|
||||||
"bugs": "https://github.com/excalidraw/excalidraw/issues",
|
"bugs": "https://github.com/excalidraw/excalidraw/issues",
|
||||||
"repository": "https://github.com/excalidraw/excalidraw",
|
"repository": "https://github.com/excalidraw/excalidraw",
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
import {
|
import {
|
||||||
exportToCanvas as _exportToCanvas,
|
exportToCanvas as _exportToCanvas,
|
||||||
exportToSvg as _exportToSvg,
|
exportToSvg as _exportToSvg,
|
||||||
} from "../scene/export";
|
} from "../../scene/export";
|
||||||
import { getDefaultAppState } from "../appState";
|
import { getDefaultAppState } from "../../appState";
|
||||||
import { AppState } from "../types";
|
import { AppState } from "../../types";
|
||||||
import { ExcalidrawElement } from "../element/types";
|
import { ExcalidrawElement } from "../../element/types";
|
||||||
import { getNonDeletedElements } from "../element";
|
import { getNonDeletedElements } from "../../element";
|
||||||
import { SCENE_NAME_FALLBACK } from "../constants";
|
|
||||||
|
|
||||||
type ExportOpts = {
|
type ExportOpts = {
|
||||||
elements: readonly ExcalidrawElement[];
|
elements: readonly ExcalidrawElement[];
|
||||||
@@ -19,7 +18,7 @@ type ExportOpts = {
|
|||||||
|
|
||||||
export const exportToCanvas = ({
|
export const exportToCanvas = ({
|
||||||
elements,
|
elements,
|
||||||
appState = { ...getDefaultAppState(), name: SCENE_NAME_FALLBACK },
|
appState = getDefaultAppState(),
|
||||||
getDimensions = (width, height) => ({ width, height, scale: 1 }),
|
getDimensions = (width, height) => ({ width, height, scale: 1 }),
|
||||||
}: ExportOpts) => {
|
}: ExportOpts) => {
|
||||||
return _exportToCanvas(
|
return _exportToCanvas(
|
||||||
@@ -75,7 +74,7 @@ export const exportToBlob = (
|
|||||||
|
|
||||||
export const exportToSvg = ({
|
export const exportToSvg = ({
|
||||||
elements,
|
elements,
|
||||||
appState = { ...getDefaultAppState(), name: SCENE_NAME_FALLBACK },
|
appState = getDefaultAppState(),
|
||||||
exportPadding,
|
exportPadding,
|
||||||
metadata,
|
metadata,
|
||||||
}: ExportOpts & {
|
}: ExportOpts & {
|
||||||
@@ -13,7 +13,7 @@ module.exports = {
|
|||||||
libraryTarget: "umd",
|
libraryTarget: "umd",
|
||||||
},
|
},
|
||||||
resolve: {
|
resolve: {
|
||||||
extensions: [".tsx", ".ts", ".js"],
|
extensions: [".ts", ".js"],
|
||||||
},
|
},
|
||||||
optimization: {
|
optimization: {
|
||||||
runtimeChunk: false,
|
runtimeChunk: false,
|
||||||
@@ -40,4 +40,18 @@ module.exports = {
|
|||||||
}),
|
}),
|
||||||
...(process.env.ANALYZER === "true" ? [new BundleAnalyzerPlugin()] : []),
|
...(process.env.ANALYZER === "true" ? [new BundleAnalyzerPlugin()] : []),
|
||||||
],
|
],
|
||||||
|
externals: {
|
||||||
|
react: {
|
||||||
|
root: "React",
|
||||||
|
commonjs2: "react",
|
||||||
|
commonjs: "react",
|
||||||
|
amd: "react",
|
||||||
|
},
|
||||||
|
"react-dom": {
|
||||||
|
root: "ReactDOM",
|
||||||
|
commonjs2: "react-dom",
|
||||||
|
commonjs: "react-dom",
|
||||||
|
amd: "react-dom",
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
+21
-6
@@ -7,7 +7,6 @@ import { renderScene, renderSceneToSvg } from "../renderer/renderScene";
|
|||||||
import { distance, SVG_NS } from "../utils";
|
import { distance, SVG_NS } from "../utils";
|
||||||
import { normalizeScroll } from "./scroll";
|
import { normalizeScroll } from "./scroll";
|
||||||
import { AppState } from "../types";
|
import { AppState } from "../types";
|
||||||
import { t } from "../i18n";
|
|
||||||
import { DEFAULT_FONT_FAMILY, DEFAULT_VERTICAL_ALIGN } from "../constants";
|
import { DEFAULT_FONT_FAMILY, DEFAULT_VERTICAL_ALIGN } from "../constants";
|
||||||
import { getDefaultAppState } from "../appState";
|
import { getDefaultAppState } from "../appState";
|
||||||
|
|
||||||
@@ -23,12 +22,14 @@ export const exportToCanvas = (
|
|||||||
viewBackgroundColor,
|
viewBackgroundColor,
|
||||||
scale = 1,
|
scale = 1,
|
||||||
shouldAddWatermark,
|
shouldAddWatermark,
|
||||||
|
watermarkText,
|
||||||
}: {
|
}: {
|
||||||
exportBackground: boolean;
|
exportBackground: boolean;
|
||||||
exportPadding?: number;
|
exportPadding?: number;
|
||||||
scale?: number;
|
scale?: number;
|
||||||
viewBackgroundColor: string;
|
viewBackgroundColor: string;
|
||||||
shouldAddWatermark: boolean;
|
shouldAddWatermark: boolean;
|
||||||
|
watermarkText?: string;
|
||||||
},
|
},
|
||||||
createCanvas: (width: number, height: number) => HTMLCanvasElement = (
|
createCanvas: (width: number, height: number) => HTMLCanvasElement = (
|
||||||
width,
|
width,
|
||||||
@@ -40,7 +41,11 @@ export const exportToCanvas = (
|
|||||||
return tempCanvas;
|
return tempCanvas;
|
||||||
},
|
},
|
||||||
) => {
|
) => {
|
||||||
const sceneElements = getElementsAndWatermark(elements, shouldAddWatermark);
|
const sceneElements = getElementsAndWatermark(
|
||||||
|
elements,
|
||||||
|
shouldAddWatermark,
|
||||||
|
watermarkText,
|
||||||
|
);
|
||||||
|
|
||||||
const [minX, minY, width, height] = getCanvasSize(
|
const [minX, minY, width, height] = getCanvasSize(
|
||||||
sceneElements,
|
sceneElements,
|
||||||
@@ -152,20 +157,25 @@ export const exportToSvg = (
|
|||||||
const getElementsAndWatermark = (
|
const getElementsAndWatermark = (
|
||||||
elements: readonly NonDeletedExcalidrawElement[],
|
elements: readonly NonDeletedExcalidrawElement[],
|
||||||
shouldAddWatermark: boolean,
|
shouldAddWatermark: boolean,
|
||||||
|
watermarkText?: string,
|
||||||
): readonly NonDeletedExcalidrawElement[] => {
|
): readonly NonDeletedExcalidrawElement[] => {
|
||||||
let _elements = [...elements];
|
let _elements = [...elements];
|
||||||
|
|
||||||
if (shouldAddWatermark) {
|
if (shouldAddWatermark) {
|
||||||
const [, , maxX, maxY] = getCommonBounds(elements);
|
const [, , maxX, maxY] = getCommonBounds(elements);
|
||||||
_elements = [..._elements, getWatermarkElement(maxX, maxY)];
|
_elements = [..._elements, getWatermarkElement(maxX, maxY, watermarkText)];
|
||||||
}
|
}
|
||||||
|
|
||||||
return _elements;
|
return _elements;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getWatermarkElement = (maxX: number, maxY: number) => {
|
const getWatermarkElement = (
|
||||||
|
maxX: number,
|
||||||
|
maxY: number,
|
||||||
|
watermarkText: string = "",
|
||||||
|
) => {
|
||||||
return newTextElement({
|
return newTextElement({
|
||||||
text: t("labels.madeWithExcalidraw"),
|
text: watermarkText,
|
||||||
fontSize: WATERMARK_HEIGHT,
|
fontSize: WATERMARK_HEIGHT,
|
||||||
fontFamily: DEFAULT_FONT_FAMILY,
|
fontFamily: DEFAULT_FONT_FAMILY,
|
||||||
textAlign: "right",
|
textAlign: "right",
|
||||||
@@ -204,8 +214,13 @@ export const getExportSize = (
|
|||||||
exportPadding: number,
|
exportPadding: number,
|
||||||
shouldAddWatermark: boolean,
|
shouldAddWatermark: boolean,
|
||||||
scale: number,
|
scale: number,
|
||||||
|
watermarkText?: string,
|
||||||
): [number, number] => {
|
): [number, number] => {
|
||||||
const sceneElements = getElementsAndWatermark(elements, shouldAddWatermark);
|
const sceneElements = getElementsAndWatermark(
|
||||||
|
elements,
|
||||||
|
shouldAddWatermark,
|
||||||
|
watermarkText,
|
||||||
|
);
|
||||||
|
|
||||||
const [, , width, height] = getCanvasSize(
|
const [, , width, height] = getCanvasSize(
|
||||||
sceneElements,
|
sceneElements,
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -2,7 +2,7 @@ import React from "react";
|
|||||||
import ReactDOM from "react-dom";
|
import ReactDOM from "react-dom";
|
||||||
import { render } from "./test-utils";
|
import { render } from "./test-utils";
|
||||||
import ExcalidrawApp from "../excalidraw-app";
|
import ExcalidrawApp from "../excalidraw-app";
|
||||||
import { defaultLang, setLanguage } from "../i18n";
|
import { setLanguage } from "../i18n";
|
||||||
import { UI, Pointer, Keyboard } from "./helpers/ui";
|
import { UI, Pointer, Keyboard } from "./helpers/ui";
|
||||||
import { API } from "./helpers/api";
|
import { API } from "./helpers/api";
|
||||||
import { KEYS } from "../keys";
|
import { KEYS } from "../keys";
|
||||||
@@ -60,7 +60,7 @@ describe("aligning", () => {
|
|||||||
ReactDOM.unmountComponentAtNode(document.getElementById("root")!);
|
ReactDOM.unmountComponentAtNode(document.getElementById("root")!);
|
||||||
mouse.reset();
|
mouse.reset();
|
||||||
|
|
||||||
await setLanguage(defaultLang);
|
await setLanguage("en.json");
|
||||||
await render(<ExcalidrawApp />);
|
await render(<ExcalidrawApp />);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -4,8 +4,9 @@ import ReactDOM from "react-dom";
|
|||||||
import { copiedStyles } from "../actions/actionStyles";
|
import { copiedStyles } from "../actions/actionStyles";
|
||||||
import { ShortcutName } from "../actions/shortcuts";
|
import { ShortcutName } from "../actions/shortcuts";
|
||||||
import { ExcalidrawElement } from "../element/types";
|
import { ExcalidrawElement } from "../element/types";
|
||||||
|
import { setLanguage } from "../i18n";
|
||||||
import { CODES, KEYS } from "../keys";
|
import { CODES, KEYS } from "../keys";
|
||||||
import ExcalidrawApp from "../excalidraw-app";
|
import Excalidraw from "../packages/excalidraw/index";
|
||||||
import { reseed } from "../random";
|
import { reseed } from "../random";
|
||||||
import * as Renderer from "../renderer/renderScene";
|
import * as Renderer from "../renderer/renderScene";
|
||||||
import { setDateTimeForTests } from "../utils";
|
import { setDateTimeForTests } from "../utils";
|
||||||
@@ -18,7 +19,6 @@ import {
|
|||||||
screen,
|
screen,
|
||||||
waitFor,
|
waitFor,
|
||||||
} from "./test-utils";
|
} from "./test-utils";
|
||||||
import { defaultLang } from "../i18n";
|
|
||||||
|
|
||||||
const { h } = window;
|
const { h } = window;
|
||||||
|
|
||||||
@@ -75,7 +75,8 @@ beforeEach(async () => {
|
|||||||
finger1.reset();
|
finger1.reset();
|
||||||
finger2.reset();
|
finger2.reset();
|
||||||
|
|
||||||
await render(<ExcalidrawApp />);
|
await setLanguage("en.json");
|
||||||
|
await render(<Excalidraw offsetLeft={0} offsetTop={0} />);
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
@@ -438,7 +439,7 @@ describe("regression tests", () => {
|
|||||||
await waitFor(() => expect(screen.queryByTitle(/thin/i)).toBeNull());
|
await waitFor(() => expect(screen.queryByTitle(/thin/i)).toBeNull());
|
||||||
// reset language
|
// reset language
|
||||||
fireEvent.change(document.querySelector(".dropdown-select__language")!, {
|
fireEvent.change(document.querySelector(".dropdown-select__language")!, {
|
||||||
target: { value: defaultLang.code },
|
target: { value: "en" },
|
||||||
});
|
});
|
||||||
// switching back to English
|
// switching back to English
|
||||||
await waitFor(() => expect(screen.queryByTitle(/thin/i)).not.toBeNull());
|
await waitFor(() => expect(screen.queryByTitle(/thin/i)).not.toBeNull());
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import {
|
|||||||
GroupId,
|
GroupId,
|
||||||
ExcalidrawBindableElement,
|
ExcalidrawBindableElement,
|
||||||
Arrowhead,
|
Arrowhead,
|
||||||
ChartType,
|
|
||||||
} from "./element/types";
|
} from "./element/types";
|
||||||
import { SHAPES } from "./shapes";
|
import { SHAPES } from "./shapes";
|
||||||
import { Point as RoughPoint } from "roughjs/bin/geometry";
|
import { Point as RoughPoint } from "roughjs/bin/geometry";
|
||||||
@@ -18,8 +17,6 @@ import { SuggestedBinding } from "./element/binding";
|
|||||||
import { ImportedDataState } from "./data/types";
|
import { ImportedDataState } from "./data/types";
|
||||||
import { ExcalidrawImperativeAPI } from "./components/App";
|
import { ExcalidrawImperativeAPI } from "./components/App";
|
||||||
import type { ResolvablePromise } from "./utils";
|
import type { ResolvablePromise } from "./utils";
|
||||||
import { Spreadsheet } from "./charts";
|
|
||||||
import { Language } from "./i18n";
|
|
||||||
|
|
||||||
export type FlooredNumber = number & { _brand: "FlooredNumber" };
|
export type FlooredNumber = number & { _brand: "FlooredNumber" };
|
||||||
export type Point = Readonly<RoughPoint>;
|
export type Point = Readonly<RoughPoint>;
|
||||||
@@ -100,16 +97,6 @@ export type AppState = {
|
|||||||
fileHandle: import("browser-nativefs").FileSystemHandle | null;
|
fileHandle: import("browser-nativefs").FileSystemHandle | null;
|
||||||
collaborators: Map<string, Collaborator>;
|
collaborators: Map<string, Collaborator>;
|
||||||
showStats: boolean;
|
showStats: boolean;
|
||||||
currentChartType: ChartType;
|
|
||||||
pasteDialog:
|
|
||||||
| {
|
|
||||||
shown: false;
|
|
||||||
data: null;
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
shown: true;
|
|
||||||
data: Spreadsheet;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type NormalizedZoomValue = number & { _brand: "normalizedZoom" };
|
export type NormalizedZoomValue = number & { _brand: "normalizedZoom" };
|
||||||
@@ -182,8 +169,6 @@ export interface ExcalidrawProps {
|
|||||||
appState: AppState,
|
appState: AppState,
|
||||||
canvas: HTMLCanvasElement | null,
|
canvas: HTMLCanvasElement | null,
|
||||||
) => void;
|
) => void;
|
||||||
renderFooter?: (isMobile: boolean) => JSX.Element;
|
|
||||||
langCode?: Language["code"];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type SceneData = {
|
export type SceneData = {
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import { FontFamily, FontString } from "./element/types";
|
|||||||
import { Zoom } from "./types";
|
import { Zoom } from "./types";
|
||||||
import { unstable_batchedUpdates } from "react-dom";
|
import { unstable_batchedUpdates } from "react-dom";
|
||||||
import { isDarwin } from "./keys";
|
import { isDarwin } from "./keys";
|
||||||
import { t } from "./i18n";
|
|
||||||
|
|
||||||
export const SVG_NS = "http://www.w3.org/2000/svg";
|
export const SVG_NS = "http://www.w3.org/2000/svg";
|
||||||
|
|
||||||
@@ -33,10 +32,6 @@ export const getDateTime = () => {
|
|||||||
return `${year}-${month}-${day}-${hr}${min}`;
|
return `${year}-${month}-${day}-${hr}${min}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getNewSceneName = () => {
|
|
||||||
return `${t("labels.untitled")}-${getDateTime()}`;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const capitalizeString = (str: string) =>
|
export const capitalizeString = (str: string) =>
|
||||||
str.charAt(0).toUpperCase() + str.slice(1);
|
str.charAt(0).toUpperCase() + str.slice(1);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user