Compare commits

..

1 Commits

Author SHA1 Message Date
dwelle
0e3a5b2042 feat: cycle through selected elements on cmd/ctrl-click 2021-05-29 22:34:41 +02:00
110 changed files with 1326 additions and 2799 deletions

View File

@@ -2,8 +2,5 @@
"firestore": {
"rules": "firestore.rules",
"indexes": "firestore.indexes.json"
},
"storage": {
"rules": "storage.rules"
}
}

View File

@@ -1,12 +0,0 @@
rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
match /{migrations} {
match /{scenes}/{scene} {
allow get, write: if true;
// redundant, but let's be explicit'
allow list: if false;
}
}
}
}

View File

@@ -31,7 +31,6 @@
"clsx": "1.1.1",
"firebase": "8.3.3",
"i18next-browser-languagedetector": "6.1.0",
"idb-keyval": "5.0.6",
"lodash.throttle": "4.1.1",
"nanoid": "3.1.22",
"open-color": "1.8.0",

Binary file not shown.

View File

@@ -34,9 +34,9 @@ export const actionChangeViewBackgroundColor = register({
type="canvasBackground"
color={appState.viewBackgroundColor}
onChange={(color) => updateData({ viewBackgroundColor: color })}
isActive={appState.openPopup === "canvasColorPicker"}
isActive={appState.openMenu === "canvasColorPicker"}
setActive={(active) =>
updateData({ openPopup: active ? "canvasColorPicker" : null })
updateData({ openMenu: active ? "canvasColorPicker" : null })
}
data-testid="canvas-background-picker"
/>

View File

@@ -1,6 +1,6 @@
import React from "react";
import { trackEvent } from "../analytics";
import { load, questionCircle, saveAs } from "../components/icons";
import { load, questionCircle, save, saveAs } from "../components/icons";
import { ProjectName } from "../components/ProjectName";
import { ToolButton } from "../components/ToolButton";
import "../components/ToolIcon.scss";
@@ -13,12 +13,6 @@ import { KEYS } from "../keys";
import { register } from "./register";
import { supported as fsSupported } from "browser-fs-access";
import { CheckboxItem } from "../components/CheckboxItem";
import { getExportSize } from "../scene/export";
import { DEFAULT_EXPORT_PADDING, EXPORT_SCALES, IDB_KEYS } from "../constants";
import { getSelectedElements, isSomeElementSelected } from "../scene";
import { getNonDeletedElements } from "../element";
import { ActiveFile } from "../components/ActiveFile";
import * as idb from "idb-keyval";
export const actionChangeProjectName = register({
name: "changeProjectName",
@@ -38,54 +32,6 @@ export const actionChangeProjectName = register({
),
});
export const actionChangeExportScale = register({
name: "changeExportScale",
perform: (_elements, appState, value) => {
return {
appState: { ...appState, exportScale: value },
commitToHistory: false,
};
},
PanelComponent: ({ elements: allElements, appState, updateData }) => {
const elements = getNonDeletedElements(allElements);
const exportSelected = isSomeElementSelected(elements, appState);
const exportedElements = exportSelected
? getSelectedElements(elements, appState)
: elements;
return (
<>
{EXPORT_SCALES.map((s) => {
const [width, height] = getExportSize(
exportedElements,
DEFAULT_EXPORT_PADDING,
s,
);
const scaleButtonTitle = `${t(
"buttons.scale",
)} ${s}x (${width}x${height})`;
return (
<ToolButton
key={s}
size="s"
type="radio"
icon={`${s}x`}
name="export-canvas-scale"
title={scaleButtonTitle}
aria-label={scaleButtonTitle}
id="export-canvas-scale"
checked={s === appState.exportScale}
onChange={() => updateData(s)}
/>
);
})}
</>
);
},
});
export const actionChangeExportBackground = register({
name: "changeExportBackground",
perform: (_elements, appState, value) => {
@@ -150,30 +96,19 @@ export const actionSaveToActiveFile = register({
if (error?.name !== "AbortError") {
console.error(error);
}
if (fileHandleExists && error.name === "AbortError") {
try {
await idb.del(IDB_KEYS.fileHandle);
} catch (error) {
console.error(error);
}
return {
commitToHistory: false,
appState: { ...appState, fileHandle: null },
};
}
return {
commitToHistory: false,
};
return { commitToHistory: false };
}
},
keyTest: (event) =>
event.key === KEYS.S && event[KEYS.CTRL_OR_CMD] && !event.shiftKey,
PanelComponent: ({ updateData, appState }) => (
<ActiveFile
onSave={() => updateData(null)}
fileName={appState.fileHandle?.name}
PanelComponent: ({ updateData }) => (
<ToolButton
type="icon"
icon={save}
title={t("buttons.save")}
aria-label={t("buttons.save")}
onClick={() => updateData(null)}
data-testid="save-button"
/>
),
});
@@ -186,13 +121,6 @@ export const actionSaveFileToDisk = register({
...appState,
fileHandle: null,
});
try {
if (fileHandle) {
await idb.set(IDB_KEYS.fileHandle, fileHandle);
}
} catch (error) {
console.error(error);
}
return { commitToHistory: false, appState: { ...appState, fileHandle } };
} catch (error) {
if (error?.name !== "AbortError") {

View File

@@ -13,13 +13,6 @@ import {
FillCrossHatchIcon,
FillHachureIcon,
FillSolidIcon,
FontFamilyCodeIcon,
FontFamilyHandDrawnIcon,
FontFamilyNormalIcon,
FontSizeExtraLargeIcon,
FontSizeLargeIcon,
FontSizeMediumIcon,
FontSizeSmallIcon,
SloppinessArchitectIcon,
SloppinessArtistIcon,
SloppinessCartoonistIcon,
@@ -27,15 +20,18 @@ import {
StrokeStyleDottedIcon,
StrokeStyleSolidIcon,
StrokeWidthIcon,
TextAlignCenterIcon,
FontSizeSmallIcon,
FontSizeMediumIcon,
FontSizeLargeIcon,
FontSizeExtraLargeIcon,
FontFamilyHandDrawnIcon,
FontFamilyNormalIcon,
FontFamilyCodeIcon,
TextAlignLeftIcon,
TextAlignCenterIcon,
TextAlignRightIcon,
} from "../components/icons";
import {
DEFAULT_FONT_FAMILY,
DEFAULT_FONT_SIZE,
FONT_FAMILY,
} from "../constants";
import { DEFAULT_FONT_FAMILY, DEFAULT_FONT_SIZE } from "../constants";
import {
getNonDeletedElements,
isTextElement,
@@ -48,7 +44,7 @@ import {
ExcalidrawElement,
ExcalidrawLinearElement,
ExcalidrawTextElement,
FontFamilyValues,
FontFamily,
TextAlign,
} from "../element/types";
import { getLanguage, t } from "../i18n";
@@ -130,9 +126,9 @@ export const actionChangeStrokeColor = register({
appState.currentItemStrokeColor,
)}
onChange={(color) => updateData({ currentItemStrokeColor: color })}
isActive={appState.openPopup === "strokeColorPicker"}
isActive={appState.openMenu === "strokeColorPicker"}
setActive={(active) =>
updateData({ openPopup: active ? "strokeColorPicker" : null })
updateData({ openMenu: active ? "strokeColorPicker" : null })
}
/>
</>
@@ -170,9 +166,9 @@ export const actionChangeBackgroundColor = register({
appState.currentItemBackgroundColor,
)}
onChange={(color) => updateData({ currentItemBackgroundColor: color })}
isActive={appState.openPopup === "backgroundColorPicker"}
isActive={appState.openMenu === "backgroundColorPicker"}
setActive={(active) =>
updateData({ openPopup: active ? "backgroundColorPicker" : null })
updateData({ openMenu: active ? "backgroundColorPicker" : null })
}
/>
</>
@@ -503,23 +499,19 @@ export const actionChangeFontFamily = register({
};
},
PanelComponent: ({ elements, appState, updateData }) => {
const options: {
value: FontFamilyValues;
text: string;
icon: JSX.Element;
}[] = [
const options: { value: FontFamily; text: string; icon: JSX.Element }[] = [
{
value: FONT_FAMILY.Virgil,
value: 1,
text: t("labels.handDrawn"),
icon: <FontFamilyHandDrawnIcon theme={appState.theme} />,
},
{
value: FONT_FAMILY.Helvetica,
value: 2,
text: t("labels.normal"),
icon: <FontFamilyNormalIcon theme={appState.theme} />,
},
{
value: FONT_FAMILY.Cascadia,
value: 3,
text: t("labels.code"),
icon: <FontFamilyCodeIcon theme={appState.theme} />,
},
@@ -528,7 +520,7 @@ export const actionChangeFontFamily = register({
return (
<fieldset>
<legend>{t("labels.fontFamily")}</legend>
<ButtonIconSelect<FontFamilyValues | false>
<ButtonIconSelect<FontFamily | false>
group="font-family"
options={options}
value={getFormValue(

View File

@@ -66,7 +66,6 @@ export type ActionName =
| "changeProjectName"
| "changeExportBackground"
| "changeExportEmbedScene"
| "changeExportScale"
| "saveToActiveFile"
| "saveFileToDisk"
| "loadScene"

View File

@@ -3,16 +3,11 @@ import {
DEFAULT_FONT_FAMILY,
DEFAULT_FONT_SIZE,
DEFAULT_TEXT_ALIGN,
EXPORT_SCALES,
} from "./constants";
import { t } from "./i18n";
import { AppState, NormalizedZoomValue } from "./types";
import { getDateTime } from "./utils";
const defaultExportScale = EXPORT_SCALES.includes(devicePixelRatio)
? devicePixelRatio
: 1;
export const getDefaultAppState = (): Omit<
AppState,
"offsetTop" | "offsetLeft" | "width" | "height"
@@ -44,7 +39,6 @@ export const getDefaultAppState = (): Omit<
elementType: "selection",
errorMessage: null,
exportBackground: true,
exportScale: defaultExportScale,
exportEmbedScene: false,
exportWithDarkMode: false,
fileHandle: null,
@@ -58,7 +52,6 @@ export const getDefaultAppState = (): Omit<
multiElement: null,
name: `${t("labels.untitled")}-${getDateTime()}`,
openMenu: null,
openPopup: null,
pasteDialog: { shown: false, data: null },
previousSelectedElementIds: {},
resizingElement: null,
@@ -123,7 +116,6 @@ const APP_STATE_STORAGE_CONF = (<
errorMessage: { browser: false, export: false },
exportBackground: { browser: true, export: false },
exportEmbedScene: { browser: true, export: false },
exportScale: { browser: true, export: false },
exportWithDarkMode: { browser: true, export: false },
fileHandle: { browser: false, export: false },
gridSize: { browser: true, export: true },
@@ -139,7 +131,6 @@ const APP_STATE_STORAGE_CONF = (<
offsetLeft: { browser: false, export: false },
offsetTop: { browser: false, export: false },
openMenu: { browser: true, export: false },
openPopup: { browser: false, export: false },
pasteDialog: { browser: false, export: false },
previousSelectedElementIds: { browser: true, export: false },
resizingElement: { browser: false, export: false },

View File

@@ -151,14 +151,23 @@ export const SelectedShapeActions = ({
);
};
const LIBRARY_ICON = (
// fa-th-large
<svg viewBox="0 0 512 512">
<path d="M296 32h192c13.255 0 24 10.745 24 24v160c0 13.255-10.745 24-24 24H296c-13.255 0-24-10.745-24-24V56c0-13.255 10.745-24 24-24zm-80 0H24C10.745 32 0 42.745 0 56v160c0 13.255 10.745 24 24 24h192c13.255 0 24-10.745 24-24V56c0-13.255-10.745-24-24-24zM0 296v160c0 13.255 10.745 24 24 24h192c13.255 0 24-10.745 24-24V296c0-13.255-10.745-24-24-24H24c-13.255 0-24 10.745-24 24zm296 184h192c13.255 0 24-10.745 24-24V296c0-13.255-10.745-24-24-24H296c-13.255 0-24 10.745-24 24v160c0 13.255 10.745 24 24 24z" />
</svg>
);
export const ShapesSwitcher = ({
canvas,
elementType,
setAppState,
isLibraryOpen,
}: {
canvas: HTMLCanvasElement | null;
elementType: ExcalidrawElement["type"];
setAppState: React.Component<any, AppState>["setState"];
isLibraryOpen: boolean;
}) => (
<>
{SHAPES.map(({ value, icon, key }, index) => {
@@ -192,6 +201,19 @@ export const ShapesSwitcher = ({
/>
);
})}
<ToolButton
className="Shape ToolIcon_type_button__library"
type="button"
icon={LIBRARY_ICON}
name="editor-library"
keyBindingLabel="9"
aria-keyshortcuts="9"
title={`${capitalizeString(t("toolBar.library"))} — 9`}
aria-label={capitalizeString(t("toolBar.library"))}
onClick={() => {
setAppState({ isLibraryOpen: !isLibraryOpen });
}}
/>
</>
);

View File

@@ -1,21 +0,0 @@
.excalidraw {
.ActiveFile {
.ActiveFile__fileName {
display: flex;
align-items: center;
span {
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
width: 9.3em;
}
svg {
width: 1.15em;
margin-inline-end: 0.3em;
transform: scaleY(0.9);
}
}
}
}

View File

@@ -1,29 +0,0 @@
import React from "react";
import Stack from "../components/Stack";
import { ToolButton } from "../components/ToolButton";
import { save, file } from "../components/icons";
import { t } from "../i18n";
import "./ActiveFile.scss";
type ActiveFileProps = {
fileName?: string;
onSave: () => void;
};
export const ActiveFile = ({ fileName, onSave }: ActiveFileProps) => (
<Stack.Row className="ActiveFile" gap={1} align="center">
<span className="ActiveFile__fileName">
{file}
<span>{fileName}</span>
</span>
<ToolButton
type="icon"
icon={save}
title={t("buttons.save")}
aria-label={t("buttons.save")}
onClick={onSave}
data-testid="save-button"
/>
</Stack.Row>
);

View File

@@ -52,7 +52,6 @@ import {
ENV,
EVENT,
GRID_SIZE,
IDB_KEYS,
LINE_CONFIRM_THRESHOLD,
MIME_TYPES,
MQ_MAX_HEIGHT_LANDSCAPE,
@@ -112,6 +111,7 @@ import {
import { LinearElementEditor } from "../element/linearElementEditor";
import { mutateElement } from "../element/mutateElement";
import { deepCopyElement, newFreeDrawElement } from "../element/newElement";
import { MaybeTransformHandleType } from "../element/transformHandles";
import {
isBindingElement,
isBindingElementType,
@@ -167,11 +167,9 @@ import { findShapeByKey } from "../shapes";
import {
AppProps,
AppState,
ExcalidrawImperativeAPI,
Gesture,
GestureEvent,
LibraryItems,
PointerDownState,
SceneData,
} from "../types";
import {
@@ -182,6 +180,7 @@ import {
isToolIcon,
isWritableElement,
resetCursor,
ResolvablePromise,
resolvablePromise,
sceneCoordsToViewportCoords,
setCursor,
@@ -195,14 +194,12 @@ import LayerUI from "./LayerUI";
import { Stats } from "./Stats";
import { Toast } from "./Toast";
import { actionToggleViewMode } from "../actions/actionToggleViewMode";
import * as idb from "idb-keyval";
const IsMobileContext = React.createContext(false);
export const useIsMobile = () => useContext(IsMobileContext);
const ExcalidrawContainerContext = React.createContext<{
container: HTMLDivElement | null;
id: string | null;
}>({ container: null, id: null });
const ExcalidrawContainerContext = React.createContext<HTMLDivElement | null>(
null,
);
export const useExcalidrawContainer = () =>
useContext(ExcalidrawContainerContext);
@@ -225,6 +222,83 @@ const gesture: Gesture = {
initialScale: null,
};
export type PointerDownState = Readonly<{
// The first position at which pointerDown happened
origin: Readonly<{ x: number; y: number }>;
// Same as "origin" but snapped to the grid, if grid is on
originInGrid: Readonly<{ x: number; y: number }>;
// Scrollbar checks
scrollbars: ReturnType<typeof isOverScrollBars>;
// The previous pointer position
lastCoords: { x: number; y: number };
// map of original elements data
originalElements: Map<string, NonDeleted<ExcalidrawElement>>;
resize: {
// Handle when resizing, might change during the pointer interaction
handleType: MaybeTransformHandleType;
// This is determined on the initial pointer down event
isResizing: boolean;
// This is determined on the initial pointer down event
offset: { x: number; y: number };
// This is determined on the initial pointer down event
arrowDirection: "origin" | "end";
// This is a center point of selected elements determined on the initial pointer down event (for rotation only)
center: { x: number; y: number };
};
hit: {
// The element the pointer is "hitting", is determined on the initial
// pointer down event
element: NonDeleted<ExcalidrawElement> | null;
// The elements the pointer is "hitting", is determined on the initial
// pointer down event
allHitElements: NonDeleted<ExcalidrawElement>[];
// This is determined on the initial pointer down event
wasAddedToSelection: boolean;
// Whether selected element(s) were duplicated, might change during the
// pointer interaction
hasBeenDuplicated: boolean;
hasHitCommonBoundingBoxOfSelectedElements: boolean;
};
withCmdOrCtrl: boolean;
drag: {
// Might change during the pointer interation
hasOccurred: boolean;
// Might change during the pointer interation
offset: { x: number; y: number } | null;
};
// We need to have these in the state so that we can unsubscribe them
eventListeners: {
// It's defined on the initial pointer down event
onMove: null | ((event: PointerEvent) => void);
// It's defined on the initial pointer down event
onUp: null | ((event: PointerEvent) => void);
// It's defined on the initial pointer down event
onKeyDown: null | ((event: KeyboardEvent) => void);
// It's defined on the initial pointer down event
onKeyUp: null | ((event: KeyboardEvent) => void);
};
}>;
export type ExcalidrawImperativeAPI = {
updateScene: InstanceType<typeof App>["updateScene"];
resetScene: InstanceType<typeof App>["resetScene"];
getSceneElementsIncludingDeleted: InstanceType<
typeof App
>["getSceneElementsIncludingDeleted"];
history: {
clear: InstanceType<typeof App>["resetHistory"];
};
scrollToContent: InstanceType<typeof App>["scrollToContent"];
getSceneElements: InstanceType<typeof App>["getSceneElements"];
getAppState: () => InstanceType<typeof App>["state"];
refresh: InstanceType<typeof App>["refresh"];
importLibrary: InstanceType<typeof App>["importLibraryFromUrl"];
setToastMessage: InstanceType<typeof App>["setToastMessage"];
readyPromise: ResolvablePromise<ExcalidrawImperativeAPI>;
ready: true;
id: string;
};
class App extends React.Component<AppProps, AppState> {
canvas: HTMLCanvasElement | null = null;
rc: RoughCanvas | null = null;
@@ -247,10 +321,6 @@ class App extends React.Component<AppProps, AppState> {
public libraryItemsFromStorage: LibraryItems | undefined;
private id: string;
private history: History;
private excalidrawContainerValue: {
container: HTMLDivElement | null;
id: string;
};
constructor(props: AppProps) {
super(props);
@@ -307,12 +377,6 @@ class App extends React.Component<AppProps, AppState> {
}
readyPromise.resolve(api);
}
this.excalidrawContainerValue = {
container: this.excalidrawContainerRef.current,
id: this.id,
};
this.scene = new Scene();
this.library = new Library(this);
this.history = new History();
@@ -340,7 +404,7 @@ class App extends React.Component<AppProps, AppState> {
if (viewModeEnabled) {
return (
<canvas
className="excalidraw__canvas"
id="canvas"
style={{
width: canvasDOMWidth,
height: canvasDOMHeight,
@@ -362,7 +426,7 @@ class App extends React.Component<AppProps, AppState> {
}
return (
<canvas
className="excalidraw__canvas"
id="canvas"
style={{
width: canvasDOMWidth,
height: canvasDOMHeight,
@@ -407,7 +471,7 @@ class App extends React.Component<AppProps, AppState> {
}
>
<ExcalidrawContainerContext.Provider
value={this.excalidrawContainerValue}
value={this.excalidrawContainerRef.current}
>
<IsMobileContext.Provider value={this.isMobile}>
<LayerUI
@@ -470,9 +534,7 @@ class App extends React.Component<AppProps, AppState> {
}
public focusContainer = () => {
if (this.props.autoFocus) {
this.excalidrawContainerRef.current?.focus();
}
this.excalidrawContainerRef.current?.focus();
};
public getSceneElementsIncludingDeleted = () => {
@@ -738,8 +800,6 @@ class App extends React.Component<AppProps, AppState> {
};
public async componentDidMount() {
this.excalidrawContainerValue.container = this.excalidrawContainerRef.current;
if (
process.env.NODE_ENV === ENV.TEST ||
process.env.NODE_ENV === ENV.DEVELOPMENT
@@ -809,15 +869,6 @@ class App extends React.Component<AppProps, AppState> {
} else {
this.updateDOMRect(this.initializeScene);
}
try {
const fileHandle = await idb.get(IDB_KEYS.fileHandle);
if (fileHandle) {
this.setState({ fileHandle });
}
} catch (error) {
console.error(error);
}
}
public componentWillUnmount() {
@@ -1600,10 +1651,10 @@ class App extends React.Component<AppProps, AppState> {
);
if (selectedElements.length) {
if (event.key === KEYS.G) {
this.setState({ openPopup: "backgroundColorPicker" });
this.setState({ openMenu: "backgroundColorPicker" });
}
if (event.key === KEYS.S) {
this.setState({ openPopup: "strokeColorPicker" });
this.setState({ openMenu: "strokeColorPicker" });
}
}
}
@@ -1811,6 +1862,7 @@ class App extends React.Component<AppProps, AppState> {
/** if true, returns the first selected element (with highest z-index)
of all hit elements */
preferSelected?: boolean;
cycleElementsUnderCursor?: boolean;
},
): NonDeleted<ExcalidrawElement> | null {
const allHitElements = this.getElementsAtPosition(x, y);
@@ -1821,6 +1873,13 @@ class App extends React.Component<AppProps, AppState> {
return allHitElements[index];
}
}
} else if (opts?.cycleElementsUnderCursor) {
const selectedIdx = allHitElements.findIndex(
(element) => this.state.selectedElementIds[element.id],
);
return selectedIdx > 0
? allHitElements[selectedIdx - 1]
: allHitElements[allHitElements.length - 1];
}
const elementWithHighestZIndex =
allHitElements[allHitElements.length - 1];
@@ -2257,6 +2316,8 @@ class App extends React.Component<AppProps, AppState> {
private handleCanvasPointerDown = (
event: React.PointerEvent<HTMLCanvasElement>,
) => {
event.persist();
// remove any active selection when we start to interact with canvas
// (mainly, we care about removing selection outside the component which
// would prevent our copy handling otherwise)
@@ -2693,10 +2754,13 @@ class App extends React.Component<AppProps, AppState> {
// hitElement may already be set above, so check first
pointerDownState.hit.element =
pointerDownState.hit.element ??
(!event[KEYS.CTRL_OR_CMD] ? pointerDownState.hit.element : null) ??
this.getElementAtPosition(
pointerDownState.origin.x,
pointerDownState.origin.y,
{
cycleElementsUnderCursor: event[KEYS.CTRL_OR_CMD],
},
);
// For overlapped elements one position may hit

View File

@@ -16,5 +16,10 @@ export const BackgroundPickerAndDarkModeToggle = ({
<div style={{ display: "flex" }}>
{actionManager.renderAction("changeViewBackgroundColor")}
{showThemeBtn && actionManager.renderAction("toggleTheme")}
{appState.fileHandle && (
<div style={{ marginInlineStart: "0.25rem" }}>
{actionManager.renderAction("saveToActiveFile")}
</div>
)}
</div>
);

View File

@@ -2,7 +2,7 @@ import clsx from "clsx";
import React, { useEffect, useState } from "react";
import { useCallbackRefState } from "../hooks/useCallbackRefState";
import { t } from "../i18n";
import { useExcalidrawContainer, useIsMobile } from "../components/App";
import { useIsMobile } from "../components/App";
import { KEYS } from "../keys";
import "./Dialog.scss";
import { back, close } from "./icons";
@@ -21,7 +21,6 @@ export const Dialog = (props: {
}) => {
const [islandNode, setIslandNode] = useCallbackRefState<HTMLDivElement>();
const [lastActiveElement] = useState(document.activeElement);
const { id } = useExcalidrawContainer();
useEffect(() => {
if (!islandNode) {
@@ -83,7 +82,7 @@ export const Dialog = (props: {
theme={props.theme}
>
<Island ref={setIslandNode}>
<h2 id={`${id}-dialog-title`} className="Dialog__title">
<h2 id="dialog-title" className="Dialog__title">
<span className="Dialog__titleContent">{props.title}</span>
<button
className="Modal__close"

View File

@@ -12,7 +12,7 @@ export const ErrorDialog = ({
onClose?: () => void;
}) => {
const [modalIsShown, setModalIsShown] = useState(!!message);
const { container: excalidrawContainer } = useExcalidrawContainer();
const excalidrawContainer = useExcalidrawContainer();
const handleClose = React.useCallback(() => {
setModalIsShown(false);

View File

@@ -97,8 +97,7 @@
border-radius: 1rem;
background-color: var(--button-color);
box-shadow: 0 3px 5px -1px rgba(0, 0, 0, 0.28),
0 6px 10px 0 rgba(0, 0, 0, 0.14);
box-shadow: 0 3px 5px -1px rgb(0 0 0 / 28%), 0 6px 10px 0 rgb(0 0 0 / 14%);
font-family: Cascadia;
font-size: 1.8em;

View File

@@ -157,13 +157,6 @@ export const HelpDialog = ({ onClose }: { onClose?: () => void }) => {
shortcuts={["Shift+P", "7"]}
/>
<Shortcut label={t("toolBar.text")} shortcuts={["T", "8"]} />
<Shortcut
label={t("helpDialog.editSelectedShape")}
shortcuts={[
getShortcutKey("Enter"),
t("helpDialog.doubleClick"),
]}
/>
<Shortcut
label={t("helpDialog.textNewLine")}
shortcuts={[

View File

@@ -5,7 +5,7 @@ import { getSelectedElements } from "../scene";
import "./HintViewer.scss";
import { AppState } from "../types";
import { isLinearElement, isTextElement } from "../element/typeChecks";
import { isLinearElement } from "../element/typeChecks";
import { getShortcutKey } from "../utils";
interface Hint {
@@ -57,14 +57,6 @@ const getHints = ({ appState, elements }: Hint) => {
return t("hints.lineEditor_info");
}
if (selectedElements.length === 1 && isTextElement(selectedElements[0])) {
return t("hints.text_selected");
}
if (appState.editingElement && isTextElement(appState.editingElement)) {
return t("hints.text_editing");
}
return null;
};

View File

@@ -8,17 +8,20 @@ import { CanvasError } from "../errors";
import { t } from "../i18n";
import { useIsMobile } from "./App";
import { getSelectedElements, isSomeElementSelected } from "../scene";
import { exportToCanvas } from "../scene/export";
import { exportToCanvas, getExportSize } from "../scene/export";
import { AppState } from "../types";
import { Dialog } from "./Dialog";
import { clipboard, exportImage } from "./icons";
import Stack from "./Stack";
import { ToolButton } from "./ToolButton";
import "./ExportDialog.scss";
import { supported as fsSupported } from "browser-fs-access";
import OpenColor from "open-color";
import { CheckboxItem } from "./CheckboxItem";
import { DEFAULT_EXPORT_PADDING } from "../constants";
const scales = [1, 2, 3];
const defaultScale = scales.includes(devicePixelRatio) ? devicePixelRatio : 1;
const supportsContextFilters =
"filter" in document.createElement("canvas").getContext("2d")!;
@@ -79,7 +82,7 @@ const ExportButton: React.FC<{
const ImageExportModal = ({
elements,
appState,
exportPadding = DEFAULT_EXPORT_PADDING,
exportPadding = 10,
actionManager,
onExportToPng,
onExportToSvg,
@@ -95,6 +98,7 @@ const ImageExportModal = ({
onCloseRequest: () => void;
}) => {
const someElementIsSelected = isSomeElementSelected(elements, appState);
const [scale, setScale] = useState(defaultScale);
const [exportSelected, setExportSelected] = useState(someElementIsSelected);
const previewRef = useRef<HTMLDivElement>(null);
const { exportBackground, viewBackgroundColor } = appState;
@@ -117,6 +121,7 @@ const ImageExportModal = ({
exportBackground,
viewBackgroundColor,
exportPadding,
scale,
});
// if converting to blob fails, there's some problem that will
@@ -139,6 +144,7 @@ const ImageExportModal = ({
exportBackground,
exportPadding,
viewBackgroundColor,
scale,
]);
return (
@@ -169,8 +175,33 @@ const ImageExportModal = ({
</div>
</div>
<div style={{ display: "flex", alignItems: "center", marginTop: ".6em" }}>
<Stack.Row gap={2}>
{actionManager.renderAction("changeExportScale")}
<Stack.Row gap={2} justifyContent={"center"}>
{scales.map((_scale) => {
const [width, height] = getExportSize(
exportedElements,
exportPadding,
_scale,
);
const scaleButtonTitle = `${t(
"buttons.scale",
)} ${_scale}x (${width}x${height})`;
return (
<ToolButton
key={_scale}
size="s"
type="radio"
icon={`${_scale}x`}
name="export-canvas-scale"
title={scaleButtonTitle}
aria-label={scaleButtonTitle}
id="export-canvas-scale"
checked={_scale === scale}
onChange={() => setScale(_scale)}
/>
);
})}
</Stack.Row>
<p style={{ marginLeft: "1em", userSelect: "none" }}>Scale</p>
</div>
@@ -189,7 +220,7 @@ const ImageExportModal = ({
color="indigo"
title={t("buttons.exportToPng")}
aria-label={t("buttons.exportToPng")}
onClick={() => onExportToPng(exportedElements)}
onClick={() => onExportToPng(exportedElements, scale)}
>
PNG
</ExportButton>
@@ -197,14 +228,14 @@ const ImageExportModal = ({
color="red"
title={t("buttons.exportToSvg")}
aria-label={t("buttons.exportToSvg")}
onClick={() => onExportToSvg(exportedElements)}
onClick={() => onExportToSvg(exportedElements, scale)}
>
SVG
</ExportButton>
{probablySupportsClipboardBlob && (
<ExportButton
title={t("buttons.copyPngToClipboard")}
onClick={() => onExportToClipboard(exportedElements)}
onClick={() => onExportToClipboard(exportedElements, scale)}
color="gray"
shade={7}
>
@@ -219,7 +250,7 @@ const ImageExportModal = ({
export const ImageExportDialog = ({
elements,
appState,
exportPadding = DEFAULT_EXPORT_PADDING,
exportPadding = 10,
actionManager,
onExportToPng,
onExportToSvg,

View File

@@ -36,7 +36,7 @@ import { Island } from "./Island";
import "./LayerUI.scss";
import { LibraryUnit } from "./LibraryUnit";
import { LoadingMessage } from "./LoadingMessage";
import { LockButton } from "./LockButton";
import { LockIcon } from "./LockIcon";
import { MobileMenu } from "./MobileMenu";
import { PasteChartDialog } from "./PasteChartDialog";
import { Section } from "./Section";
@@ -47,7 +47,6 @@ import { Tooltip } from "./Tooltip";
import { UserList } from "./UserList";
import Library from "../data/library";
import { JSONExportDialog } from "./JSONExportDialog";
import { LibraryButton } from "./LibraryButton";
interface LayerUIProps {
actionManager: ActionManager;
@@ -108,7 +107,6 @@ const LibraryMenuItems = ({
onAddToLibrary,
onInsertShape,
pendingElements,
theme,
setAppState,
setLibraryItems,
libraryReturnUrl,
@@ -121,7 +119,6 @@ const LibraryMenuItems = ({
onRemoveFromLibrary: (index: number) => void;
onInsertShape: (elements: LibraryItem) => void;
onAddToLibrary: (elements: LibraryItem) => void;
theme: AppState["theme"];
setAppState: React.Component<any, AppState>["setState"];
setLibraryItems: (library: LibraryItems) => void;
libraryReturnUrl: ExcalidrawProps["libraryReturnUrl"];
@@ -195,7 +192,7 @@ const LibraryMenuItems = ({
<a
href={`https://libraries.excalidraw.com?target=${
window.name || "_blank"
}&referrer=${referrer}&useHash=true&token=${id}&theme=${theme}`}
}&referrer=${referrer}&useHash=true&token=${id}`}
target="_excalidraw_libraries"
>
{t("labels.libraries")}
@@ -249,7 +246,6 @@ const LibraryMenu = ({
onInsertShape,
pendingElements,
onAddToLibrary,
theme,
setAppState,
libraryReturnUrl,
focusContainer,
@@ -260,7 +256,6 @@ const LibraryMenu = ({
onClickOutside: (event: MouseEvent) => void;
onInsertShape: (elements: LibraryItem) => void;
onAddToLibrary: () => void;
theme: AppState["theme"];
setAppState: React.Component<any, AppState>["setState"];
libraryReturnUrl: ExcalidrawProps["libraryReturnUrl"];
focusContainer: () => void;
@@ -350,7 +345,6 @@ const LibraryMenu = ({
libraryReturnUrl={libraryReturnUrl}
focusContainer={focusContainer}
library={library}
theme={theme}
id={id}
/>
)}
@@ -406,11 +400,13 @@ const LayerUI = ({
const createExporter = (type: ExportType): ExportCB => async (
exportedElements,
scale,
) => {
await exportCanvas(type, exportedElements, appState, {
exportBackground: appState.exportBackground,
name: appState.name,
viewBackgroundColor: appState.viewBackgroundColor,
scale,
})
.catch(muteFSAbortError)
.catch((error) => {
@@ -488,9 +484,6 @@ const LayerUI = ({
setAppState={setAppState}
showThemeBtn={showThemeBtn}
/>
{appState.fileHandle && (
<>{actionManager.renderAction("saveToActiveFile")}</>
)}
</Stack.Col>
</Island>
</Section>
@@ -509,8 +502,7 @@ const LayerUI = ({
style={{
// we want to make sure this doesn't overflow so substracting 200
// which is approximately height of zoom footer and top left menu items with some buffer
// if active file name is displayed, subtracting 248 to account for its height
maxHeight: `${appState.height - (appState.fileHandle ? 248 : 200)}px`,
maxHeight: `${appState.height - 200}px`,
}}
>
<SelectedShapeActions
@@ -547,7 +539,6 @@ const LayerUI = ({
libraryReturnUrl={libraryReturnUrl}
focusContainer={focusContainer}
library={library}
theme={appState.theme}
id={id}
/>
) : null;
@@ -575,12 +566,6 @@ const LayerUI = ({
{(heading) => (
<Stack.Col gap={4} align="start">
<Stack.Row gap={1}>
<LockButton
zenModeEnabled={zenModeEnabled}
checked={appState.elementLocked}
onChange={onLockToggle}
title={t("toolBar.lock")}
/>
<Island
padding={1}
className={clsx({ "zen-mode": zenModeEnabled })}
@@ -592,12 +577,15 @@ const LayerUI = ({
canvas={canvas}
elementType={appState.elementType}
setAppState={setAppState}
isLibraryOpen={appState.isLibraryOpen}
/>
</Stack.Row>
</Island>
<LibraryButton
appState={appState}
setAppState={setAppState}
<LockIcon
zenModeEnabled={zenModeEnabled}
checked={appState.elementLocked}
onChange={onLockToggle}
title={t("toolBar.lock")}
/>
</Stack.Row>
{libraryMenu}

View File

@@ -1,45 +0,0 @@
import clsx from "clsx";
import { t } from "../i18n";
import { AppState } from "../types";
import { capitalizeString } from "../utils";
const LIBRARY_ICON = (
<svg viewBox="0 0 576 512">
<path
fill="currentColor"
d="M542.22 32.05c-54.8 3.11-163.72 14.43-230.96 55.59-4.64 2.84-7.27 7.89-7.27 13.17v363.87c0 11.55 12.63 18.85 23.28 13.49 69.18-34.82 169.23-44.32 218.7-46.92 16.89-.89 30.02-14.43 30.02-30.66V62.75c.01-17.71-15.35-31.74-33.77-30.7zM264.73 87.64C197.5 46.48 88.58 35.17 33.78 32.05 15.36 31.01 0 45.04 0 62.75V400.6c0 16.24 13.13 29.78 30.02 30.66 49.49 2.6 149.59 12.11 218.77 46.95 10.62 5.35 23.21-1.94 23.21-13.46V100.63c0-5.29-2.62-10.14-7.27-12.99z"
></path>
</svg>
);
export const LibraryButton: React.FC<{
appState: AppState;
setAppState: React.Component<any, AppState>["setState"];
}> = ({ appState, setAppState }) => {
return (
<label
className={clsx(
"ToolIcon ToolIcon_type_floating ToolIcon__library zen-mode-visibility",
`ToolIcon_size_m`,
{
"zen-mode-visibility--hidden": appState.zenModeEnabled,
},
)}
title={`${capitalizeString(t("toolBar.library"))} — 9`}
style={{ marginInlineStart: "var(--space-factor)" }}
>
<input
className="ToolIcon_type_checkbox"
type="checkbox"
name="editor-library"
onChange={(event) => {
setAppState({ isLibraryOpen: event.target.checked });
}}
checked={appState.isLibraryOpen}
aria-label={capitalizeString(t("toolBar.library"))}
aria-keyshortcuts="9"
/>
<div className="ToolIcon__icon">{LIBRARY_ICON}</div>
</label>
);
};

View File

@@ -8,8 +8,10 @@ type LockIconSize = "s" | "m";
type LockIconProps = {
title?: string;
name?: string;
id?: string;
checked: boolean;
onChange?(): void;
size?: LockIconSize;
zenModeEnabled?: boolean;
};
@@ -39,12 +41,12 @@ const ICONS = {
),
};
export const LockButton = (props: LockIconProps) => {
export const LockIcon = (props: LockIconProps) => {
return (
<label
className={clsx(
"ToolIcon ToolIcon__lock ToolIcon_type_floating zen-mode-visibility",
`ToolIcon_size_${DEFAULT_SIZE}`,
`ToolIcon_size_${props.size || DEFAULT_SIZE}`,
{
"zen-mode-visibility--hidden": props.zenModeEnabled,
},
@@ -55,6 +57,7 @@ export const LockButton = (props: LockIconProps) => {
className="ToolIcon_type_checkbox"
type="checkbox"
name={props.name}
id={props.id}
onChange={props.onChange}
checked={props.checked}
aria-label={props.title}

View File

@@ -13,10 +13,9 @@ import { SelectedShapeActions, ShapesSwitcher } from "./Actions";
import { Section } from "./Section";
import CollabButton from "./CollabButton";
import { SCROLLBAR_WIDTH, SCROLLBAR_MARGIN } from "../scene/scrollbars";
import { LockButton } from "./LockButton";
import { LockIcon } from "./LockIcon";
import { UserList } from "./UserList";
import { BackgroundPickerAndDarkModeToggle } from "./BackgroundPickerAndDarkModeToggle";
import { LibraryButton } from "./LibraryButton";
type MobileMenuProps = {
appState: AppState;
@@ -65,15 +64,15 @@ export const MobileMenu = ({
canvas={canvas}
elementType={appState.elementType}
setAppState={setAppState}
isLibraryOpen={appState.isLibraryOpen}
/>
</Stack.Row>
</Island>
<LockButton
<LockIcon
checked={appState.elementLocked}
onChange={onLockToggle}
title={t("toolBar.lock")}
/>
<LibraryButton appState={appState} setAppState={setAppState} />
</Stack.Row>
{libraryMenu}
</Stack.Col>

View File

@@ -58,7 +58,7 @@ const useBodyRoot = (theme: AppState["theme"]) => {
const isMobileRef = useRef(isMobile);
isMobileRef.current = isMobile;
const { container: excalidrawContainer } = useExcalidrawContainer();
const excalidrawContainer = useExcalidrawContainer();
useLayoutEffect(() => {
if (div) {

View File

@@ -1,10 +1,9 @@
import "./TextInput.scss";
import React, { useState } from "react";
import React, { Component } from "react";
import { focusNearestParent } from "../utils";
import "./ProjectName.scss";
import { useExcalidrawContainer } from "./App";
type Props = {
value: string;
@@ -13,19 +12,22 @@ type Props = {
isNameEditable: boolean;
};
export const ProjectName = (props: Props) => {
const { id } = useExcalidrawContainer();
const [fileName, setFileName] = useState<string>(props.value);
const handleBlur = (event: any) => {
type State = {
fileName: string;
};
export class ProjectName extends Component<Props, State> {
state = {
fileName: this.props.value,
};
private handleBlur = (event: any) => {
focusNearestParent(event.target);
const value = event.target.value;
if (value !== props.value) {
props.onChange(value);
if (value !== this.props.value) {
this.props.onChange(value);
}
};
const handleKeyDown = (event: React.KeyboardEvent<HTMLElement>) => {
private handleKeyDown = (event: React.KeyboardEvent<HTMLElement>) => {
if (event.key === "Enter") {
event.preventDefault();
if (event.nativeEvent.isComposing || event.keyCode === 229) {
@@ -35,25 +37,29 @@ export const ProjectName = (props: Props) => {
}
};
return (
<div className="ProjectName">
<label className="ProjectName-label" htmlFor="filename">
{`${props.label}${props.isNameEditable ? "" : ":"}`}
</label>
{props.isNameEditable ? (
<input
className="TextInput"
onBlur={handleBlur}
onKeyDown={handleKeyDown}
id={`${id}-filename`}
value={fileName}
onChange={(event) => setFileName(event.target.value)}
/>
) : (
<span className="TextInput TextInput--readonly" id={`${id}-filename`}>
{props.value}
</span>
)}
</div>
);
};
public render() {
return (
<div className="ProjectName">
<label className="ProjectName-label" htmlFor="filename">
{`${this.props.label}${this.props.isNameEditable ? "" : ":"}`}
</label>
{this.props.isNameEditable ? (
<input
className="TextInput"
onBlur={this.handleBlur}
onKeyDown={this.handleKeyDown}
id="filename"
value={this.state.fileName}
onChange={(event) =>
this.setState({ fileName: event.target.value })
}
/>
) : (
<span className="TextInput TextInput--readonly" id="filename">
{this.props.value}
</span>
)}
</div>
);
}
}

View File

@@ -1,6 +1,5 @@
import React from "react";
import { t } from "../i18n";
import { useExcalidrawContainer } from "./App";
interface SectionProps extends React.HTMLProps<HTMLElement> {
heading: string;
@@ -8,14 +7,13 @@ interface SectionProps extends React.HTMLProps<HTMLElement> {
}
export const Section = ({ heading, children, ...props }: SectionProps) => {
const { id } = useExcalidrawContainer();
const header = (
<h2 className="visually-hidden" id={`${id}-${heading}-title`}>
<h2 className="visually-hidden" id={`${heading}-title`}>
{t(`headings.${heading}`)}
</h2>
);
return (
<section {...props} aria-labelledby={`${id}-${heading}-title`}>
<section {...props} aria-labelledby={`${heading}-title`}>
{typeof children === "function" ? (
children(header)
) : (

View File

@@ -2,7 +2,6 @@ import "./ToolIcon.scss";
import React from "react";
import clsx from "clsx";
import { useExcalidrawContainer } from "./App";
type ToolIconSize = "s" | "m";
@@ -44,7 +43,6 @@ type ToolButtonProps =
const DEFAULT_SIZE: ToolIconSize = "m";
export const ToolButton = React.forwardRef((props: ToolButtonProps, ref) => {
const { id: excalId } = useExcalidrawContainer();
const innerRef = React.useRef(null);
React.useImperativeHandle(ref, () => innerRef.current);
const sizeCn = `ToolIcon_size_${props.size || DEFAULT_SIZE}`;
@@ -100,7 +98,7 @@ export const ToolButton = React.forwardRef((props: ToolButtonProps, ref) => {
aria-label={props["aria-label"]}
aria-keyshortcuts={props["aria-keyshortcuts"]}
data-testid={props["data-testid"]}
id={`${excalId}-${props.id}`}
id={props.id}
onChange={props.onChange}
checked={props.checked}
ref={innerRef}

View File

@@ -8,18 +8,10 @@
position: relative;
font-family: Cascadia;
cursor: pointer;
background-color: var(--button-gray-1);
-webkit-tap-highlight-color: transparent;
border-radius: var(--space-factor);
user-select: none;
background-color: var(--button-gray-1);
&:hover {
background-color: var(--button-gray-2);
}
&:active {
background-color: var(--button-gray-3);
}
}
.ToolIcon--plain {
@@ -74,6 +66,14 @@
margin: 0;
font-size: inherit;
&:hover {
background-color: var(--button-gray-1);
}
&:active {
background-color: var(--button-gray-2);
}
&:focus {
box-shadow: 0 0 0 2px var(--focus-highlight-color);
}
@@ -86,14 +86,6 @@
}
}
&:hover {
background-color: var(--button-gray-2);
}
&:active {
background-color: var(--button-gray-3);
}
&--show {
visibility: visible;
}
@@ -111,9 +103,6 @@
&:not(.ToolIcon_toggle_opaque):checked + .ToolIcon__icon {
background-color: var(--button-gray-2);
&:active {
background-color: var(--button-gray-3);
}
}
&:focus + .ToolIcon__icon {
@@ -141,21 +130,12 @@
}
.ToolIcon__icon {
background-color: var(--button-gray-1);
&:hover {
background-color: var(--button-gray-2);
}
&:active {
background-color: var(--button-gray-3);
}
width: 2rem;
height: 2em;
}
}
.ToolIcon.ToolIcon__lock {
margin-inline-end: var(--space-factor);
&.ToolIcon_type_floating {
margin-left: 0.1rem;
}
@@ -186,9 +166,10 @@
// 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) {
.ToolIcon.ToolIcon_type_floating {
.ToolIcon.ToolIcon__lock {
display: inline-block;
position: absolute;
top: 60px;
right: -8px;
margin-left: 0;
@@ -213,14 +194,6 @@
position: static;
}
}
.ToolIcon.ToolIcon__library {
top: 100px;
}
.ToolIcon.ToolIcon__lock {
margin-inline-end: 0;
top: 60px;
}
}
.unlocked-icon {

View File

@@ -24,10 +24,7 @@ type Opts = {
mirror?: true;
} & React.SVGProps<SVGSVGElement>;
export const createIcon = (
d: string | React.ReactNode,
opts: number | Opts = 512,
) => {
const createIcon = (d: string | React.ReactNode, opts: number | Opts = 512) => {
const { width = 512, height = width, mirror, style } =
typeof opts === "number" ? ({ width: opts } as Opts) : opts;
return (
@@ -477,11 +474,6 @@ export const shield = createIcon(
{ width: 24 },
);
export const file = createIcon(
"M369.9 97.9L286 14C277 5 264.8-.1 252.1-.1H48C21.5 0 0 21.5 0 48v416c0 26.5 21.5 48 48 48h288c26.5 0 48-21.5 48-48V131.9c0-12.7-5.1-25-14.1-34zM332.1 128H256V51.9l76.1 76.1zM48 464V48h160v104c0 13.3 10.7 24 24 24h104v288H48zm32-48h224V288l-23.5-23.5c-4.7-4.7-12.3-4.7-17 0L176 352l-39.5-39.5c-4.7-4.7-12.3-4.7-17 0L80 352v64zm48-240c-26.5 0-48 21.5-48 48s21.5 48 48 48 48-21.5 48-48-21.5-48-48-48z",
{ width: 384, height: 512 },
);
export const GroupIcon = React.memo(({ theme }: { theme: "light" | "dark" }) =>
createIcon(
<>

View File

@@ -1,6 +1,6 @@
import { FontFamily } from "./element/types";
import cssVariables from "./css/variables.module.scss";
import { AppProps } from "./types";
import { FontFamilyValues } from "./element/types";
export const APP_NAME = "Excalidraw";
@@ -63,15 +63,15 @@ export const CLASSES = {
// 1-based in case we ever do `if(element.fontFamily)`
export const FONT_FAMILY = {
Virgil: 1,
Helvetica: 2,
Cascadia: 3,
};
1: "Virgil",
2: "Helvetica",
3: "Cascadia",
} as const;
export const WINDOWS_EMOJI_FALLBACK_FONT = "Segoe UI Emoji";
export const DEFAULT_FONT_SIZE = 20;
export const DEFAULT_FONT_FAMILY: FontFamilyValues = FONT_FAMILY.Virgil;
export const DEFAULT_FONT_FAMILY: FontFamily = 1;
export const DEFAULT_TEXT_ALIGN = "left";
export const DEFAULT_VERTICAL_ALIGN = "top";
export const DEFAULT_VERSION = "{version}";
@@ -97,10 +97,6 @@ export const STORAGE_KEYS = {
LOCAL_STORAGE_LIBRARY: "excalidraw-library",
} as const;
export const IDB_KEYS = {
fileHandle: "fileHandle",
} as const;
// time in milliseconds
export const TAP_TWICE_TIMEOUT = 300;
export const TOUCH_CTX_MENU_TIMEOUT = 500;
@@ -148,6 +144,3 @@ export const MQ_MAX_WIDTH_LANDSCAPE = 1000;
export const MQ_MAX_HEIGHT_LANDSCAPE = 500;
export const MAX_DECIMALS_FOR_SVG_EXPORT = 2;
export const EXPORT_SCALES = [1, 2, 3];
export const DEFAULT_EXPORT_PADDING = 10; // px

View File

@@ -51,12 +51,11 @@
image-rendering: -moz-crisp-edges; // FF
z-index: var(--zIndex-canvas);
// Remove the main canvas from document flow to avoid resizeObserver
// feedback loop (see https://github.com/excalidraw/excalidraw/pull/3379)
}
&__canvas {
#canvas {
// Remove the main canvas from document flow to avoid resizeObserver
// feedback loop (see https://github.com/excalidraw/excalidraw/pull/3379)
position: absolute;
}

View File

@@ -3,7 +3,6 @@ import {
copyBlobToClipboardAsPng,
copyTextToSystemClipboard,
} from "../clipboard";
import { DEFAULT_EXPORT_PADDING } from "../constants";
import { NonDeletedExcalidrawElement } from "../element/types";
import { t } from "../i18n";
import { exportToCanvas, exportToSvg } from "../scene/export";
@@ -21,14 +20,16 @@ export const exportCanvas = async (
appState: AppState,
{
exportBackground,
exportPadding = DEFAULT_EXPORT_PADDING,
exportPadding = 10,
viewBackgroundColor,
name,
scale = 1,
}: {
exportBackground: boolean;
exportPadding?: number;
viewBackgroundColor: string;
name: string;
scale?: number;
},
) => {
if (elements.length === 0) {
@@ -40,7 +41,7 @@ export const exportCanvas = async (
exportWithDarkMode: appState.exportWithDarkMode,
viewBackgroundColor,
exportPadding,
exportScale: appState.exportScale,
scale,
metadata:
appState.exportEmbedScene && type === "svg"
? await (
@@ -66,6 +67,7 @@ export const exportCanvas = async (
exportBackground,
viewBackgroundColor,
exportPadding,
scale,
});
tempCanvas.style.display = "none";
document.body.appendChild(tempCanvas);

View File

@@ -1,4 +1,4 @@
import { fileOpen, fileSave, FileSystemHandle } from "browser-fs-access";
import { fileOpen, fileSave } from "browser-fs-access";
import { cleanAppStateForExport } from "../appState";
import { EXPORT_DATA_TYPES, EXPORT_SOURCE, MIME_TYPES } from "../constants";
import { clearElementsForExport } from "../element";
@@ -12,7 +12,6 @@ import {
ExportedLibraryData,
} from "./types";
import Library from "./library";
import { AbortError } from "../errors";
export const serializeAsJSON = (
elements: readonly ExcalidrawElement[],
@@ -29,26 +28,6 @@ export const serializeAsJSON = (
return JSON.stringify(data, null, 2);
};
// adapted from https://web.dev/file-system-access
const verifyPermission = async (fileHandle: FileSystemHandle) => {
try {
const options = { mode: "readwrite" } as any;
// Check if permission was already granted. If so, return true.
if ((await fileHandle.queryPermission(options)) === "granted") {
return true;
}
// Request permission. If the user grants permission, return true.
if ((await fileHandle.requestPermission(options)) === "granted") {
return true;
}
// The user didn't grant permission, so return false.
return false;
} catch (error) {
console.error(error);
return false;
}
};
export const saveAsJSON = async (
elements: readonly ExcalidrawElement[],
appState: AppState,
@@ -58,12 +37,6 @@ export const saveAsJSON = async (
type: MIME_TYPES.excalidraw,
});
if (appState.fileHandle) {
if (!(await verifyPermission(appState.fileHandle))) {
throw new AbortError();
}
}
const fileHandle = await fileSave(
blob,
{

View File

@@ -2,7 +2,7 @@ import { loadLibraryFromBlob } from "./blob";
import { LibraryItems, LibraryItem } from "../types";
import { restoreElements } from "./restore";
import { getNonDeletedElements } from "../element";
import type App from "../components/App";
import App from "../components/App";
class Library {
private libraryCache: LibraryItems | null = null;

View File

@@ -1,18 +1,18 @@
import {
ExcalidrawElement,
FontFamily,
ExcalidrawSelectionElement,
FontFamilyValues,
} from "../element/types";
import { AppState, NormalizedZoomValue } from "../types";
import { ImportedDataState } from "./types";
import { getNormalizedDimensions, isInvisiblySmallElement } from "../element";
import { isInvisiblySmallElement, getNormalizedDimensions } from "../element";
import { isLinearElementType } from "../element/typeChecks";
import { randomId } from "../random";
import {
FONT_FAMILY,
DEFAULT_FONT_FAMILY,
DEFAULT_TEXT_ALIGN,
DEFAULT_VERTICAL_ALIGN,
FONT_FAMILY,
} from "../constants";
import { getDefaultAppState } from "../appState";
import { LinearElementEditor } from "../element/linearElementEditor";
@@ -41,11 +41,11 @@ export type RestoredDataState = {
appState: RestoredAppState;
};
const getFontFamilyByName = (fontFamilyName: string): FontFamilyValues => {
if (Object.keys(FONT_FAMILY).includes(fontFamilyName)) {
return FONT_FAMILY[
fontFamilyName as keyof typeof FONT_FAMILY
] as FontFamilyValues;
const getFontFamilyByName = (fontFamilyName: string): FontFamily => {
for (const [id, fontFamilyString] of Object.entries(FONT_FAMILY)) {
if (fontFamilyString.includes(fontFamilyName)) {
return parseInt(id) as FontFamily;
}
}
return DEFAULT_FONT_FAMILY;
};

View File

@@ -5,7 +5,7 @@ import { mutateElement } from "./mutateElement";
import { getPerfectElementSize } from "./sizeHelpers";
import Scene from "../scene/Scene";
import { NonDeletedExcalidrawElement } from "./types";
import { PointerDownState } from "../types";
import { PointerDownState } from "../components/App";
export const dragSelectedElements = (
pointerDownState: PointerDownState,

View File

@@ -1,7 +1,6 @@
import { duplicateElement } from "./newElement";
import { mutateElement } from "./mutateElement";
import { API } from "../tests/helpers/api";
import { FONT_FAMILY } from "../constants";
const isPrimitive = (val: any) => {
const type = typeof val;
@@ -80,7 +79,7 @@ it("clones text element", () => {
opacity: 100,
text: "hello",
fontSize: 20,
fontFamily: FONT_FAMILY.Virgil,
fontFamily: 1,
textAlign: "left",
verticalAlign: "top",
});

View File

@@ -5,11 +5,11 @@ import {
ExcalidrawGenericElement,
NonDeleted,
TextAlign,
FontFamily,
GroupId,
VerticalAlign,
Arrowhead,
ExcalidrawFreeDrawElement,
FontFamilyValues,
} from "../element/types";
import { measureText, getFontString } from "../utils";
import { randomInteger, randomId } from "../random";
@@ -109,7 +109,7 @@ export const newTextElement = (
opts: {
text: string;
fontSize: number;
fontFamily: FontFamilyValues;
fontFamily: FontFamily;
textAlign: TextAlign;
verticalAlign: VerticalAlign;
} & ElementConstructorOpts,

View File

@@ -32,7 +32,8 @@ import {
MaybeTransformHandleType,
TransformHandleDirection,
} from "./transformHandles";
import { Point, PointerDownState } from "../types";
import { PointerDownState } from "../components/App";
import { Point } from "../types";
export const normalizeAngle = (angle: number): number => {
if (angle >= 2 * Math.PI) {

View File

@@ -3,8 +3,7 @@ import { FONT_FAMILY } from "../constants";
export type ChartType = "bar" | "line";
export type FillStyle = "hachure" | "cross-hatch" | "solid";
export type FontFamilyKeys = keyof typeof FONT_FAMILY;
export type FontFamilyValues = typeof FONT_FAMILY[FontFamilyKeys];
export type FontFamily = keyof typeof FONT_FAMILY;
export type FontString = string & { _brand: "fontString" };
export type GroupId = string;
export type PointerType = "mouse" | "pen" | "touch";
@@ -92,7 +91,7 @@ export type ExcalidrawTextElement = _ExcalidrawElementBase &
Readonly<{
type: "text";
fontSize: number;
fontFamily: FontFamilyValues;
fontFamily: FontFamily;
text: string;
baseline: number;
textAlign: TextAlign;

View File

@@ -1,5 +1,4 @@
type CANVAS_ERROR_NAMES = "CANVAS_ERROR" | "CANVAS_POSSIBLY_TOO_BIG";
export class CanvasError extends Error {
constructor(
message: string = "Couldn't export canvas.",
@@ -10,11 +9,3 @@ export class CanvasError extends Error {
this.message = message;
}
}
export class AbortError extends Error {
constructor(message: string = "Request aborted") {
super();
this.name = "AbortError";
this.message = message;
}
}

View File

@@ -1,6 +1,6 @@
import throttle from "lodash.throttle";
import React, { PureComponent } from "react";
import { ExcalidrawImperativeAPI } from "../../types";
import { ExcalidrawImperativeAPI } from "../../components/App";
import { ErrorDialog } from "../../components/ErrorDialog";
import { APP_NAME, ENV, EVENT } from "../../constants";
import { ImportedDataState } from "../../data/types";

View File

@@ -1,92 +0,0 @@
import React from "react";
import { Card } from "../../components/Card";
import { ToolButton } from "../../components/ToolButton";
import { serializeAsJSON } from "../../data/json";
import { getImportedKey, createIV, generateEncryptionKey } from "../data";
import { loadFirebaseStorage } from "../data/firebase";
import { NonDeletedExcalidrawElement } from "../../element/types";
import { AppState } from "../../types";
import { nanoid } from "nanoid";
import { t } from "../../i18n";
import { excalidrawPlusIcon } from "./icons";
const encryptData = async (
key: string,
json: string,
): Promise<{ blob: Blob; iv: Uint8Array }> => {
const importedKey = await getImportedKey(key, "encrypt");
const iv = createIV();
const encoded = new TextEncoder().encode(json);
const ciphertext = await window.crypto.subtle.encrypt(
{
name: "AES-GCM",
iv,
},
importedKey,
encoded,
);
return { blob: new Blob([new Uint8Array(ciphertext)]), iv };
};
const exportToExcalidrawPlus = async (
elements: readonly NonDeletedExcalidrawElement[],
appState: AppState,
) => {
const firebase = await loadFirebaseStorage();
const id = `${nanoid(12)}`;
const key = (await generateEncryptionKey())!;
const encryptedData = await encryptData(
key,
serializeAsJSON(elements, appState),
);
const blob = new Blob([encryptedData.iv, encryptedData.blob], {
type: "application/octet-stream",
});
await firebase
.storage()
.ref(`/migrations/scenes/${id}`)
.put(blob, {
customMetadata: {
data: JSON.stringify({ version: 1, name: appState.name }),
created: Date.now().toString(),
},
});
window.open(`https://plus.excalidraw.com/import?excalidraw=${id},${key}`);
};
export const ExportToExcalidrawPlus: React.FC<{
elements: readonly NonDeletedExcalidrawElement[];
appState: AppState;
onError: (error: Error) => void;
}> = ({ elements, appState, onError }) => {
return (
<Card color="indigo">
<div className="Card-icon">{excalidrawPlusIcon}</div>
<h2>Excalidraw+</h2>
<div className="Card-details">
{t("exportDialog.excalidrawplus_description")}
</div>
<ToolButton
className="Card-button"
type="button"
title={t("exportDialog.excalidrawplus_button")}
aria-label={t("exportDialog.excalidrawplus_button")}
showAriaLabel={true}
onClick={async () => {
try {
await exportToExcalidrawPlus(elements, appState);
} catch (error) {
console.error(error);
onError(new Error(t("exportDialog.excalidrawplus_exportError")));
}
}}
/>
</Card>
);
};

File diff suppressed because one or more lines are too long

View File

@@ -5,19 +5,15 @@ import { getSceneVersion } from "../../element";
import Portal from "../collab/Portal";
import { restoreElements } from "../../data/restore";
// private
// -----------------------------------------------------------------------------
let firebasePromise: Promise<
typeof import("firebase/app").default
> | null = null;
let firestorePromise: Promise<any> | null = null;
let firebseStoragePromise: Promise<any> | null = null;
const _loadFirebase = async () => {
const loadFirebase = async () => {
const firebase = (
await import(/* webpackChunkName: "firebase" */ "firebase/app")
).default;
await import(/* webpackChunkName: "firestore" */ "firebase/firestore");
const firebaseConfig = JSON.parse(process.env.REACT_APP_FIREBASE_CONFIG);
firebase.initializeApp(firebaseConfig);
@@ -25,37 +21,13 @@ const _loadFirebase = async () => {
return firebase;
};
const _getFirebase = async (): Promise<
const getFirebase = async (): Promise<
typeof import("firebase/app").default
> => {
if (!firebasePromise) {
firebasePromise = _loadFirebase();
firebasePromise = loadFirebase();
}
return firebasePromise;
};
// -----------------------------------------------------------------------------
const loadFirestore = async () => {
const firebase = await _getFirebase();
if (!firestorePromise) {
firestorePromise = import(
/* webpackChunkName: "firestore" */ "firebase/firestore"
);
await firestorePromise;
}
return firebase;
};
export const loadFirebaseStorage = async () => {
const firebase = await _getFirebase();
if (!firebseStoragePromise) {
firebseStoragePromise = import(
/* webpackChunkName: "storage" */ "firebase/storage"
);
await firebseStoragePromise;
}
return firebase;
return await firebasePromise!;
};
interface FirebaseStoredScene {
@@ -136,7 +108,7 @@ export const saveToFirebase = async (
return true;
}
const firebase = await loadFirestore();
const firebase = await getFirebase();
const sceneVersion = getSceneVersion(elements);
const { ciphertext, iv } = await encryptElements(roomKey, elements);
@@ -178,7 +150,7 @@ export const loadFromFirebase = async (
roomKey: string,
socket: SocketIOClient.Socket | null,
): Promise<readonly ExcalidrawElement[] | null> => {
const firebase = await loadFirestore();
const firebase = await getFirebase();
const db = firebase.firestore();
const docRef = db.collection("scenes").doc(roomId);

View File

@@ -17,7 +17,7 @@ const generateRandomID = async () => {
return Array.from(arr, byteToHex).join("");
};
export const generateEncryptionKey = async () => {
const generateEncryptionKey = async () => {
const key = await window.crypto.subtle.generateKey(
{
name: "AES-GCM",
@@ -176,7 +176,7 @@ export const getImportedKey = (key: string, usage: KeyUsage) =>
[usage],
);
export const decryptImported = async (
const decryptImported = async (
iv: ArrayBuffer,
encrypted: ArrayBuffer,
privateKey: string,

View File

@@ -8,6 +8,7 @@ import React, {
} from "react";
import { trackEvent } from "../analytics";
import { getDefaultAppState } from "../appState";
import { ExcalidrawImperativeAPI } from "../components/App";
import { ErrorDialog } from "../components/ErrorDialog";
import { TopErrorBoundary } from "../components/TopErrorBoundary";
import {
@@ -30,7 +31,7 @@ import Excalidraw, {
defaultLang,
languages,
} from "../packages/excalidraw/index";
import { AppState, LibraryItems, ExcalidrawImperativeAPI } from "../types";
import { AppState, LibraryItems } from "../types";
import {
debounce,
getVersion,
@@ -55,7 +56,6 @@ import { Tooltip } from "../components/Tooltip";
import { shield } from "../components/icons";
import "./index.scss";
import { ExportToExcalidrawPlus } from "./components/ExportToExcalidrawPlus";
const languageDetector = new LanguageDetector();
languageDetector.init({
@@ -428,21 +428,6 @@ const ExcalidrawWrapper = () => {
canvasActions: {
export: {
onExportToBackend,
renderCustomUI: (elements, appState) => {
return (
<ExportToExcalidrawPlus
elements={elements}
appState={appState}
onError={(error) => {
excalidrawAPI?.updateScene({
appState: {
errorMessage: error.message,
},
});
}}
/>
);
},
},
},
}}
@@ -453,7 +438,6 @@ const ExcalidrawWrapper = () => {
detectScroll={false}
handleKeyboardGlobally={true}
onLibraryChange={onLibraryChange}
autoFocus={true}
/>
{excalidrawAPI && <CollabWrapper excalidrawAPI={excalidrawAPI} />}
{errorMessage && (

View File

@@ -69,6 +69,7 @@ const canvas = exportToCanvas(
{
exportBackground: true,
viewBackgroundColor: "#ffffff",
scale: 1,
},
createCanvas,
);

View File

@@ -101,8 +101,6 @@
"viewMode": "نمط العرض",
"toggleExportColorScheme": "",
"share": "مشاركة",
"showStroke": "",
"showBackground": "",
"toggleTheme": "غير النمط"
},
"buttons": {
@@ -225,10 +223,7 @@
"disk_button": "إحفظ لملف",
"link_title": "رابط قابل للمشاركة",
"link_details": "صدر الملف للمشاهدة فقط.",
"link_button": "التصدير كرابط",
"excalidrawplus_description": "",
"excalidrawplus_button": "",
"excalidrawplus_exportError": ""
"link_button": "التصدير كرابط"
},
"helpDialog": {
"blog": "اقرأ مدونتنا",

View File

@@ -101,8 +101,6 @@
"viewMode": "Изглед",
"toggleExportColorScheme": "",
"share": "",
"showStroke": "",
"showBackground": "",
"toggleTheme": ""
},
"buttons": {
@@ -225,10 +223,7 @@
"disk_button": "",
"link_title": "",
"link_details": "",
"link_button": "",
"excalidrawplus_description": "",
"excalidrawplus_button": "",
"excalidrawplus_exportError": ""
"link_button": ""
},
"helpDialog": {
"blog": "Прочетете нашия блог",

View File

@@ -20,8 +20,8 @@
"background": "Color del fons",
"fill": "Estil del fons",
"strokeWidth": "Amplada del traç",
"strokeShape": "Estil del traç",
"strokeShape_gel": "Bolígraf de gel",
"strokeShape": "",
"strokeShape_gel": "",
"strokeShape_fountain": "",
"strokeShape_brush": "",
"strokeStyle": "Estil del traç",
@@ -42,7 +42,7 @@
"fontSize": "Mida de lletra",
"fontFamily": "Tipus de lletra",
"onlySelected": "Només seleccionats",
"withBackground": "Fons",
"withBackground": "",
"exportEmbedScene": "",
"exportEmbedScene_details": "Les dades de lescena es desaran al fitxer PNG/SVG de manera que es pugui restaurar lescena.\nAugmentarà la mida del fitxer exportat.",
"addWatermark": "Afegir \"Fet amb Excalidraw\"",
@@ -101,21 +101,19 @@
"viewMode": "Mode de visualització",
"toggleExportColorScheme": "Canvia l'esquema de colors de l'exportació",
"share": "Compartir",
"showStroke": "",
"showBackground": "",
"toggleTheme": ""
},
"buttons": {
"clearReset": "Netejar el llenç",
"exportJSON": "Exporta a un fitxer",
"exportImage": "Desa com a imatge",
"exportJSON": "",
"exportImage": "",
"export": "Exportar",
"exportToPng": "Exportar a PNG",
"exportToSvg": "Exportar a SNG",
"copyToClipboard": "Copiar al porta-retalls",
"copyPngToClipboard": "Copiar PNG al porta-retalls",
"scale": "Escala",
"save": "Desa al fitxer actual",
"save": "",
"saveAs": "Desar com",
"load": "Carregar",
"getShareableLink": "Obtenir enllaç per compartir",
@@ -220,15 +218,12 @@
"title": "Error"
},
"exportDialog": {
"disk_title": "Desa la disc",
"disk_title": "",
"disk_details": "",
"disk_button": "Desa en un fitxer",
"disk_button": "",
"link_title": "",
"link_details": "",
"link_button": "",
"excalidrawplus_description": "",
"excalidrawplus_button": "Exporta",
"excalidrawplus_exportError": "No és possible exportar a Excalidraw+ ara mateix..."
"link_button": ""
},
"helpDialog": {
"blog": "Llegiu el nostre blog",

View File

@@ -101,8 +101,6 @@
"viewMode": "Náhled",
"toggleExportColorScheme": "",
"share": "Sdílet",
"showStroke": "",
"showBackground": "",
"toggleTheme": "Přepnout tmavý řežim"
},
"buttons": {
@@ -225,10 +223,7 @@
"disk_button": "",
"link_title": "",
"link_details": "",
"link_button": "",
"excalidrawplus_description": "",
"excalidrawplus_button": "",
"excalidrawplus_exportError": ""
"link_button": ""
},
"helpDialog": {
"blog": "",

View File

@@ -101,8 +101,6 @@
"viewMode": "Ansichtsmodus",
"toggleExportColorScheme": "Exportfarbschema umschalten",
"share": "Teilen",
"showStroke": "Auswahl für Strichfarbe anzeigen",
"showBackground": "Auswahl für Hintergrundfarbe anzeigen",
"toggleTheme": "Design umschalten"
},
"buttons": {
@@ -225,10 +223,7 @@
"disk_button": "In Datei speichern",
"link_title": "Teilbarer Link",
"link_details": "Als schreibgeschützten Link exportieren.",
"link_button": "Als Link exportieren",
"excalidrawplus_description": "Speichere die Szene in deinem Excalidraw+ Arbeitsbereich.",
"excalidrawplus_button": "Exportieren",
"excalidrawplus_exportError": "Konnte nicht nach Excalidraw+ exportieren..."
"link_button": "Als Link exportieren"
},
"helpDialog": {
"blog": "Lies unseren Blog",

View File

@@ -101,8 +101,6 @@
"viewMode": "Λειτουργία προβολής",
"toggleExportColorScheme": "Εναλλαγή εξαγωγής θέματος χρωμάτων",
"share": "Κοινοποίηση",
"showStroke": "",
"showBackground": "",
"toggleTheme": ""
},
"buttons": {
@@ -225,10 +223,7 @@
"disk_button": "",
"link_title": "",
"link_details": "",
"link_button": "",
"excalidrawplus_description": "",
"excalidrawplus_button": "",
"excalidrawplus_exportError": ""
"link_button": ""
},
"helpDialog": {
"blog": "Διαβάστε το Blog μας",

View File

@@ -180,8 +180,6 @@
"linearElement": "Click to start multiple points, drag for single line",
"freeDraw": "Click and drag, release when you're finished",
"text": "Tip: you can also add text by double-clicking anywhere with the selection tool",
"text_selected": "Double-click or press ENTER to edit text",
"text_editing": "Press Escape or CtrlOrCmd+ENTER to finish editing",
"linearElementMulti": "Click on last point or press Escape or Enter to finish",
"lockAngle": "You can constrain angle by holding SHIFT",
"resize": "You can constrain proportions by holding SHIFT while resizing,\nhold ALT to resize from the center",
@@ -227,10 +225,7 @@
"disk_button": "Save to file",
"link_title": "Shareable link",
"link_details": "Export as a read-only link.",
"link_button": "Export to Link",
"excalidrawplus_description": "Save the scene to your Excalidraw+ workspace.",
"excalidrawplus_button": "Export",
"excalidrawplus_exportError": "Couldn't export to Excalidraw+ at this moment..."
"link_button": "Export to Link"
},
"helpDialog": {
"blog": "Read our blog",
@@ -238,18 +233,16 @@
"curvedArrow": "Curved arrow",
"curvedLine": "Curved line",
"documentation": "Documentation",
"doubleClick": "double-click",
"drag": "drag",
"editor": "Editor",
"editSelectedShape": "Edit selected shape (text/arrow/line)",
"github": "Found an issue? Submit",
"howto": "Follow our guides",
"or": "or",
"preventBinding": "Prevent arrow binding",
"shapes": "Shapes",
"shortcuts": "Keyboard shortcuts",
"textFinish": "Finish editing (text editor)",
"textNewLine": "Add new line (text editor)",
"textFinish": "Finish editing (text)",
"textNewLine": "Add new line (text)",
"title": "Help",
"view": "View",
"zoomToFit": "Zoom to fit all elements",

View File

@@ -42,8 +42,8 @@
"fontSize": "Tamaño de la fuente",
"fontFamily": "Tipo de fuente",
"onlySelected": "Sólo seleccionados",
"withBackground": "Fondo",
"exportEmbedScene": "Embeber escena",
"withBackground": "",
"exportEmbedScene": "",
"exportEmbedScene_details": "Los datos de escena se guardarán en el archivo PNG/SVG exportado, así la escena puede ser restaurada de la misma.\nEsto aumentará el tamaño del archivo exportado.",
"addWatermark": "Agregar \"Hecho con Excalidraw\"",
"handDrawn": "Dibujado a mano",
@@ -101,21 +101,19 @@
"viewMode": "Modo presentación",
"toggleExportColorScheme": "Cambiar el esquema de colores de exportación",
"share": "Compartir",
"showStroke": "Mostrar el selector de color del trazo",
"showBackground": "Mostrar el selector de color de fondo",
"toggleTheme": "Alternar tema"
},
"buttons": {
"clearReset": "Limpiar lienzo y reiniciar el color de fondo",
"exportJSON": "Exportar como archivo",
"exportImage": "Guardar como imagen",
"exportJSON": "",
"exportImage": "",
"export": "Exportar",
"exportToPng": "Exportar a PNG",
"exportToSvg": "Exportar a SVG",
"copyToClipboard": "Copiar al portapapeles",
"copyPngToClipboard": "Copiar PNG al portapapeles",
"scale": "Escalar",
"save": "Guardal al archivo actual",
"save": "",
"saveAs": "Guardar como",
"load": "Cargar",
"getShareableLink": "Obtener enlace para compartir",
@@ -124,7 +122,7 @@
"scrollBackToContent": "Volver al contenido",
"zoomIn": "Acercarse",
"zoomOut": "Alejarse",
"resetZoom": "Restablecer zoom",
"resetZoom": "Restablecer acercamiento",
"menu": "Menú",
"done": "Hecho",
"edit": "Editar",
@@ -220,15 +218,12 @@
"title": "Error"
},
"exportDialog": {
"disk_title": "Guardar en el disco",
"disk_details": "Exportar los datos de la escena a un archivo desde el cual se puede importar más tarde.",
"disk_button": "Guardar en el archivo",
"link_title": "Enlace para compartir",
"link_details": "Exportar como enlace de sólo lectura.",
"link_button": "Exportar al link",
"excalidrawplus_description": "Guarda la escena en tu espacio de trabajo de Excalidraw+.",
"excalidrawplus_button": "Exportar",
"excalidrawplus_exportError": "No se pudo exportar a Excalidraw+ en este momento..."
"disk_title": "",
"disk_details": "",
"disk_button": "",
"link_title": "",
"link_details": "",
"link_button": ""
},
"helpDialog": {
"blog": "Lee nuestro blog",
@@ -253,7 +248,7 @@
},
"encrypted": {
"tooltip": "Tus dibujos están cifrados de punto a punto, por lo que los servidores de Excalidraw nunca los verán.",
"link": "Entrada en el blog sobre cifrado de extremo a extremo"
"link": ""
},
"stats": {
"angle": "Ángulo",

View File

@@ -101,8 +101,6 @@
"viewMode": "",
"toggleExportColorScheme": "",
"share": "",
"showStroke": "",
"showBackground": "",
"toggleTheme": ""
},
"buttons": {
@@ -225,10 +223,7 @@
"disk_button": "",
"link_title": "",
"link_details": "",
"link_button": "",
"excalidrawplus_description": "",
"excalidrawplus_button": "",
"excalidrawplus_exportError": ""
"link_button": ""
},
"helpDialog": {
"blog": "بلاگ ما را بخوانید",

View File

@@ -42,8 +42,8 @@
"fontSize": "Kirjasinkoko",
"fontFamily": "Kirjasintyyppi",
"onlySelected": "Vain valitut",
"withBackground": "Taustalla",
"exportEmbedScene": "Upota työ",
"withBackground": "",
"exportEmbedScene": "",
"exportEmbedScene_details": "Teoksen tiedot tallennetaan PNG/SVG-tiedostoon, jolloin teoksen voi palauttaa siitä. Kasvattaa tallennetun tiedoston kokoa.",
"addWatermark": "Lisää \"Tehty Excalidrawilla\"",
"handDrawn": "Käsinkirjoitettu",
@@ -101,21 +101,19 @@
"viewMode": "Katselutila",
"toggleExportColorScheme": "Vaihda viennin väriteema",
"share": "Jaa",
"showStroke": "Näytä viivan värin valitsin",
"showBackground": "Näytä taustavärin valitsin",
"toggleTheme": "Vaihda teema"
},
"buttons": {
"clearReset": "Tyhjennä piirtoalue",
"exportJSON": "Vie tiedostoon",
"exportImage": "Tallenna kuvana",
"exportJSON": "",
"exportImage": "",
"export": "Vie",
"exportToPng": "Vie PNG-tiedostona",
"exportToSvg": "Vie SVG-tiedostona",
"copyToClipboard": "Kopioi leikepöydälle",
"copyPngToClipboard": "Kopioi PNG-tiedosto leikepöydälle",
"scale": "Koko",
"save": "Tallenna nykyiseen tiedostoon",
"save": "",
"saveAs": "Tallenna nimellä",
"load": "Avaa",
"getShareableLink": "Hae jaettava linkki",
@@ -220,15 +218,12 @@
"title": "Virhe"
},
"exportDialog": {
"disk_title": "Tallenna levylle",
"disk_details": "Vie työn tiedot tiedostoon, josta sen voi tuoda myöhemmin.",
"disk_button": "Tallenna tiedostoon",
"link_title": "Jaettava linkki",
"link_details": "Vie vain luku -linkkinä.",
"link_button": "Vie linkkinä",
"excalidrawplus_description": "Tallenna teos Excalidraw+ tilaan.",
"excalidrawplus_button": "Vie",
"excalidrawplus_exportError": "Ei voitu viedä Excalidraw+-palveluun tällä hetkellä..."
"disk_title": "",
"disk_details": "",
"disk_button": "",
"link_title": "",
"link_details": "",
"link_button": ""
},
"helpDialog": {
"blog": "Lue blogiamme",

View File

@@ -16,7 +16,7 @@
"delete": "Supprimer",
"copyStyles": "Copier les styles",
"pasteStyles": "Coller les styles",
"stroke": "Trait",
"stroke": "Contour",
"background": "Arrière-plan",
"fill": "Remplissage",
"strokeWidth": "Largeur du trait",
@@ -43,7 +43,7 @@
"fontFamily": "Police",
"onlySelected": "Uniquement la sélection",
"withBackground": "Arrière-plan",
"exportEmbedScene": "Intégrer la scène",
"exportEmbedScene": "Scène intégrée",
"exportEmbedScene_details": "Les données de scène seront enregistrées dans le fichier PNG/SVG exporté, afin que la scène puisse être restaurée à partir de celui-ci.\nCela augmentera la taille du fichier exporté.",
"addWatermark": "Ajouter \"Fait avec Excalidraw\"",
"handDrawn": "À main levée",
@@ -101,21 +101,19 @@
"viewMode": "Mode présentation",
"toggleExportColorScheme": "Activer/Désactiver l'export du thème de couleur",
"share": "Partager",
"showStroke": "Afficher le sélecteur de couleur de trait",
"showBackground": "Afficher le sélecteur de couleur d'arrière-plan",
"toggleTheme": "Changer le thème"
},
"buttons": {
"clearReset": "Réinitialiser le canevas",
"exportJSON": "Exporter comme fichier",
"exportJSON": "Exporter dans un fichier",
"exportImage": "Enregistrer comme image",
"export": "Exporter",
"exportToPng": "Enregistrer en PNG",
"exportToSvg": "Enregistrer en SVG",
"exportToPng": "Exporter en PNG",
"exportToSvg": "Exporter en SVG",
"copyToClipboard": "Copier dans le presse-papier",
"copyPngToClipboard": "Copier le PNG dans le presse-papier",
"copyPngToClipboard": "Copier le PNG vers le presse-papier",
"scale": "Échelle",
"save": "Enregistrer dans le fichier actuel",
"save": "Sauvegarder dans le fichier actuel",
"saveAs": "Enregistrer sous",
"load": "Ouvrir",
"getShareableLink": "Obtenir un lien de partage",
@@ -220,15 +218,12 @@
"title": "Erreur"
},
"exportDialog": {
"disk_title": "Enregistrer sur le disque",
"disk_details": "Exporter les données de la scène comme un fichier que vous pourrez importer ultérieurement.",
"disk_button": "Enregistrer comme fichier",
"link_title": "Lien partageable",
"disk_title": "Sauvegarder sur le disque",
"disk_details": "Exportez les données de la scène dans un fichier que vous pourrez importer ultérieurement.",
"disk_button": "Sauvegarder dans un fichier",
"link_title": "Lien à partager",
"link_details": "Exporter comme un lien en lecture seule.",
"link_button": "Exporter comme lien",
"excalidrawplus_description": "Enregistrer la scène dans votre espace de travail Excalidraw+.",
"excalidrawplus_button": "Exporter",
"excalidrawplus_exportError": "Impossible d'exporter vers Excalidraw+ pour le moment..."
"link_button": "Exporter vers un lien"
},
"helpDialog": {
"blog": "Lire notre blog",
@@ -272,8 +267,8 @@
},
"toast": {
"copyStyles": "Styles copiés.",
"copyToClipboard": "Copié dans le presse-papier.",
"copyToClipboardAsPng": "{{exportSelection}} copié dans le presse-papier en PNG\n({{exportColorScheme}})",
"copyToClipboard": "Copié vers le presse-papiers.",
"copyToClipboardAsPng": "{{exportSelection}} copié dans le presse-papiers en PNG\n({{exportColorScheme}})",
"fileSaved": "Fichier enregistré.",
"fileSavedToFilename": "Enregistré sous {filename}",
"canvas": "canevas",

View File

@@ -101,8 +101,6 @@
"viewMode": "מצב תצוגה",
"toggleExportColorScheme": "",
"share": "",
"showStroke": "",
"showBackground": "",
"toggleTheme": ""
},
"buttons": {
@@ -225,10 +223,7 @@
"disk_button": "",
"link_title": "",
"link_details": "",
"link_button": "",
"excalidrawplus_description": "",
"excalidrawplus_button": "",
"excalidrawplus_exportError": ""
"link_button": ""
},
"helpDialog": {
"blog": "קרא את הבלוג שלנו",

View File

@@ -101,8 +101,6 @@
"viewMode": "",
"toggleExportColorScheme": "",
"share": "",
"showStroke": "",
"showBackground": "",
"toggleTheme": ""
},
"buttons": {
@@ -225,10 +223,7 @@
"disk_button": "",
"link_title": "",
"link_details": "",
"link_button": "",
"excalidrawplus_description": "",
"excalidrawplus_button": "",
"excalidrawplus_exportError": ""
"link_button": ""
},
"helpDialog": {
"blog": "हमारा ब्लॉग पढे",

View File

@@ -101,8 +101,6 @@
"viewMode": "",
"toggleExportColorScheme": "",
"share": "",
"showStroke": "",
"showBackground": "",
"toggleTheme": ""
},
"buttons": {
@@ -225,10 +223,7 @@
"disk_button": "",
"link_title": "",
"link_details": "",
"link_button": "",
"excalidrawplus_description": "",
"excalidrawplus_button": "",
"excalidrawplus_exportError": ""
"link_button": ""
},
"helpDialog": {
"blog": "",

View File

@@ -101,8 +101,6 @@
"viewMode": "Mode tampilan",
"toggleExportColorScheme": "Ubah skema warna ekspor",
"share": "Bagikan",
"showStroke": "Tampilkan garis pengambil warna",
"showBackground": "Tampilkan latar pengambil warna",
"toggleTheme": "Ubah tema"
},
"buttons": {
@@ -225,10 +223,7 @@
"disk_button": "Simpan ke file",
"link_title": "Tautan",
"link_details": "Ekspor sebagai tautan yang hanya dibaca.",
"link_button": "Ekspor ke tautan",
"excalidrawplus_description": "Simpan pemandangan ke ruang kerja Excalidraw+ Anda.",
"excalidrawplus_button": "Ekspor",
"excalidrawplus_exportError": "Tidak dapat ekspor ke Excalidraw+ saat ini..."
"link_button": "Ekspor ke tautan"
},
"helpDialog": {
"blog": "Baca blog kami",

View File

@@ -101,8 +101,6 @@
"viewMode": "Modalità visualizzazione",
"toggleExportColorScheme": "Cambia lo schema di colori in esportazione",
"share": "Condividi",
"showStroke": "Mostra selettore colore del tratto",
"showBackground": "Mostra selettore colore di sfondo",
"toggleTheme": "Cambia tema"
},
"buttons": {
@@ -225,10 +223,7 @@
"disk_button": "Salva su file",
"link_title": "Link condivisibile",
"link_details": "Esporta come link di sola lettura.",
"link_button": "Esporta come Link",
"excalidrawplus_description": "Salva la scena nel tuo spazio di lavoro Excalidraw+.",
"excalidrawplus_button": "Esporta",
"excalidrawplus_exportError": "Non è stato possibile esportare su Excalidraw+ al questo momento..."
"link_button": "Esporta come Link"
},
"helpDialog": {
"blog": "Leggi il nostro blog",

View File

@@ -101,8 +101,6 @@
"viewMode": "閲覧モード",
"toggleExportColorScheme": "",
"share": "共有",
"showStroke": "",
"showBackground": "",
"toggleTheme": "テーマの切り替え"
},
"buttons": {
@@ -225,10 +223,7 @@
"disk_button": "ファイルへ保存",
"link_title": "共有可能なリンク",
"link_details": "読み取り専用リンクとしてエクスポート",
"link_button": "リンクとしてエクスポート",
"excalidrawplus_description": "Excalidraw+ ワークスペースにシーンを保存します。",
"excalidrawplus_button": "エクスポート",
"excalidrawplus_exportError": "Excalidraw+ にエクスポートできませんでした..."
"link_button": "リンクとしてエクスポート"
},
"helpDialog": {
"blog": "公式ブログを読む",

View File

@@ -20,7 +20,7 @@
"background": "Agilal",
"fill": "Taččart",
"strokeWidth": "Tehri n yizirig",
"strokeShape": "Talɣa n yizirig",
"strokeShape": "",
"strokeShape_gel": "",
"strokeShape_fountain": "",
"strokeShape_brush": "Amfezzu",
@@ -101,8 +101,6 @@
"viewMode": "Askar n tmuɣli",
"toggleExportColorScheme": "Sermed/sens asifeḍ usentel n yini",
"share": "Bḍu",
"showStroke": "Beqqeḍ amelqaḍ n yini n yizirig",
"showBackground": "Beqqeḍ amelqaḍ n yini n ugilal",
"toggleTheme": "Snifel asentel"
},
"buttons": {
@@ -225,10 +223,7 @@
"disk_button": "Sekles deg ufaylu",
"link_title": "Aseɣwen n beṭṭu",
"link_details": "Sifeḍ am useɣwen n tɣuri kan.",
"link_button": "Sifeḍ deg useɣwen",
"excalidrawplus_description": "Sekles asayes-inek•inem di tallunt n umahil Excalidraw+.",
"excalidrawplus_button": "Sifeḍ",
"excalidrawplus_exportError": "Ulamek asifeḍ ɣer Excalidraw+ akka tura..."
"link_button": "Sifeḍ deg useɣwen"
},
"helpDialog": {
"blog": "Ɣeṛ ablug-nneɣ",

View File

@@ -101,8 +101,6 @@
"viewMode": "보기 모드",
"toggleExportColorScheme": "",
"share": "",
"showStroke": "",
"showBackground": "",
"toggleTheme": ""
},
"buttons": {
@@ -225,10 +223,7 @@
"disk_button": "",
"link_title": "",
"link_details": "",
"link_button": "",
"excalidrawplus_description": "",
"excalidrawplus_button": "",
"excalidrawplus_exportError": ""
"link_button": ""
},
"helpDialog": {
"blog": "블로그 읽어보기",

View File

@@ -101,8 +101,6 @@
"viewMode": "",
"toggleExportColorScheme": "",
"share": "",
"showStroke": "",
"showBackground": "",
"toggleTheme": ""
},
"buttons": {
@@ -225,10 +223,7 @@
"disk_button": "",
"link_title": "",
"link_details": "",
"link_button": "",
"excalidrawplus_description": "",
"excalidrawplus_button": "",
"excalidrawplus_exportError": ""
"link_button": ""
},
"helpDialog": {
"blog": "",

View File

@@ -101,8 +101,6 @@
"viewMode": "",
"toggleExportColorScheme": "",
"share": "",
"showStroke": "",
"showBackground": "",
"toggleTheme": ""
},
"buttons": {
@@ -225,10 +223,7 @@
"disk_button": "",
"link_title": "",
"link_details": "",
"link_button": "",
"excalidrawplus_description": "",
"excalidrawplus_button": "",
"excalidrawplus_exportError": ""
"link_button": ""
},
"helpDialog": {
"blog": "",

View File

@@ -101,8 +101,6 @@
"viewMode": "Visningsmodus",
"toggleExportColorScheme": "Veksle eksport av fargepalett",
"share": "Del",
"showStroke": "Vis fargevelger for kantfarge",
"showBackground": "Vis fargevelger for bakgrunnsfarge",
"toggleTheme": "Veksle tema"
},
"buttons": {
@@ -225,10 +223,7 @@
"disk_button": "Lagre til fil",
"link_title": "Delbar lenke",
"link_details": "Eksporter som en skrivebeskyttet lenke.",
"link_button": "Eksporter til lenke",
"excalidrawplus_description": "Lagre scenen til ditt Excalidraw+ arbeidsområde.",
"excalidrawplus_button": "Eksporter",
"excalidrawplus_exportError": "Kunne ikke eksportere til Excalidraw+ for øyeblikket..."
"link_button": "Eksporter til lenke"
},
"helpDialog": {
"blog": "Les bloggen vår",

View File

@@ -101,8 +101,6 @@
"viewMode": "Weergavemodus",
"toggleExportColorScheme": "Kleurenschema exporteren aan/uit",
"share": "Deel",
"showStroke": "Toon lijn kleur kiezer",
"showBackground": "Toon achtergrondkleur kiezer",
"toggleTheme": "Thema aan/uit"
},
"buttons": {
@@ -225,10 +223,7 @@
"disk_button": "Opslaan naar bestand",
"link_title": "Deelbare link",
"link_details": "Exporteren als een alleen-lezen link.",
"link_button": "Exporteer naar link",
"excalidrawplus_description": "Sla de scène op in je Excalidraw+ werkruimte.",
"excalidrawplus_button": "Exporteren",
"excalidrawplus_exportError": "Kan op dit moment niet exporteren naar Excalidraw+..."
"link_button": "Exporteer naar link"
},
"helpDialog": {
"blog": "Lees onze blog",

View File

@@ -101,8 +101,6 @@
"viewMode": "Visningsmodus",
"toggleExportColorScheme": "Veksle eksport av fargepalett",
"share": "Del",
"showStroke": "",
"showBackground": "",
"toggleTheme": ""
},
"buttons": {
@@ -225,10 +223,7 @@
"disk_button": "",
"link_title": "",
"link_details": "",
"link_button": "",
"excalidrawplus_description": "",
"excalidrawplus_button": "",
"excalidrawplus_exportError": ""
"link_button": ""
},
"helpDialog": {
"blog": "Les bloggen vår",

View File

@@ -42,8 +42,8 @@
"fontSize": "Talha poliça",
"fontFamily": "Familha de poliça",
"onlySelected": "Seleccion sonque",
"withBackground": "Rèireplan",
"exportEmbedScene": "Scèna embarcada",
"withBackground": "",
"exportEmbedScene": "",
"exportEmbedScene_details": "Las donadas de scèna seràn enregistradas dins lo fichièr PNG/SVG exportat, per que la scèna pòsca èsser restaurada a partir daqueste fichièr.\nAumentarà la talha del fichièr exportat.",
"addWatermark": "Apondre «Fabricat amb Excalidraw»",
"handDrawn": "A la man levada",
@@ -101,21 +101,19 @@
"viewMode": "Mòde de vista",
"toggleExportColorScheme": "Alternar lesquèma de color dexpòrt",
"share": "Partejar",
"showStroke": "Mostrar lo selector de color de contorn",
"showBackground": "Mostrar lo selector de color de fons",
"toggleTheme": "Alternar tèma"
},
"buttons": {
"clearReset": "Reïnicializar lo canabàs",
"exportJSON": "Exportar en fichièr",
"exportImage": "Salvar coma imatge",
"exportJSON": "",
"exportImage": "",
"export": "Exportar",
"exportToPng": "Exportar en PNG",
"exportToSvg": "Exportar en SVG",
"copyToClipboard": "Copiar al quichapapièrs",
"copyPngToClipboard": "Copiar PNG al quichapapièrs",
"scale": "Escala",
"save": "Salvar al fichièr actual",
"save": "",
"saveAs": "Enregistrar jos",
"load": "Cargar",
"getShareableLink": "Obténer lo ligam de partatge",
@@ -220,15 +218,12 @@
"title": "Error"
},
"exportDialog": {
"disk_title": "Salvar al disc",
"disk_details": "Exportar las donadas de la scèna cap a un fichièr que podètz importar mai tard.",
"disk_button": "Salvar al fichièr",
"link_title": "Ligam de partejar",
"link_details": "Exportar coma un ligam de lectura sola.",
"link_button": "Exportar en ligam",
"excalidrawplus_description": "Enregistrar la scèna dins vòstre espaci de trabalh Excalidraw+.",
"excalidrawplus_button": "Exportar",
"excalidrawplus_exportError": "Export impossibla cap a Excalidraw+ pel moment..."
"disk_title": "",
"disk_details": "",
"disk_button": "",
"link_title": "",
"link_details": "",
"link_button": ""
},
"helpDialog": {
"blog": "Legir nòstre blog",

View File

@@ -101,8 +101,6 @@
"viewMode": "ਦੇਖੋ ਮੋਡ",
"toggleExportColorScheme": "",
"share": "ਸਾਂਝਾ ਕਰੋ",
"showStroke": "",
"showBackground": "",
"toggleTheme": "ਥੀਮ ਬਦਲੋ"
},
"buttons": {
@@ -225,10 +223,7 @@
"disk_button": "",
"link_title": "",
"link_details": "",
"link_button": "",
"excalidrawplus_description": "",
"excalidrawplus_button": "",
"excalidrawplus_exportError": ""
"link_button": ""
},
"helpDialog": {
"blog": "ਸਾਡਾ ਬਲੌਗ ਪੜ੍ਹੋ",

View File

@@ -1,39 +1,39 @@
{
"ar-SA": 92,
"bg-BG": 83,
"ca-ES": 94,
"ar-SA": 94,
"bg-BG": 85,
"ca-ES": 92,
"cs-CZ": 36,
"de-DE": 100,
"el-GR": 88,
"el-GR": 90,
"en": 100,
"es-ES": 98,
"fa-IR": 79,
"fi-FI": 99,
"es-ES": 93,
"fa-IR": 81,
"fi-FI": 94,
"fr-FR": 100,
"he-IL": 80,
"hi-IN": 82,
"hu-HU": 73,
"he-IL": 82,
"hi-IN": 84,
"hu-HU": 74,
"id-ID": 100,
"it-IT": 99,
"ja-JP": 98,
"kab-KAB": 98,
"ko-KR": 83,
"ja-JP": 99,
"kab-KAB": 97,
"ko-KR": 85,
"lv-LV": 17,
"my-MM": 68,
"my-MM": 69,
"nb-NO": 100,
"nl-NL": 100,
"nn-NO": 90,
"oc-FR": 100,
"pa-IN": 89,
"pl-PL": 85,
"pt-BR": 90,
"pt-PT": 92,
"nn-NO": 92,
"oc-FR": 95,
"pa-IN": 91,
"pl-PL": 87,
"pt-BR": 92,
"pt-PT": 94,
"ro-RO": 100,
"ru-RU": 97,
"sk-SK": 100,
"ru-RU": 95,
"sk-SK": 95,
"sv-SE": 100,
"tr-TR": 91,
"uk-UA": 97,
"zh-CN": 98,
"tr-TR": 93,
"uk-UA": 100,
"zh-CN": 94,
"zh-TW": 99
}

View File

@@ -101,8 +101,6 @@
"viewMode": "Tryb widoku",
"toggleExportColorScheme": "",
"share": "",
"showStroke": "",
"showBackground": "",
"toggleTheme": ""
},
"buttons": {
@@ -225,10 +223,7 @@
"disk_button": "",
"link_title": "",
"link_details": "",
"link_button": "",
"excalidrawplus_description": "",
"excalidrawplus_button": "",
"excalidrawplus_exportError": ""
"link_button": ""
},
"helpDialog": {
"blog": "Przeczytaj na naszym blogu",

View File

@@ -101,8 +101,6 @@
"viewMode": "Modo de visualização",
"toggleExportColorScheme": "Alternar esquema de cores de exportação",
"share": "Compartilhar",
"showStroke": "",
"showBackground": "",
"toggleTheme": ""
},
"buttons": {
@@ -225,10 +223,7 @@
"disk_button": "",
"link_title": "",
"link_details": "",
"link_button": "",
"excalidrawplus_description": "",
"excalidrawplus_button": "",
"excalidrawplus_exportError": ""
"link_button": ""
},
"helpDialog": {
"blog": "Leia o nosso blog",

View File

@@ -101,8 +101,6 @@
"viewMode": "Modo de visualização",
"toggleExportColorScheme": "Alternar esquema de cores de exportação",
"share": "Partilhar",
"showStroke": "",
"showBackground": "",
"toggleTheme": "Alternar tema"
},
"buttons": {
@@ -225,10 +223,7 @@
"disk_button": "",
"link_title": "",
"link_details": "",
"link_button": "",
"excalidrawplus_description": "",
"excalidrawplus_button": "",
"excalidrawplus_exportError": ""
"link_button": ""
},
"helpDialog": {
"blog": "Leia o nosso blog",

View File

@@ -101,8 +101,6 @@
"viewMode": "Mod de vizualizare",
"toggleExportColorScheme": "Comutare schemă de culori de export",
"share": "Distribuie",
"showStroke": "Afișare selector culoare contur",
"showBackground": "Afișare selector culoare fundal",
"toggleTheme": "Comutare temă"
},
"buttons": {
@@ -225,10 +223,7 @@
"disk_button": "Salvare în fișier",
"link_title": "URL partajabil",
"link_details": "Exportă ca URL doar în citire.",
"link_button": "Exportare în URL",
"excalidrawplus_description": "Salvează scena în spațiul de lucru Excalidraw+.",
"excalidrawplus_button": "Exportare",
"excalidrawplus_exportError": "Excalidraw+ nu a putut fi exportat în acest moment..."
"link_button": "Exportare în URL"
},
"helpDialog": {
"blog": "Citește blogul nostru",

View File

@@ -20,10 +20,10 @@
"background": "Фон",
"fill": "Заливка",
"strokeWidth": "Толщина штриха",
"strokeShape": "Стиль обводки",
"strokeShape_gel": "Гелевая ручка",
"strokeShape_fountain": "Фонтанная ручка",
"strokeShape_brush": "Кисть",
"strokeShape": "",
"strokeShape_gel": "",
"strokeShape_fountain": "",
"strokeShape_brush": "",
"strokeStyle": "Стиль обводки",
"strokeStyle_solid": "Сплошная",
"strokeStyle_dashed": "Пунктирная",
@@ -96,13 +96,11 @@
"centerHorizontally": "Центрировать по горизонтали",
"distributeHorizontally": "Распределить по горизонтали",
"distributeVertically": "Распределить по вертикали",
"flipHorizontal": "Переворот по горизонтали",
"flipVertical": "Переворот по вертикали",
"flipHorizontal": "",
"flipVertical": "",
"viewMode": "Вид",
"toggleExportColorScheme": "Экспортировать цветовую схему",
"share": "Поделиться",
"showStroke": "",
"showBackground": "",
"toggleTheme": ""
},
"buttons": {
@@ -225,10 +223,7 @@
"disk_button": "Сохранить в файл",
"link_title": "Поделитесь ссылкой",
"link_details": "Экспорт ссылки только для чтения.",
"link_button": "Экспорт в ссылку",
"excalidrawplus_description": "",
"excalidrawplus_button": "Экспорт",
"excalidrawplus_exportError": "Не удалось экспортировать в Excalidraw+ на данный момент..."
"link_button": "Экспорт в ссылку"
},
"helpDialog": {
"blog": "Прочитайте наш блог",
@@ -253,7 +248,7 @@
},
"encrypted": {
"tooltip": "Ваши данные защищены сквозным (End-to-end) шифрованием. Серверы Excalidraw никогда не получат доступ к ним.",
"link": "Запись блога о сквозном шифровании в Excalidraw"
"link": ""
},
"stats": {
"angle": "Угол",

View File

@@ -42,8 +42,8 @@
"fontSize": "Veľkosť písma",
"fontFamily": "Písmo",
"onlySelected": "Iba vybrané",
"withBackground": "Pozadie",
"exportEmbedScene": "Zahrnúť scénu",
"withBackground": "",
"exportEmbedScene": "",
"exportEmbedScene_details": "Údaje scény budú uložené do exportovaného PNG/SVG súboru, takže scéna z neho môže byť opäť obnovená.\nBude to mať za následok zvýšenie veľkosti súboru.",
"addWatermark": "Pridať \"Vytvorené s Excalidraw\"",
"handDrawn": "Ručne písané",
@@ -101,21 +101,19 @@
"viewMode": "Režim zobrazenia",
"toggleExportColorScheme": "Prepnúť exportovanie farebnej schémy",
"share": "Zdieľať",
"showStroke": "Zobraziť výber farby pre obrys",
"showBackground": "Zobraziť výber farby pre pozadie",
"toggleTheme": "Prepnúť tému"
},
"buttons": {
"clearReset": "Obnoviť plátno",
"exportJSON": "Exportovať do súboru",
"exportImage": "Uložiť ako obrázok",
"exportJSON": "",
"exportImage": "",
"export": "Exportovať",
"exportToPng": "Exportovať do PNG",
"exportToSvg": "Exportovať do SVG",
"copyToClipboard": "Kopírovať do schránky",
"copyPngToClipboard": "Kopírovať PNG do schránky",
"scale": "Mierka",
"save": "Uložiť do aktuálneho súboru",
"save": "",
"saveAs": "Uložiť ako",
"load": "Nahrať",
"getShareableLink": "Získať odkaz na zdieľanie",
@@ -220,15 +218,12 @@
"title": "Chyba"
},
"exportDialog": {
"disk_title": "Uložiť na disk",
"disk_details": "Exportovať údaje scény do súboru, z ktorého môžu byť neskôr importované.",
"disk_button": "Uložiť do súboru",
"link_title": "Odkaz na zdieľanie",
"link_details": "Exportovať ako odkaz iba na čítanie.",
"link_button": "Exportovať ako odkaz",
"excalidrawplus_description": "Uložiť scénu do vášho Excalidraw+ pracovného priestoru.",
"excalidrawplus_button": "Exportovať",
"excalidrawplus_exportError": "Nepodarilo sa vykonať export do Excalidraw+..."
"disk_title": "",
"disk_details": "",
"disk_button": "",
"link_title": "",
"link_details": "",
"link_button": ""
},
"helpDialog": {
"blog": "Prečítajte si náš blog",

View File

@@ -101,8 +101,6 @@
"viewMode": "Visningsläge",
"toggleExportColorScheme": "Växla färgschema för export",
"share": "Dela",
"showStroke": "Visa färgväljare för linjefärg",
"showBackground": "Visa färgväljare för bakgrundsfärg",
"toggleTheme": "Växla tema"
},
"buttons": {
@@ -225,10 +223,7 @@
"disk_button": "Spara till fil",
"link_title": "Delbar länk",
"link_details": "Exportera som en skrivskyddad länk.",
"link_button": "Exportera till länk",
"excalidrawplus_description": "Spara skissen till din Excalidraw+ arbetsyta.",
"excalidrawplus_button": "Exportera",
"excalidrawplus_exportError": "Det gick inte att exportera till Excalidraw+ just nu..."
"link_button": "Exportera till länk"
},
"helpDialog": {
"blog": "Läs vår blogg",

View File

@@ -101,8 +101,6 @@
"viewMode": "Görünüm modu",
"toggleExportColorScheme": "Renk şemasını dışa aktar/aktarma",
"share": "Paylaş",
"showStroke": "",
"showBackground": "",
"toggleTheme": "Temayı etkinleştir/devre dışı bırak"
},
"buttons": {
@@ -225,10 +223,7 @@
"disk_button": "",
"link_title": "",
"link_details": "",
"link_button": "",
"excalidrawplus_description": "",
"excalidrawplus_button": "",
"excalidrawplus_exportError": ""
"link_button": ""
},
"helpDialog": {
"blog": "Blog'umuzu okuyun",

View File

@@ -101,8 +101,6 @@
"viewMode": "Режим перегляду",
"toggleExportColorScheme": "Переключити колірну схему експорту",
"share": "Поділитися",
"showStroke": "",
"showBackground": "",
"toggleTheme": "Перемкнути тему"
},
"buttons": {
@@ -225,10 +223,7 @@
"disk_button": "Зберегти до файлу",
"link_title": "Доступ за посиланням",
"link_details": "Експортувати як посилання тільки для читання.",
"link_button": "Експортувати у посилання",
"excalidrawplus_description": "",
"excalidrawplus_button": "",
"excalidrawplus_exportError": ""
"link_button": "Експортувати у посилання"
},
"helpDialog": {
"blog": "Наш блог",

View File

@@ -20,7 +20,7 @@
"background": "背景",
"fill": "填充",
"strokeWidth": "描边宽度",
"strokeShape": "描边形状",
"strokeShape": "",
"strokeShape_gel": "中性笔",
"strokeShape_fountain": "钢笔",
"strokeShape_brush": "墨笔",
@@ -42,8 +42,8 @@
"fontSize": "字体大小",
"fontFamily": "字体",
"onlySelected": "仅被选中",
"withBackground": "背景",
"exportEmbedScene": "包含画布数据",
"withBackground": "",
"exportEmbedScene": "",
"exportEmbedScene_details": "画布数据将被保存到导出的 PNG/SVG 文件,以便恢复。\n将会增加导出的文件大小。",
"addWatermark": "添加 “使用 Excalidraw 创建” 水印",
"handDrawn": "手写",
@@ -101,21 +101,19 @@
"viewMode": "查看模式",
"toggleExportColorScheme": "切换导出配色方案",
"share": "分享",
"showStroke": "显示描边颜色选择器",
"showBackground": "显示背景颜色选择器",
"toggleTheme": "切换主题"
},
"buttons": {
"clearReset": "重置画布",
"exportJSON": "导出为文件",
"exportImage": "保存为图像",
"exportJSON": "",
"exportImage": "",
"export": "导出",
"exportToPng": "导出为 PNG",
"exportToSvg": "导出为 SVG",
"copyToClipboard": "复制到剪贴板",
"copyPngToClipboard": "复制 PNG 到剪切板",
"scale": "缩放",
"save": "保存至当前文件",
"save": "",
"saveAs": "保存为",
"load": "载入文件",
"getShareableLink": "获取共享链接",
@@ -166,7 +164,7 @@
"ellipse": "椭圆",
"arrow": "箭头",
"line": "线条",
"freedraw": "自由书写",
"freedraw": "",
"text": "文字",
"library": "库",
"lock": "绘制后保持所选的工具栏状态"
@@ -220,15 +218,12 @@
"title": "错误"
},
"exportDialog": {
"disk_title": "保存到本地",
"disk_details": "将画布数据导出为文件,以便以后导入",
"disk_button": "保存为文件",
"link_title": "分享链接",
"link_details": "导出为只读链接。",
"link_button": "导出链接",
"excalidrawplus_description": "",
"excalidrawplus_button": "",
"excalidrawplus_exportError": ""
"disk_title": "",
"disk_details": "",
"disk_button": "",
"link_title": "",
"link_details": "",
"link_button": ""
},
"helpDialog": {
"blog": "浏览我们的博客",

View File

@@ -101,8 +101,6 @@
"viewMode": "檢視模式",
"toggleExportColorScheme": "切換輸出配色",
"share": "共享",
"showStroke": "顯示線條檢色器",
"showBackground": "顯示背景檢色器",
"toggleTheme": "切換主題"
},
"buttons": {
@@ -225,10 +223,7 @@
"disk_button": "儲存至檔案",
"link_title": "可共享連結",
"link_details": "匯出為唯讀連結",
"link_button": "匯出為連結",
"excalidrawplus_description": "將此場景儲存至你的 Excalidraw+ 工作區",
"excalidrawplus_button": "輸出",
"excalidrawplus_exportError": "目前無法輸出至 Excalidraw+"
"link_button": "匯出為連結"
},
"helpDialog": {
"blog": "閱讀部落格",

View File

@@ -19,12 +19,6 @@ Please add the latest change on the top under the correct section.
### Features
- Expose [`FONT_FAMILY`](https://github.com/excalidraw/excalidraw/blob/master/src/packages/excalidraw/README.md#FONT_FAMILY) so that consumer can use when passing `initialData.appState.currentItemFontFamily`.
- Added prop [`autoFocus`](https://github.com/excalidraw/excalidraw/blob/master/src/packages/excalidraw/README.md#autoFocus) to focus the excalidraw component on page load when enabled, defaults to false [#3691](https://github.com/excalidraw/excalidraw/pull/3691).
Note: Earlier Excalidraw component was focussed by default on page load, you need to enable `autoFocus` prop to retain the same behaviour.
- Added prop `UIOptions.canvasActions.export.renderCustomUI` to support Custom UI rendering inside export dialog [#3666](https://github.com/excalidraw/excalidraw/pull/3666).
- Added prop `UIOptions.canvasActions.saveAsImage` to show/hide the **Save as image** button in the canvas actions. Defauls to `true` hence the **Save as Image** button is rendered [#3662](https://github.com/excalidraw/excalidraw/pull/3662).
@@ -37,10 +31,6 @@ Please add the latest change on the top under the correct section.
- `UIOptions.canvasActions.saveAsScene` is now renamed to `UiOptions.canvasActions.export.saveFileToDisk`. Defaults to `true` hence the **save file to disk** button is rendered inside the export dialog.
- `exportToBackend` is now renamed to `UIOptions.canvasActions.export.exportToBackend`. If this prop is not passed, the **shareable-link** button will not be rendered, same as before.
### Fixes
- Use excalidraw Id in elements so every element has unique id [#3696](https://github.com/excalidraw/excalidraw/pull/3696).
### Refactor
- #### BREAKING CHANGE

View File

@@ -153,7 +153,7 @@ To view the full example visit :point_down:
</details>
Since Excalidraw doesn't support server side rendering yet, you should render the component once the host is mounted.
Since Excalidraw doesn't support server side rendering yet so you will have to make sure the component is rendered once host is mounted.
```js
import { useState, useEffect } from "react";
@@ -161,7 +161,7 @@ export default function IndexPage() {
const [Comp, setComp] = useState(null);
useEffect(() => {
import("@excalidraw/excalidraw").then((comp) => setComp(comp.default));
}, []);
});
return <>{Comp && <Comp />}</>;
}
```

View File

@@ -159,7 +159,7 @@ To view the full example visit :point_down:
</details>
Since Excalidraw doesn't support server side rendering yet, you should render the component once the host is mounted.
Since Excalidraw doesn't support server side rendering yet so you will have to make sure the component is rendered once host is mounted.
```js
import { useState, useEffect } from "react";
@@ -167,7 +167,7 @@ export default function IndexPage() {
const [Comp, setComp] = useState(null);
useEffect(() => {
import("@excalidraw/excalidraw-next").then((comp) => setComp(comp.default));
}, []);
});
return <>{Comp && <Comp />}</>;
}
```
@@ -378,7 +378,6 @@ To view the full example visit :point_down:
| [`detectScroll`](#detectScroll) | boolean | true | Indicates whether to update the offsets when nearest ancestor is scrolled. |
| [`handleKeyboardGlobally`](#handleKeyboardGlobally) | boolean | false | Indicates whether to bind the keyboard events to document. |
| [`onLibraryChange`](#onLibraryChange) | <pre>(items: <a href="https://github.com/excalidraw/excalidraw/blob/master/src/types.ts#L200">LibraryItems</a>) => void &#124; Promise&lt;any&gt; </pre> | | The callback if supplied is triggered when the library is updated and receives the library items. |
| [`autoFocus`](#autoFocus) | boolean | false | Implies whether to focus the Excalidraw component on page load |
### Dimensions of Excalidraw
@@ -645,10 +644,6 @@ It is invoked with empty items when user clears the library. You can use this ca
The unique id of the excalidraw component. This can be used to identify the excalidraw component, for example importing the library items to the excalidraw component from where it was initiated when you have multiple excalidraw components rendered on the same page as shown in [multiple excalidraw demo](https://codesandbox.io/s/multiple-excalidraw-k1xx5).
### autoFocus
This prop implies whether to focus the Excalidraw component on page load. Defaults to false.
### Extra API's
#### `getSceneVersion`
@@ -840,21 +835,3 @@ This function returns a svg with the exported elements.
| exportBackground | boolean | true | Indicates whether background should be exported |
| viewBackgroundColor | string | #fff | The default background color |
| exportWithDarkMode | boolean | false | Indicates whether to export with dark mode |
### FONT_FAMILY
**_Signature_**
```js
import { FONT_FAMILY } from "./constants";
```
`FONT_FAMILY` contains all the font families used in `Excalidraw` as explained below
| Font Family | Description |
| ----------- | -------------------- |
| Virgil | The handwritten font |
| Helvetica | The Normal Font |
| Cacadia | The Code Font |
Defaults to `FONT_FAMILY.Virgil` unless passed in `initialData.appState.currentItemFontFamily`.

View File

@@ -33,7 +33,6 @@ const Excalidraw = (props: ExcalidrawProps) => {
detectScroll = true,
handleKeyboardGlobally = false,
onLibraryChange,
autoFocus = false,
} = props;
const canvasActions = props.UIOptions?.canvasActions;
@@ -93,7 +92,6 @@ const Excalidraw = (props: ExcalidrawProps) => {
detectScroll={detectScroll}
handleKeyboardGlobally={handleKeyboardGlobally}
onLibraryChange={onLibraryChange}
autoFocus={autoFocus}
/>
</InitializeApp>
);
@@ -180,4 +178,3 @@ export {
exportToSvg,
} from "../../packages/utils";
export { serializeAsJSON } from "../../data/json";
export { FONT_FAMILY } from "../../constants";

View File

@@ -56,7 +56,7 @@
"@babel/preset-env": "7.14.2",
"@babel/preset-react": "7.13.13",
"@babel/preset-typescript": "7.13.0",
"autoprefixer": "10.2.6",
"autoprefixer": "10.2.5",
"babel-loader": "8.2.2",
"babel-plugin-transform-class-properties": "6.24.1",
"cross-env": "7.0.3",
@@ -66,9 +66,9 @@
"postcss-loader": "5.3.0",
"sass-loader": "11.1.1",
"terser-webpack-plugin": "5.1.2",
"ts-loader": "9.2.3",
"typescript": "4.3.2",
"webpack": "5.38.1",
"ts-loader": "8.1.0",
"typescript": "4.2.4",
"webpack": "5.37.1",
"webpack-bundle-analyzer": "4.4.1",
"webpack-cli": "4.7.0"
},

View File

@@ -1240,15 +1240,15 @@ ansi-styles@^4.1.0:
dependencies:
color-convert "^2.0.1"
autoprefixer@10.2.6:
version "10.2.6"
resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.2.6.tgz#aadd9ec34e1c98d403e01950038049f0eb252949"
integrity sha512-8lChSmdU6dCNMCQopIf4Pe5kipkAGj/fvTMslCsih0uHpOrXOPUEVOmYMMqmw3cekQkSD7EhIeuYl5y0BLdKqg==
autoprefixer@10.2.5:
version "10.2.5"
resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.2.5.tgz#096a0337dbc96c0873526d7fef5de4428d05382d"
integrity sha512-7H4AJZXvSsn62SqZyJCP+1AWwOuoYpUfK6ot9vm0e87XD6mT8lDywc9D9OTJPMULyGcvmIxzTAMeG2Cc+YX+fA==
dependencies:
browserslist "^4.16.6"
caniuse-lite "^1.0.30001230"
browserslist "^4.16.3"
caniuse-lite "^1.0.30001196"
colorette "^1.2.2"
fraction.js "^4.1.1"
fraction.js "^4.0.13"
normalize-range "^0.1.2"
postcss-value-parser "^4.1.0"
@@ -1404,7 +1404,7 @@ braces@^3.0.1:
dependencies:
fill-range "^7.0.1"
browserslist@^4.14.5, browserslist@^4.16.3, browserslist@^4.16.6:
browserslist@^4.14.5, browserslist@^4.16.3:
version "4.16.6"
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.16.6.tgz#d7901277a5a88e554ed305b183ec9b0c08f66fa2"
integrity sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ==
@@ -1433,7 +1433,7 @@ callsites@^3.0.0:
resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73"
integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==
caniuse-lite@^1.0.30001219, caniuse-lite@^1.0.30001230:
caniuse-lite@^1.0.30001196, caniuse-lite@^1.0.30001219:
version "1.0.30001230"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001230.tgz#8135c57459854b2240b57a4a6786044bdc5a9f71"
integrity sha512-5yBd5nWCBS+jWKTcHOzXwo5xzcj4ePE/yjtkZyUV1BTUmrBaA9MRGC+e7mxnqXSA90CmCA8L3eKLaSUkt099IQ==
@@ -1551,6 +1551,11 @@ core-js@^2.4.0:
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.12.tgz#d9333dfa7b065e347cc5682219d6f690859cc2ec"
integrity sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==
core-util-is@~1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=
cosmiconfig@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.0.0.tgz#ef9b44d773959cae63ddecd122de23853b60f8d3"
@@ -1635,10 +1640,19 @@ emojis-list@^3.0.0:
resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78"
integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==
enhanced-resolve@^5.0.0, enhanced-resolve@^5.8.0:
version "5.8.2"
resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.8.2.tgz#15ddc779345cbb73e97c611cd00c01c1e7bf4d8b"
integrity sha512-F27oB3WuHDzvR2DOGNTaYy0D5o0cnrv8TeI482VM4kYgQd/FT9lUQwuNsJ0oOHtBUq7eiW5ytqzp7nBFknL+GA==
enhanced-resolve@^4.0.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.5.0.tgz#2f3cfd84dbe3b487f18f2db2ef1e064a571ca5ec"
integrity sha512-Nv9m36S/vxpsI+Hc4/ZGRs0n9mXqSWGGq49zxb/cJfPAQMbUtttJAlNPS4AQzaBdw/pKskw5bMbekT/Y7W/Wlg==
dependencies:
graceful-fs "^4.1.2"
memory-fs "^0.5.0"
tapable "^1.0.0"
enhanced-resolve@^5.8.0:
version "5.8.0"
resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.8.0.tgz#d9deae58f9d3773b6a111a5a46831da5be5c9ac0"
integrity sha512-Sl3KRpJA8OpprrtaIswVki3cWPiPKxXuFxJXBp+zNb6s6VwNWwFRUdtmzd2ReUut8n+sCPx7QCtQ7w5wfJhSgQ==
dependencies:
graceful-fs "^4.2.4"
tapable "^2.2.0"
@@ -1648,6 +1662,13 @@ envinfo@^7.7.3:
resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.7.4.tgz#c6311cdd38a0e86808c1c9343f667e4267c4a320"
integrity sha512-TQXTYFVVwwluWSFis6K2XKxgrD22jEv0FTuLCQI+OjH7rn93+iY0fSSFM5lrSxFY+H1+B0/cvvlamr3UsBivdQ==
errno@^0.1.3:
version "0.1.8"
resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.8.tgz#8bb3e9c7d463be4976ff888f76b4809ebc2e811f"
integrity sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==
dependencies:
prr "~1.0.1"
error-ex@^1.3.1:
version "1.3.2"
resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf"
@@ -1670,7 +1691,7 @@ escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5:
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=
eslint-scope@5.1.1:
eslint-scope@^5.1.1:
version "5.1.1"
resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c"
integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==
@@ -1767,10 +1788,10 @@ find-up@^4.0.0:
locate-path "^5.0.0"
path-exists "^4.0.0"
fraction.js@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.1.1.tgz#ac4e520473dae67012d618aab91eda09bcb400ff"
integrity sha512-MHOhvvxHTfRFpF1geTK9czMIZ6xclsEor2wkIGYYq+PxcQqT7vStJqjhe6S1TenZrMZzo+wlqOufBDVepUEgPg==
fraction.js@^4.0.13:
version "4.0.13"
resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.0.13.tgz#3c1c315fa16b35c85fffa95725a36fa729c69dfe"
integrity sha512-E1fz2Xs9ltlUp+qbiyx9wmt2n9dRzPsS11Jtdb8D2o+cC7wr9xkkKsVKJuBX0ST+LVS+LhLO+SbLJNtfWcJvXA==
function-bind@^1.1.1:
version "1.1.1"
@@ -1883,6 +1904,11 @@ indexes-of@^1.0.1:
resolved "https://registry.yarnpkg.com/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607"
integrity sha1-8w9xbI4r00bHtn0985FVZqfAVgc=
inherits@~2.0.3:
version "2.0.4"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
interpret@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/interpret/-/interpret-2.2.0.tgz#1a78a0b5965c40a5416d007ad6f50ad27c417df9"
@@ -1924,6 +1950,11 @@ is-stream@^2.0.0:
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.0.tgz#bde9c32680d6fae04129d6ac9d921ce7815f78e3"
integrity sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==
isarray@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=
isexe@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
@@ -2068,6 +2099,14 @@ make-dir@^3.0.2, make-dir@^3.1.0:
dependencies:
semver "^6.0.0"
memory-fs@^0.5.0:
version "0.5.0"
resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.5.0.tgz#324c01288b88652966d161db77838720845a8e3c"
integrity sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==
dependencies:
errno "^0.1.3"
readable-stream "^2.0.1"
merge-stream@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60"
@@ -2317,6 +2356,16 @@ postcss@^8.2.15:
nanoid "^3.1.23"
source-map-js "^0.6.2"
process-nextick-args@~2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==
prr@~1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476"
integrity sha1-0/wRS6BplaRexok/SEzrHXj19HY=
punycode@^2.1.0:
version "2.1.1"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
@@ -2329,6 +2378,19 @@ randombytes@^2.1.0:
dependencies:
safe-buffer "^5.1.0"
readable-stream@^2.0.1:
version "2.3.7"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57"
integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==
dependencies:
core-util-is "~1.0.0"
inherits "~2.0.3"
isarray "~1.0.0"
process-nextick-args "~2.0.0"
safe-buffer "~5.1.1"
string_decoder "~1.1.1"
util-deprecate "~1.0.1"
rechoir@^0.7.0:
version "0.7.0"
resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.7.0.tgz#32650fd52c21ab252aa5d65b19310441c7e03aca"
@@ -2419,7 +2481,7 @@ safe-buffer@^5.1.0:
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
safe-buffer@~5.1.1:
safe-buffer@~5.1.0, safe-buffer@~5.1.1:
version "5.1.2"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
@@ -2540,6 +2602,13 @@ source-map@~0.7.2:
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383"
integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==
string_decoder@~1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8"
integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==
dependencies:
safe-buffer "~5.1.0"
strip-ansi@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf"
@@ -2571,6 +2640,11 @@ supports-color@^7.0.0, supports-color@^7.1.0:
dependencies:
has-flag "^4.0.0"
tapable@^1.0.0:
version "1.1.3"
resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2"
integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==
tapable@^2.1.1, tapable@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.0.tgz#5c373d281d9c672848213d0e037d1c4165ab426b"
@@ -2619,13 +2693,14 @@ totalist@^1.0.0:
resolved "https://registry.yarnpkg.com/totalist/-/totalist-1.1.0.tgz#a4d65a3e546517701e3e5c37a47a70ac97fe56df"
integrity sha512-gduQwd1rOdDMGxFG1gEvhV88Oirdo2p+KjoYFU7k2g+i7n6AFFbDQ5kMPUsW0pNbfQsB/cwXvT1i4Bue0s9g5g==
ts-loader@9.2.3:
version "9.2.3"
resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-9.2.3.tgz#dc3b6362a4d4382493cd4f138d345f419656de68"
integrity sha512-sEyWiU3JMHBL55CIeC4iqJQadI0U70A5af0kvgbNLHVNz2ACztQg0j/9x10bjjIht8WfFYLKfn4L6tkZ+pu+8Q==
ts-loader@8.1.0:
version "8.1.0"
resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-8.1.0.tgz#d6292487df279c7cc79b6d3b70bb9d31682b693e"
integrity sha512-YiQipGGAFj2zBfqLhp28yUvPP9jUGqHxRzrGYuc82Z2wM27YIHbElXiaZDc93c3x0mz4zvBmS6q/DgExpdj37A==
dependencies:
chalk "^4.1.0"
enhanced-resolve "^5.0.0"
enhanced-resolve "^4.0.0"
loader-utils "^2.0.0"
micromatch "^4.0.0"
semver "^7.3.4"
@@ -2634,10 +2709,10 @@ tslib@^1.9.0:
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
typescript@4.3.2:
version "4.3.2"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.3.2.tgz#399ab18aac45802d6f2498de5054fcbbe716a805"
integrity sha512-zZ4hShnmnoVnAHpVHWpTcxdv7dWP60S2FsydQLV8V5PbS3FifjWFFRiHSWpDJahly88PRyV5teTSLoq4eG7mKw==
typescript@4.2.4:
version "4.2.4"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.2.4.tgz#8610b59747de028fda898a8aef0e103f156d0961"
integrity sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg==
unicode-canonical-property-names-ecmascript@^1.0.4:
version "1.0.4"
@@ -2674,7 +2749,7 @@ uri-js@^4.2.2:
dependencies:
punycode "^2.1.0"
util-deprecate@^1.0.2:
util-deprecate@^1.0.2, util-deprecate@~1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
@@ -2684,10 +2759,10 @@ v8-compile-cache@^2.2.0:
resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.2.0.tgz#9471efa3ef9128d2f7c6a7ca39c4dd6b5055b132"
integrity sha512-gTpR5XQNKFwOd4clxfnhaqvfqMpqEwr4tOtCyz4MtYZX2JYhfr1JvBFKdS+7K/9rfpZR3VLX+YWBbKoxCgS43Q==
watchpack@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.2.0.tgz#47d78f5415fe550ecd740f99fe2882323a58b1ce"
integrity sha512-up4YAn/XHgZHIxFBVCdlMiWDj6WaLKpwVeGQk2I5thdYxF/KmF0aaz6TfJZ/hfl1h/XlcDr7k1KH7ThDagpFaA==
watchpack@^2.0.0:
version "2.1.1"
resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.1.1.tgz#e99630550fca07df9f90a06056987baa40a689c7"
integrity sha512-Oo7LXCmc1eE1AjyuSBmtC3+Wy4HcV8PxWh2kP6fOl8yTlNS7r0K9l1ao2lrrUza7V39Y3D/BbJgY8VeSlc5JKw==
dependencies:
glob-to-regexp "^0.4.1"
graceful-fs "^4.1.2"
@@ -2742,18 +2817,18 @@ webpack-sources@^1.1.0:
source-list-map "^2.0.0"
source-map "~0.6.1"
webpack-sources@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-2.3.0.tgz#9ed2de69b25143a4c18847586ad9eccb19278cfa"
integrity sha512-WyOdtwSvOML1kbgtXbTDnEW0jkJ7hZr/bDByIwszhWd/4XX1A3XMkrbFMsuH4+/MfLlZCUzlAdg4r7jaGKEIgQ==
webpack-sources@^2.1.1:
version "2.2.0"
resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-2.2.0.tgz#058926f39e3d443193b6c31547229806ffd02bac"
integrity sha512-bQsA24JLwcnWGArOKUxYKhX3Mz/nK1Xf6hxullKERyktjNMC4x8koOeaDNTA2fEJ09BdWLbM/iTW0ithREUP0w==
dependencies:
source-list-map "^2.0.1"
source-map "^0.6.1"
webpack@5.38.1:
version "5.38.1"
resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.38.1.tgz#5224c7f24c18e729268d3e3bc97240d6e880258e"
integrity sha512-OqRmYD1OJbHZph6RUMD93GcCZy4Z4wC0ele4FXyYF0J6AxO1vOSuIlU1hkS/lDlR9CDYBz64MZRmdbdnFFoT2g==
webpack@5.37.1:
version "5.37.1"
resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.37.1.tgz#2deb5acd350583c1ab9338471f323381b0b0c14b"
integrity sha512-btZjGy/hSjCAAVHw+cKG+L0M+rstlyxbO2C+BOTaQ5/XAnxkDrP5sVbqWhXgo4pL3X2dcOib6rqCP20Zr9PLow==
dependencies:
"@types/eslint-scope" "^3.7.0"
"@types/estree" "^0.0.47"
@@ -2765,7 +2840,7 @@ webpack@5.38.1:
chrome-trace-event "^1.0.2"
enhanced-resolve "^5.8.0"
es-module-lexer "^0.4.0"
eslint-scope "5.1.1"
eslint-scope "^5.1.1"
events "^3.2.0"
glob-to-regexp "^0.4.1"
graceful-fs "^4.2.4"
@@ -2776,8 +2851,8 @@ webpack@5.38.1:
schema-utils "^3.0.0"
tapable "^2.1.1"
terser-webpack-plugin "^5.1.1"
watchpack "^2.2.0"
webpack-sources "^2.3.0"
watchpack "^2.0.0"
webpack-sources "^2.1.1"
which@^2.0.1:
version "2.0.2"
@@ -2792,9 +2867,9 @@ wildcard@^2.0.0:
integrity sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==
ws@^7.3.1:
version "7.4.6"
resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c"
integrity sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==
version "7.4.3"
resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.3.tgz#1f9643de34a543b8edb124bdcbc457ae55a6e5cd"
integrity sha512-hr6vCR76GsossIRsr8OLR9acVVm1jyfEWvhbNjtgPOrfvAlKzvyeg/P6r8RuDjRyrcQoPQT7K0DGEPc7Ae6jzA==
yallist@^4.0.0:
version "4.0.0"

View File

@@ -36,10 +36,10 @@
"devDependencies": {
"@babel/core": "7.14.3",
"@babel/plugin-transform-arrow-functions": "7.13.0",
"@babel/plugin-transform-async-to-generator": "7.14.5",
"@babel/plugin-transform-async-to-generator": "7.13.0",
"@babel/plugin-transform-runtime": "^7.12.10",
"@babel/plugin-transform-typescript": "7.14.5",
"@babel/preset-env": "7.14.5",
"@babel/plugin-transform-typescript": "7.14.3",
"@babel/preset-env": "7.14.1",
"@babel/preset-typescript": "7.13.0",
"babel-loader": "8.2.2",
"babel-plugin-transform-class-properties": "6.24.1",
@@ -47,8 +47,8 @@
"css-loader": "5.2.6",
"file-loader": "6.2.0",
"sass-loader": "11.1.1",
"ts-loader": "9.2.3",
"webpack": "5.38.1",
"ts-loader": "8.1.0",
"webpack": "5.37.1",
"webpack-bundle-analyzer": "4.4.2",
"webpack-cli": "4.7.0"
},

File diff suppressed because it is too large Load Diff

View File

@@ -4,7 +4,7 @@ import { getCommonBounds } from "../element/bounds";
import { renderScene, renderSceneToSvg } from "../renderer/renderScene";
import { distance, SVG_NS } from "../utils";
import { AppState } from "../types";
import { DEFAULT_EXPORT_PADDING, THEME_FILTER } from "../constants";
import { THEME_FILTER } from "../constants";
import { getDefaultAppState } from "../appState";
export const SVG_EXPORT_TAG = `<!-- svg-source:excalidraw -->`;
@@ -14,34 +14,39 @@ export const exportToCanvas = (
appState: AppState,
{
exportBackground,
exportPadding = DEFAULT_EXPORT_PADDING,
exportPadding = 10,
viewBackgroundColor,
scale = 1,
}: {
exportBackground: boolean;
exportPadding?: number;
scale?: number;
viewBackgroundColor: string;
},
createCanvas: (
width: number,
height: number,
) => { canvas: HTMLCanvasElement; scale: number } = (width, height) => {
const canvas = document.createElement("canvas");
canvas.width = width * appState.exportScale;
canvas.height = height * appState.exportScale;
return { canvas, scale: appState.exportScale };
const tempCanvas = document.createElement("canvas");
tempCanvas.width = width * scale;
tempCanvas.height = height * scale;
return { canvas: tempCanvas, scale };
},
) => {
const [minX, minY, width, height] = getCanvasSize(elements, exportPadding);
const { canvas, scale = 1 } = createCanvas(width, height);
const { canvas: tempCanvas, scale: newScale = scale } = createCanvas(
width,
height,
);
renderScene(
elements,
appState,
null,
scale,
rough.canvas(canvas),
canvas,
newScale,
rough.canvas(tempCanvas),
tempCanvas,
{
viewBackgroundColor: exportBackground ? viewBackgroundColor : null,
exportWithDarkMode: appState.exportWithDarkMode,
@@ -62,22 +67,22 @@ export const exportToCanvas = (
},
);
return canvas;
return tempCanvas;
};
export const exportToSvg = (
elements: readonly NonDeletedExcalidrawElement[],
{
exportBackground,
exportPadding = DEFAULT_EXPORT_PADDING,
exportPadding = 10,
viewBackgroundColor,
exportWithDarkMode,
exportScale = 1,
scale = 1,
metadata = "",
}: {
exportBackground: boolean;
exportPadding?: number;
exportScale?: number;
scale?: number;
viewBackgroundColor: string;
exportWithDarkMode?: boolean;
metadata?: string;
@@ -90,8 +95,8 @@ export const exportToSvg = (
svgRoot.setAttribute("version", "1.1");
svgRoot.setAttribute("xmlns", SVG_NS);
svgRoot.setAttribute("viewBox", `0 0 ${width} ${height}`);
svgRoot.setAttribute("width", `${width * exportScale}`);
svgRoot.setAttribute("height", `${height * exportScale}`);
svgRoot.setAttribute("width", `${width * scale}`);
svgRoot.setAttribute("height", `${height * scale}`);
if (exportWithDarkMode) {
svgRoot.setAttribute("filter", THEME_FILTER);
}

View File

@@ -1,11 +1,6 @@
import "@testing-library/jest-dom";
import "jest-canvas-mock";
jest.mock("nanoid", () => {
return {
nanoid: jest.fn(() => "test-id"),
};
});
// ReactDOM is located inside index.tsx file
// as a result, we need a place for it to render into
const element = document.createElement("div");

View File

@@ -28,7 +28,6 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportScale": 1,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
@@ -44,7 +43,6 @@ Object {
"offsetLeft": 20,
"offsetTop": 10,
"openMenu": null,
"openPopup": null,
"pasteDialog": Object {
"data": null,
"shown": false,
@@ -195,7 +193,6 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportScale": 1,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
@@ -211,7 +208,6 @@ Object {
"offsetLeft": 20,
"offsetTop": 10,
"openMenu": null,
"openPopup": null,
"pasteDialog": Object {
"data": null,
"shown": false,
@@ -508,7 +504,6 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportScale": 1,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
@@ -524,7 +519,6 @@ Object {
"offsetLeft": 20,
"offsetTop": 10,
"openMenu": null,
"openPopup": null,
"pasteDialog": Object {
"data": null,
"shown": false,
@@ -821,7 +815,6 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportScale": 1,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
@@ -837,7 +830,6 @@ Object {
"offsetLeft": 20,
"offsetTop": 10,
"openMenu": null,
"openPopup": null,
"pasteDialog": Object {
"data": null,
"shown": false,
@@ -988,7 +980,6 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportScale": 1,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
@@ -1004,7 +995,6 @@ Object {
"offsetLeft": 20,
"offsetTop": 10,
"openMenu": null,
"openPopup": null,
"pasteDialog": Object {
"data": null,
"shown": false,
@@ -1188,7 +1178,6 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportScale": 1,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
@@ -1204,7 +1193,6 @@ Object {
"offsetLeft": 20,
"offsetTop": 10,
"openMenu": null,
"openPopup": null,
"pasteDialog": Object {
"data": null,
"shown": false,
@@ -1441,7 +1429,6 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportScale": 1,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
@@ -1457,7 +1444,6 @@ Object {
"offsetLeft": 20,
"offsetTop": 10,
"openMenu": null,
"openPopup": null,
"pasteDialog": Object {
"data": null,
"shown": false,
@@ -1772,7 +1758,6 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportScale": 1,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
@@ -1787,8 +1772,7 @@ Object {
"name": "Untitled-201933152653",
"offsetLeft": 20,
"offsetTop": 10,
"openMenu": null,
"openPopup": "backgroundColorPicker",
"openMenu": "backgroundColorPicker",
"pasteDialog": Object {
"data": null,
"shown": false,
@@ -2505,7 +2489,6 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportScale": 1,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
@@ -2521,7 +2504,6 @@ Object {
"offsetLeft": 20,
"offsetTop": 10,
"openMenu": null,
"openPopup": null,
"pasteDialog": Object {
"data": null,
"shown": false,
@@ -2818,7 +2800,6 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportScale": 1,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
@@ -2834,7 +2815,6 @@ Object {
"offsetLeft": 20,
"offsetTop": 10,
"openMenu": null,
"openPopup": null,
"pasteDialog": Object {
"data": null,
"shown": false,
@@ -3131,7 +3111,6 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportScale": 1,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
@@ -3147,7 +3126,6 @@ Object {
"offsetLeft": 20,
"offsetTop": 10,
"openMenu": null,
"openPopup": null,
"pasteDialog": Object {
"data": null,
"shown": false,
@@ -3518,7 +3496,6 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportScale": 1,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
@@ -3534,7 +3511,6 @@ Object {
"offsetLeft": 20,
"offsetTop": 10,
"openMenu": null,
"openPopup": null,
"pasteDialog": Object {
"data": null,
"shown": false,
@@ -3777,7 +3753,6 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportScale": 1,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
@@ -3793,7 +3768,6 @@ Object {
"offsetLeft": 20,
"offsetTop": 10,
"openMenu": null,
"openPopup": null,
"pasteDialog": Object {
"data": null,
"shown": false,
@@ -4111,7 +4085,6 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportScale": 1,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
@@ -4127,7 +4100,6 @@ Object {
"offsetLeft": 20,
"offsetTop": 10,
"openMenu": null,
"openPopup": null,
"pasteDialog": Object {
"data": null,
"shown": false,
@@ -4213,7 +4185,6 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportScale": 1,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
@@ -4229,7 +4200,6 @@ Object {
"offsetLeft": 20,
"offsetTop": 10,
"openMenu": null,
"openPopup": null,
"pasteDialog": Object {
"data": null,
"shown": false,
@@ -4293,7 +4263,6 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportScale": 1,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
@@ -4309,7 +4278,6 @@ Object {
"offsetLeft": 20,
"offsetTop": 10,
"openMenu": null,
"openPopup": null,
"pasteDialog": Object {
"data": null,
"shown": false,

View File

@@ -2,12 +2,12 @@
exports[`<Excalidraw/> Test UIOptions prop Test canvasActions should not hide any UI element when canvasActions is "undefined" 1`] = `
<section
aria-labelledby="test-id-canvasActions-title"
aria-labelledby="canvasActions-title"
class="zen-mode-transition"
>
<h2
class="visually-hidden"
id="test-id-canvasActions-title"
id="canvasActions-title"
>
Canvas actions
</h2>
@@ -201,12 +201,12 @@ exports[`<Excalidraw/> Test UIOptions prop Test canvasActions should not hide an
exports[`<Excalidraw/> Test UIOptions prop should not hide any UI element when the UIOptions prop is "undefined" 1`] = `
<section
aria-labelledby="test-id-canvasActions-title"
aria-labelledby="canvasActions-title"
class="zen-mode-transition"
>
<h2
class="visually-hidden"
id="test-id-canvasActions-title"
id="canvasActions-title"
>
Canvas actions
</h2>

View File

@@ -28,7 +28,6 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportScale": 1,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
@@ -44,7 +43,6 @@ Object {
"offsetLeft": 0,
"offsetTop": 0,
"openMenu": null,
"openPopup": null,
"pasteDialog": Object {
"data": null,
"shown": false,
@@ -497,7 +495,6 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportScale": 1,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
@@ -513,7 +510,6 @@ Object {
"offsetLeft": 0,
"offsetTop": 0,
"openMenu": null,
"openPopup": null,
"pasteDialog": Object {
"data": null,
"shown": false,
@@ -972,7 +968,6 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportScale": 1,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
@@ -988,7 +983,6 @@ Object {
"offsetLeft": 0,
"offsetTop": 0,
"openMenu": null,
"openPopup": null,
"pasteDialog": Object {
"data": null,
"shown": false,
@@ -1762,7 +1756,6 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportScale": 1,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
@@ -1778,7 +1771,6 @@ Object {
"offsetLeft": 0,
"offsetTop": 0,
"openMenu": null,
"openPopup": null,
"pasteDialog": Object {
"data": null,
"shown": false,
@@ -1970,7 +1962,6 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportScale": 1,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
@@ -1986,7 +1977,6 @@ Object {
"offsetLeft": 0,
"offsetTop": 0,
"openMenu": null,
"openPopup": null,
"pasteDialog": Object {
"data": null,
"shown": false,
@@ -2436,7 +2426,6 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportScale": 1,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
@@ -2452,7 +2441,6 @@ Object {
"offsetLeft": 0,
"offsetTop": 0,
"openMenu": null,
"openPopup": null,
"pasteDialog": Object {
"data": null,
"shown": false,
@@ -2693,7 +2681,6 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportScale": 1,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
@@ -2709,7 +2696,6 @@ Object {
"offsetLeft": 0,
"offsetTop": 0,
"openMenu": null,
"openPopup": null,
"pasteDialog": Object {
"data": null,
"shown": false,
@@ -2860,7 +2846,6 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportScale": 1,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
@@ -2876,7 +2861,6 @@ Object {
"offsetLeft": 0,
"offsetTop": 0,
"openMenu": null,
"openPopup": null,
"pasteDialog": Object {
"data": null,
"shown": false,
@@ -3309,7 +3293,6 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportScale": 1,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
@@ -3324,8 +3307,7 @@ Object {
"name": "Untitled-201933152653",
"offsetLeft": 0,
"offsetTop": 0,
"openMenu": null,
"openPopup": "strokeColorPicker",
"openMenu": "strokeColorPicker",
"pasteDialog": Object {
"data": null,
"shown": false,
@@ -3550,7 +3532,6 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportScale": 1,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
@@ -3566,7 +3547,6 @@ Object {
"offsetLeft": 0,
"offsetTop": 0,
"openMenu": null,
"openPopup": null,
"pasteDialog": Object {
"data": null,
"shown": false,
@@ -3758,7 +3738,6 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportScale": 1,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
@@ -3774,7 +3753,6 @@ Object {
"offsetLeft": 0,
"offsetTop": 0,
"openMenu": null,
"openPopup": null,
"pasteDialog": Object {
"data": null,
"shown": false,
@@ -4007,7 +3985,6 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportScale": 1,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
@@ -4023,7 +4000,6 @@ Object {
"offsetLeft": 0,
"offsetTop": 0,
"openMenu": null,
"openPopup": null,
"pasteDialog": Object {
"data": null,
"shown": false,
@@ -4263,7 +4239,6 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportScale": 1,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
@@ -4279,7 +4254,6 @@ Object {
"offsetLeft": 0,
"offsetTop": 0,
"openMenu": null,
"openPopup": null,
"pasteDialog": Object {
"data": null,
"shown": false,
@@ -4651,7 +4625,6 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportScale": 1,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
@@ -4667,7 +4640,6 @@ Object {
"offsetLeft": 0,
"offsetTop": 0,
"openMenu": null,
"openPopup": null,
"pasteDialog": Object {
"data": null,
"shown": false,
@@ -4950,7 +4922,6 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportScale": 1,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
@@ -4966,7 +4937,6 @@ Object {
"offsetLeft": 0,
"offsetTop": 0,
"openMenu": null,
"openPopup": null,
"pasteDialog": Object {
"data": null,
"shown": false,
@@ -5227,7 +5197,6 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportScale": 1,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
@@ -5243,7 +5212,6 @@ Object {
"offsetLeft": 0,
"offsetTop": 0,
"openMenu": null,
"openPopup": null,
"pasteDialog": Object {
"data": null,
"shown": false,
@@ -5438,7 +5406,6 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportScale": 1,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
@@ -5454,7 +5421,6 @@ Object {
"offsetLeft": 0,
"offsetTop": 0,
"openMenu": null,
"openPopup": null,
"pasteDialog": Object {
"data": null,
"shown": false,
@@ -5605,7 +5571,6 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportScale": 1,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
@@ -5621,7 +5586,6 @@ Object {
"offsetLeft": 0,
"offsetTop": 0,
"openMenu": null,
"openPopup": null,
"pasteDialog": Object {
"data": null,
"shown": false,
@@ -6066,7 +6030,6 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportScale": 1,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
@@ -6082,7 +6045,6 @@ Object {
"offsetLeft": 0,
"offsetTop": 0,
"openMenu": null,
"openPopup": null,
"pasteDialog": Object {
"data": null,
"shown": false,
@@ -6389,7 +6351,6 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportScale": 1,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
@@ -6405,7 +6366,6 @@ Object {
"offsetLeft": 0,
"offsetTop": 0,
"openMenu": null,
"openPopup": null,
"pasteDialog": Object {
"data": null,
"shown": false,
@@ -8447,7 +8407,6 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportScale": 1,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
@@ -8463,7 +8422,6 @@ Object {
"offsetLeft": 0,
"offsetTop": 0,
"openMenu": null,
"openPopup": null,
"pasteDialog": Object {
"data": null,
"shown": false,
@@ -8814,7 +8772,6 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportScale": 1,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
@@ -8830,7 +8787,6 @@ Object {
"offsetLeft": 0,
"offsetTop": 0,
"openMenu": null,
"openPopup": null,
"pasteDialog": Object {
"data": null,
"shown": false,
@@ -9071,7 +9027,6 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportScale": 1,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
@@ -9087,7 +9042,6 @@ Object {
"offsetLeft": 0,
"offsetTop": 0,
"openMenu": null,
"openPopup": null,
"pasteDialog": Object {
"data": null,
"shown": false,
@@ -9292,7 +9246,6 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportScale": 1,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
@@ -9308,7 +9261,6 @@ Object {
"offsetLeft": 0,
"offsetTop": 0,
"openMenu": null,
"openPopup": null,
"pasteDialog": Object {
"data": null,
"shown": false,
@@ -9576,7 +9528,6 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportScale": 1,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
@@ -9592,7 +9543,6 @@ Object {
"offsetLeft": 0,
"offsetTop": 0,
"openMenu": null,
"openPopup": null,
"pasteDialog": Object {
"data": null,
"shown": false,
@@ -9743,7 +9693,6 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportScale": 1,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
@@ -9759,7 +9708,6 @@ Object {
"offsetLeft": 0,
"offsetTop": 0,
"openMenu": null,
"openPopup": null,
"pasteDialog": Object {
"data": null,
"shown": false,
@@ -9910,7 +9858,6 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportScale": 1,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
@@ -9926,7 +9873,6 @@ Object {
"offsetLeft": 0,
"offsetTop": 0,
"openMenu": null,
"openPopup": null,
"pasteDialog": Object {
"data": null,
"shown": false,
@@ -10077,7 +10023,6 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportScale": 1,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
@@ -10093,7 +10038,6 @@ Object {
"offsetLeft": 0,
"offsetTop": 0,
"openMenu": null,
"openPopup": null,
"pasteDialog": Object {
"data": null,
"shown": false,
@@ -10274,7 +10218,6 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportScale": 1,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
@@ -10290,7 +10233,6 @@ Object {
"offsetLeft": 0,
"offsetTop": 0,
"openMenu": null,
"openPopup": null,
"pasteDialog": Object {
"data": null,
"shown": false,
@@ -10471,7 +10413,6 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportScale": 1,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
@@ -10487,7 +10428,6 @@ Object {
"offsetLeft": 0,
"offsetTop": 0,
"openMenu": null,
"openPopup": null,
"pasteDialog": Object {
"data": null,
"shown": false,
@@ -10680,7 +10620,6 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportScale": 1,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
@@ -10696,7 +10635,6 @@ Object {
"offsetLeft": 0,
"offsetTop": 0,
"openMenu": null,
"openPopup": null,
"pasteDialog": Object {
"data": null,
"shown": false,
@@ -10877,7 +10815,6 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportScale": 1,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
@@ -10893,7 +10830,6 @@ Object {
"offsetLeft": 0,
"offsetTop": 0,
"openMenu": null,
"openPopup": null,
"pasteDialog": Object {
"data": null,
"shown": false,
@@ -11044,7 +10980,6 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportScale": 1,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
@@ -11060,7 +10995,6 @@ Object {
"offsetLeft": 0,
"offsetTop": 0,
"openMenu": null,
"openPopup": null,
"pasteDialog": Object {
"data": null,
"shown": false,
@@ -11211,7 +11145,6 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportScale": 1,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
@@ -11227,7 +11160,6 @@ Object {
"offsetLeft": 0,
"offsetTop": 0,
"openMenu": null,
"openPopup": null,
"pasteDialog": Object {
"data": null,
"shown": false,
@@ -11408,7 +11340,6 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportScale": 1,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
@@ -11424,7 +11355,6 @@ Object {
"offsetLeft": 0,
"offsetTop": 0,
"openMenu": null,
"openPopup": null,
"pasteDialog": Object {
"data": null,
"shown": false,
@@ -11575,7 +11505,6 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportScale": 1,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
@@ -11591,7 +11520,6 @@ Object {
"offsetLeft": 0,
"offsetTop": 0,
"openMenu": null,
"openPopup": null,
"pasteDialog": Object {
"data": null,
"shown": false,
@@ -11784,7 +11712,6 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportScale": 1,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
@@ -11800,7 +11727,6 @@ Object {
"offsetLeft": 0,
"offsetTop": 0,
"openMenu": null,
"openPopup": null,
"pasteDialog": Object {
"data": null,
"shown": false,
@@ -12511,7 +12437,6 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportScale": 1,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
@@ -12527,7 +12452,6 @@ Object {
"offsetLeft": 0,
"offsetTop": 0,
"openMenu": null,
"openPopup": null,
"pasteDialog": Object {
"data": null,
"shown": false,
@@ -12768,7 +12692,6 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportScale": 1,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
@@ -12784,7 +12707,6 @@ Object {
"offsetLeft": 0,
"offsetTop": 0,
"openMenu": null,
"openPopup": null,
"pasteDialog": Object {
"data": null,
"shown": false,
@@ -12872,7 +12794,6 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportScale": 1,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
@@ -12888,7 +12809,6 @@ Object {
"offsetLeft": 0,
"offsetTop": 0,
"openMenu": null,
"openPopup": null,
"pasteDialog": Object {
"data": null,
"shown": false,
@@ -12974,7 +12894,6 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportScale": 1,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
@@ -12990,7 +12909,6 @@ Object {
"offsetLeft": 0,
"offsetTop": 0,
"openMenu": null,
"openPopup": null,
"pasteDialog": Object {
"data": null,
"shown": false,
@@ -13144,7 +13062,6 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportScale": 1,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
@@ -13160,7 +13077,6 @@ Object {
"offsetLeft": 0,
"offsetTop": 0,
"openMenu": null,
"openPopup": null,
"pasteDialog": Object {
"data": null,
"shown": false,
@@ -13470,7 +13386,6 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportScale": 1,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
@@ -13486,7 +13401,6 @@ Object {
"offsetLeft": 0,
"offsetTop": 0,
"openMenu": null,
"openPopup": null,
"pasteDialog": Object {
"data": null,
"shown": false,
@@ -13674,7 +13588,6 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportScale": 1,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
@@ -13690,7 +13603,6 @@ Object {
"offsetLeft": 0,
"offsetTop": 0,
"openMenu": null,
"openPopup": null,
"pasteDialog": Object {
"data": null,
"shown": false,
@@ -14510,7 +14422,6 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportScale": 1,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
@@ -14526,7 +14437,6 @@ Object {
"offsetLeft": 0,
"offsetTop": 0,
"openMenu": null,
"openPopup": null,
"pasteDialog": Object {
"data": null,
"shown": false,
@@ -14612,7 +14522,6 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportScale": 1,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
@@ -14628,7 +14537,6 @@ Object {
"offsetLeft": 0,
"offsetTop": 0,
"openMenu": null,
"openPopup": null,
"pasteDialog": Object {
"data": null,
"shown": false,
@@ -15381,7 +15289,6 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportScale": 1,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
@@ -15397,7 +15304,6 @@ Object {
"offsetLeft": 0,
"offsetTop": 0,
"openMenu": null,
"openPopup": null,
"pasteDialog": Object {
"data": null,
"shown": false,
@@ -15791,7 +15697,6 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportScale": 1,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
@@ -15807,7 +15712,6 @@ Object {
"offsetLeft": 0,
"offsetTop": 0,
"openMenu": null,
"openPopup": null,
"pasteDialog": Object {
"data": null,
"shown": false,
@@ -16068,7 +15972,6 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportScale": 1,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
@@ -16084,7 +15987,6 @@ Object {
"offsetLeft": 0,
"offsetTop": 0,
"openMenu": null,
"openPopup": null,
"pasteDialog": Object {
"data": null,
"shown": false,
@@ -16172,7 +16074,6 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportScale": 1,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
@@ -16188,7 +16089,6 @@ Object {
"offsetLeft": 0,
"offsetTop": 0,
"openMenu": null,
"openPopup": null,
"pasteDialog": Object {
"data": null,
"shown": false,
@@ -16676,7 +16576,6 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportScale": 1,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
@@ -16692,7 +16591,6 @@ Object {
"offsetLeft": 0,
"offsetTop": 0,
"openMenu": null,
"openPopup": null,
"pasteDialog": Object {
"data": null,
"shown": false,
@@ -16778,7 +16676,6 @@ Object {
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportScale": 1,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
@@ -16794,7 +16691,6 @@ Object {
"offsetLeft": 0,
"offsetTop": 0,
"openMenu": null,
"openPopup": null,
"pasteDialog": Object {
"data": null,
"shown": false,

Some files were not shown because too many files have changed in this diff Show More