Merge remote-tracking branch 'origin/master' into feat-custom-actions
This commit is contained in:
commit
e6fb7e3016
@ -167,9 +167,6 @@
|
||||
body,
|
||||
html {
|
||||
margin: 0;
|
||||
--ui-font: Assistant, system-ui, BlinkMacSystemFont, -apple-system,
|
||||
Segoe UI, Roboto, Helvetica, Arial, sans-serif;
|
||||
font-family: var(--ui-font);
|
||||
-webkit-text-size-adjust: 100%;
|
||||
|
||||
width: 100%;
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { ColorPicker } from "../components/ColorPicker";
|
||||
import { eraser, ZoomInIcon, ZoomOutIcon } from "../components/icons";
|
||||
import { ZoomInIcon, ZoomOutIcon } from "../components/icons";
|
||||
import { ToolButton } from "../components/ToolButton";
|
||||
import { MIN_ZOOM, THEME, ZOOM_STEP } from "../constants";
|
||||
import { CURSOR_TYPE, MIN_ZOOM, THEME, ZOOM_STEP } from "../constants";
|
||||
import { getCommonBounds, getNonDeletedElements } from "../element";
|
||||
import { ExcalidrawElement } from "../element/types";
|
||||
import { t } from "../i18n";
|
||||
@ -10,12 +10,15 @@ import { getNormalizedZoom, getSelectedElements } from "../scene";
|
||||
import { centerScrollOn } from "../scene/scroll";
|
||||
import { getStateForZoom } from "../scene/zoom";
|
||||
import { AppState, NormalizedZoomValue } from "../types";
|
||||
import { getShortcutKey, updateActiveTool } from "../utils";
|
||||
import { getShortcutKey, setCursor, updateActiveTool } from "../utils";
|
||||
import { register } from "./register";
|
||||
import { Tooltip } from "../components/Tooltip";
|
||||
import { newElementWith } from "../element/mutateElement";
|
||||
import { getDefaultAppState, isEraserActive } from "../appState";
|
||||
import clsx from "clsx";
|
||||
import {
|
||||
getDefaultAppState,
|
||||
isEraserActive,
|
||||
isHandToolActive,
|
||||
} from "../appState";
|
||||
|
||||
export const actionChangeViewBackgroundColor = register({
|
||||
name: "changeViewBackgroundColor",
|
||||
@ -306,15 +309,15 @@ export const actionToggleTheme = register({
|
||||
},
|
||||
});
|
||||
|
||||
export const actionErase = register({
|
||||
name: "eraser",
|
||||
export const actionToggleEraserTool = register({
|
||||
name: "toggleEraserTool",
|
||||
trackEvent: { category: "toolbar" },
|
||||
perform: (elements, appState) => {
|
||||
let activeTool: AppState["activeTool"];
|
||||
|
||||
if (isEraserActive(appState)) {
|
||||
activeTool = updateActiveTool(appState, {
|
||||
...(appState.activeTool.lastActiveToolBeforeEraser || {
|
||||
...(appState.activeTool.lastActiveTool || {
|
||||
type: "selection",
|
||||
}),
|
||||
lastActiveToolBeforeEraser: null,
|
||||
@ -337,17 +340,38 @@ export const actionErase = register({
|
||||
};
|
||||
},
|
||||
keyTest: (event) => event.key === KEYS.E,
|
||||
PanelComponent: ({ elements, appState, updateData, data }) => (
|
||||
<ToolButton
|
||||
type="button"
|
||||
icon={eraser}
|
||||
className={clsx("eraser", { active: isEraserActive(appState) })}
|
||||
title={`${t("toolBar.eraser")}-${getShortcutKey("E")}`}
|
||||
aria-label={t("toolBar.eraser")}
|
||||
onClick={() => {
|
||||
updateData(null);
|
||||
}}
|
||||
size={data?.size || "medium"}
|
||||
></ToolButton>
|
||||
),
|
||||
});
|
||||
|
||||
export const actionToggleHandTool = register({
|
||||
name: "toggleHandTool",
|
||||
trackEvent: { category: "toolbar" },
|
||||
perform: (elements, appState, _, app) => {
|
||||
let activeTool: AppState["activeTool"];
|
||||
|
||||
if (isHandToolActive(appState)) {
|
||||
activeTool = updateActiveTool(appState, {
|
||||
...(appState.activeTool.lastActiveTool || {
|
||||
type: "selection",
|
||||
}),
|
||||
lastActiveToolBeforeEraser: null,
|
||||
});
|
||||
} else {
|
||||
activeTool = updateActiveTool(appState, {
|
||||
type: "hand",
|
||||
lastActiveToolBeforeEraser: appState.activeTool,
|
||||
});
|
||||
setCursor(app.canvas, CURSOR_TYPE.GRAB);
|
||||
}
|
||||
|
||||
return {
|
||||
appState: {
|
||||
...appState,
|
||||
selectedElementIds: {},
|
||||
selectedGroupIds: {},
|
||||
activeTool,
|
||||
},
|
||||
commitToHistory: true,
|
||||
};
|
||||
},
|
||||
keyTest: (event) => event.key === KEYS.H,
|
||||
});
|
||||
|
@ -145,7 +145,7 @@ export const actionFinalize = register({
|
||||
let activeTool: AppState["activeTool"];
|
||||
if (appState.activeTool.type === "eraser") {
|
||||
activeTool = updateActiveTool(appState, {
|
||||
...(appState.activeTool.lastActiveToolBeforeEraser || {
|
||||
...(appState.activeTool.lastActiveTool || {
|
||||
type: "selection",
|
||||
}),
|
||||
lastActiveToolBeforeEraser: null,
|
||||
|
@ -5,10 +5,11 @@ import { t } from "../i18n";
|
||||
import History, { HistoryEntry } from "../history";
|
||||
import { ExcalidrawElement } from "../element/types";
|
||||
import { AppState } from "../types";
|
||||
import { isWindows, KEYS } from "../keys";
|
||||
import { KEYS } from "../keys";
|
||||
import { newElementWith } from "../element/mutateElement";
|
||||
import { fixBindingsAfterDeletion } from "../element/binding";
|
||||
import { arrayToMap } from "../utils";
|
||||
import { isWindows } from "../constants";
|
||||
|
||||
const writeData = (
|
||||
prevElements: readonly ExcalidrawElement[],
|
||||
|
@ -5,7 +5,7 @@ import {
|
||||
moveAllLeft,
|
||||
moveAllRight,
|
||||
} from "../zindex";
|
||||
import { KEYS, isDarwin, CODES } from "../keys";
|
||||
import { KEYS, CODES } from "../keys";
|
||||
import { t } from "../i18n";
|
||||
import { getShortcutKey } from "../utils";
|
||||
import { register } from "./register";
|
||||
@ -15,6 +15,7 @@ import {
|
||||
SendBackwardIcon,
|
||||
SendToBackIcon,
|
||||
} from "../components/icons";
|
||||
import { isDarwin } from "../constants";
|
||||
|
||||
export const actionSendBackward = register({
|
||||
name: "sendBackward",
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { isDarwin } from "../constants";
|
||||
import { t } from "../i18n";
|
||||
import { isDarwin } from "../keys";
|
||||
import { getShortcutKey } from "../utils";
|
||||
import { ActionName } from "./types";
|
||||
|
||||
|
@ -125,10 +125,11 @@ const actionNames = [
|
||||
"decreaseFontSize",
|
||||
"unbindText",
|
||||
"hyperlink",
|
||||
"eraser",
|
||||
"bindText",
|
||||
"toggleLock",
|
||||
"toggleLinearEditor",
|
||||
"toggleEraserTool",
|
||||
"toggleHandTool",
|
||||
] as const;
|
||||
|
||||
// So we can have the `isActionName` type guard
|
||||
|
@ -45,7 +45,7 @@ export const getDefaultAppState = (): Omit<
|
||||
type: "selection",
|
||||
customType: null,
|
||||
locked: false,
|
||||
lastActiveToolBeforeEraser: null,
|
||||
lastActiveTool: null,
|
||||
},
|
||||
penMode: false,
|
||||
penDetected: false,
|
||||
@ -228,3 +228,11 @@ export const isEraserActive = ({
|
||||
}: {
|
||||
activeTool: AppState["activeTool"];
|
||||
}) => activeTool.type === "eraser";
|
||||
|
||||
export const isHandToolActive = ({
|
||||
activeTool,
|
||||
}: {
|
||||
activeTool: AppState["activeTool"];
|
||||
}) => {
|
||||
return activeTool.type === "hand";
|
||||
};
|
||||
|
@ -180,16 +180,16 @@ export const parseClipboard = async (
|
||||
};
|
||||
|
||||
export const copyBlobToClipboardAsPng = async (blob: Blob | Promise<Blob>) => {
|
||||
let promise;
|
||||
try {
|
||||
// in Safari so far we need to construct the ClipboardItem synchronously
|
||||
// (i.e. in the same tick) otherwise browser will complain for lack of
|
||||
// user intent. Using a Promise ClipboardItem constructor solves this.
|
||||
// https://bugs.webkit.org/show_bug.cgi?id=222262
|
||||
//
|
||||
// not await so that we can detect whether the thrown error likely relates
|
||||
// to a lack of support for the Promise ClipboardItem constructor
|
||||
promise = navigator.clipboard.write([
|
||||
// Note that Firefox (and potentially others) seems to support Promise
|
||||
// ClipboardItem constructor, but throws on an unrelated MIME type error.
|
||||
// So we need to await this and fallback to awaiting the blob if applicable.
|
||||
await navigator.clipboard.write([
|
||||
new window.ClipboardItem({
|
||||
[MIME_TYPES.png]: blob,
|
||||
}),
|
||||
@ -207,7 +207,6 @@ export const copyBlobToClipboardAsPng = async (blob: Blob | Promise<Blob>) => {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
await promise;
|
||||
};
|
||||
|
||||
export const copyTextToSystemClipboard = async (text: string | null) => {
|
||||
|
@ -224,9 +224,10 @@ export const ShapesSwitcher = ({
|
||||
<>
|
||||
{SHAPES.map(({ value, icon, key, numericKey, fillable }, index) => {
|
||||
const label = t(`toolBar.${value}`);
|
||||
const letter = key && (typeof key === "string" ? key : key[0]);
|
||||
const letter =
|
||||
key && capitalizeString(typeof key === "string" ? key : key[0]);
|
||||
const shortcut = letter
|
||||
? `${capitalizeString(letter)} ${t("helpDialog.or")} ${numericKey}`
|
||||
? `${letter} ${t("helpDialog.or")} ${numericKey}`
|
||||
: `${numericKey}`;
|
||||
return (
|
||||
<ToolButton
|
||||
@ -237,7 +238,7 @@ export const ShapesSwitcher = ({
|
||||
checked={activeTool.type === value}
|
||||
name="editor-current-shape"
|
||||
title={`${capitalizeString(label)} — ${shortcut}`}
|
||||
keyBindingLabel={numericKey}
|
||||
keyBindingLabel={numericKey || letter}
|
||||
aria-label={capitalizeString(label)}
|
||||
aria-keyshortcuts={shortcut}
|
||||
data-testid={`toolbar-${value}`}
|
||||
|
35
src/components/ActiveConfirmDialog.tsx
Normal file
35
src/components/ActiveConfirmDialog.tsx
Normal file
@ -0,0 +1,35 @@
|
||||
import { atom, useAtom } from "jotai";
|
||||
import { actionClearCanvas } from "../actions";
|
||||
import { t } from "../i18n";
|
||||
import { useExcalidrawActionManager } from "./App";
|
||||
import ConfirmDialog from "./ConfirmDialog";
|
||||
|
||||
export const activeConfirmDialogAtom = atom<"clearCanvas" | null>(null);
|
||||
|
||||
export const ActiveConfirmDialog = () => {
|
||||
const [activeConfirmDialog, setActiveConfirmDialog] = useAtom(
|
||||
activeConfirmDialogAtom,
|
||||
);
|
||||
const actionManager = useExcalidrawActionManager();
|
||||
|
||||
if (!activeConfirmDialog) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (activeConfirmDialog === "clearCanvas") {
|
||||
return (
|
||||
<ConfirmDialog
|
||||
onConfirm={() => {
|
||||
actionManager.executeAction(actionClearCanvas);
|
||||
setActiveConfirmDialog(null);
|
||||
}}
|
||||
onCancel={() => setActiveConfirmDialog(null)}
|
||||
title={t("clearCanvasDialog.title")}
|
||||
>
|
||||
<p className="clear-canvas__content"> {t("alerts.clearReset")}</p>
|
||||
</ConfirmDialog>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
@ -41,7 +41,11 @@ import { ActionManager } from "../actions/manager";
|
||||
import { getActions } from "../actions/register";
|
||||
import { ActionResult } from "../actions/types";
|
||||
import { trackEvent } from "../analytics";
|
||||
import { getDefaultAppState, isEraserActive } from "../appState";
|
||||
import {
|
||||
getDefaultAppState,
|
||||
isEraserActive,
|
||||
isHandToolActive,
|
||||
} from "../appState";
|
||||
import { parseClipboard } from "../clipboard";
|
||||
import {
|
||||
APP_NAME,
|
||||
@ -57,6 +61,7 @@ import {
|
||||
EVENT,
|
||||
GRID_SIZE,
|
||||
IMAGE_RENDER_TIMEOUT,
|
||||
isAndroid,
|
||||
LINE_CONFIRM_THRESHOLD,
|
||||
MAX_ALLOWED_FILE_BYTES,
|
||||
MIME_TYPES,
|
||||
@ -166,7 +171,6 @@ import {
|
||||
shouldRotateWithDiscreteAngle,
|
||||
isArrowKey,
|
||||
KEYS,
|
||||
isAndroid,
|
||||
} from "../keys";
|
||||
import { distance2d, getGridPoint, isPathALoop } from "../math";
|
||||
import { renderScene } from "../renderer/renderScene";
|
||||
@ -274,6 +278,7 @@ import {
|
||||
import { shouldShowBoundingBox } from "../element/transformHandles";
|
||||
import { Fonts } from "../scene/Fonts";
|
||||
import { actionPaste } from "../actions/actionClipboard";
|
||||
import { actionToggleHandTool } from "../actions/actionCanvas";
|
||||
|
||||
const deviceContextInitialValue = {
|
||||
isSmScreen: false,
|
||||
@ -577,6 +582,7 @@ class App extends React.Component<AppProps, AppState> {
|
||||
elements={this.scene.getNonDeletedElements()}
|
||||
onLockToggle={this.toggleLock}
|
||||
onPenModeToggle={this.togglePenMode}
|
||||
onHandToolToggle={this.onHandToolToggle}
|
||||
onInsertElements={(elements) =>
|
||||
this.addElementsFromPasteOrLibrary({
|
||||
elements,
|
||||
@ -1815,6 +1821,10 @@ class App extends React.Component<AppProps, AppState> {
|
||||
});
|
||||
};
|
||||
|
||||
onHandToolToggle = () => {
|
||||
this.actionManager.executeAction(actionToggleHandTool);
|
||||
};
|
||||
|
||||
scrollToContent = (
|
||||
target:
|
||||
| ExcalidrawElement
|
||||
@ -2232,11 +2242,13 @@ class App extends React.Component<AppProps, AppState> {
|
||||
|
||||
private setActiveTool = (
|
||||
tool:
|
||||
| { type: typeof SHAPES[number]["value"] | "eraser" }
|
||||
| { type: typeof SHAPES[number]["value"] | "eraser" | "hand" }
|
||||
| { type: "custom"; customType: string },
|
||||
) => {
|
||||
const nextActiveTool = updateActiveTool(this.state, tool);
|
||||
if (!isHoldingSpace) {
|
||||
if (nextActiveTool.type === "hand") {
|
||||
setCursor(this.canvas, CURSOR_TYPE.GRAB);
|
||||
} else if (!isHoldingSpace) {
|
||||
setCursorForShape(this.canvas, this.state);
|
||||
}
|
||||
if (isToolIcon(document.activeElement)) {
|
||||
@ -2907,7 +2919,12 @@ class App extends React.Component<AppProps, AppState> {
|
||||
null;
|
||||
}
|
||||
|
||||
if (isHoldingSpace || isPanning || isDraggingScrollBar) {
|
||||
if (
|
||||
isHoldingSpace ||
|
||||
isPanning ||
|
||||
isDraggingScrollBar ||
|
||||
isHandToolActive(this.state)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -3499,7 +3516,10 @@ class App extends React.Component<AppProps, AppState> {
|
||||
);
|
||||
} else if (this.state.activeTool.type === "custom") {
|
||||
setCursor(this.canvas, CURSOR_TYPE.AUTO);
|
||||
} else if (this.state.activeTool.type !== "eraser") {
|
||||
} else if (
|
||||
this.state.activeTool.type !== "eraser" &&
|
||||
this.state.activeTool.type !== "hand"
|
||||
) {
|
||||
this.createGenericElementOnPointerDown(
|
||||
this.state.activeTool.type,
|
||||
pointerDownState,
|
||||
@ -3610,6 +3630,7 @@ class App extends React.Component<AppProps, AppState> {
|
||||
gesture.pointers.size <= 1 &&
|
||||
(event.button === POINTER_BUTTON.WHEEL ||
|
||||
(event.button === POINTER_BUTTON.MAIN && isHoldingSpace) ||
|
||||
isHandToolActive(this.state) ||
|
||||
this.state.viewModeEnabled)
|
||||
) ||
|
||||
isTextElement(this.state.editingElement)
|
||||
|
@ -96,6 +96,10 @@
|
||||
width: 5rem;
|
||||
height: 5rem;
|
||||
margin: 0 0.2em;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
border-radius: 1rem;
|
||||
background-color: var(--button-color);
|
||||
|
32
src/components/HandButton.tsx
Normal file
32
src/components/HandButton.tsx
Normal file
@ -0,0 +1,32 @@
|
||||
import "./ToolIcon.scss";
|
||||
|
||||
import clsx from "clsx";
|
||||
import { ToolButton } from "./ToolButton";
|
||||
import { handIcon } from "./icons";
|
||||
import { KEYS } from "../keys";
|
||||
|
||||
type LockIconProps = {
|
||||
title?: string;
|
||||
name?: string;
|
||||
checked: boolean;
|
||||
onChange?(): void;
|
||||
isMobile?: boolean;
|
||||
};
|
||||
|
||||
export const HandButton = (props: LockIconProps) => {
|
||||
return (
|
||||
<ToolButton
|
||||
className={clsx("Shape", { fillable: false })}
|
||||
type="radio"
|
||||
icon={handIcon}
|
||||
name="editor-current-shape"
|
||||
checked={props.checked}
|
||||
title={`${props.title} — H`}
|
||||
keyBindingLabel={!props.isMobile ? KEYS.H.toLocaleUpperCase() : undefined}
|
||||
aria-label={`${props.title} — H`}
|
||||
aria-keyshortcuts={KEYS.H}
|
||||
data-testid={`toolbar-hand`}
|
||||
onChange={() => props.onChange?.()}
|
||||
/>
|
||||
);
|
||||
};
|
@ -1,10 +1,12 @@
|
||||
import React from "react";
|
||||
import { t } from "../i18n";
|
||||
import { isDarwin, isWindows, KEYS } from "../keys";
|
||||
import { KEYS } from "../keys";
|
||||
import { Dialog } from "./Dialog";
|
||||
import { getShortcutKey } from "../utils";
|
||||
import "./HelpDialog.scss";
|
||||
import { ExternalLinkIcon } from "./icons";
|
||||
import { probablySupportsClipboardBlob } from "../clipboard";
|
||||
import { isDarwin, isFirefox, isWindows } from "../constants";
|
||||
|
||||
const Header = () => (
|
||||
<div className="HelpDialog__header">
|
||||
@ -67,6 +69,10 @@ function* intersperse(as: JSX.Element[][], delim: string | null) {
|
||||
}
|
||||
}
|
||||
|
||||
const upperCaseSingleChars = (str: string) => {
|
||||
return str.replace(/\b[a-z]\b/, (c) => c.toUpperCase());
|
||||
};
|
||||
|
||||
const Shortcut = ({
|
||||
label,
|
||||
shortcuts,
|
||||
@ -81,7 +87,9 @@ const Shortcut = ({
|
||||
? [...shortcut.slice(0, -2).split("+"), "+"]
|
||||
: shortcut.split("+");
|
||||
|
||||
return keys.map((key) => <ShortcutKey key={key}>{key}</ShortcutKey>);
|
||||
return keys.map((key) => (
|
||||
<ShortcutKey key={key}>{upperCaseSingleChars(key)}</ShortcutKey>
|
||||
));
|
||||
});
|
||||
|
||||
return (
|
||||
@ -118,6 +126,7 @@ export const HelpDialog = ({ onClose }: { onClose?: () => void }) => {
|
||||
className="HelpDialog__island--tools"
|
||||
caption={t("helpDialog.tools")}
|
||||
>
|
||||
<Shortcut label={t("toolBar.hand")} shortcuts={[KEYS.H]} />
|
||||
<Shortcut
|
||||
label={t("toolBar.selection")}
|
||||
shortcuts={[KEYS.V, KEYS["1"]]}
|
||||
@ -304,10 +313,14 @@ export const HelpDialog = ({ onClose }: { onClose?: () => void }) => {
|
||||
label={t("labels.pasteAsPlaintext")}
|
||||
shortcuts={[getShortcutKey("CtrlOrCmd+Shift+V")]}
|
||||
/>
|
||||
{/* firefox supports clipboard API under a flag, so we'll
|
||||
show users what they can do in the error message */}
|
||||
{(probablySupportsClipboardBlob || isFirefox) && (
|
||||
<Shortcut
|
||||
label={t("labels.copyAsPng")}
|
||||
shortcuts={[getShortcutKey("Shift+Alt+C")]}
|
||||
/>
|
||||
)}
|
||||
<Shortcut
|
||||
label={t("labels.copyStyles")}
|
||||
shortcuts={[getShortcutKey("CtrlOrCmd+Alt+C")]}
|
||||
|
@ -12,7 +12,7 @@ import Stack from "./Stack";
|
||||
import "./ExportDialog.scss";
|
||||
import OpenColor from "open-color";
|
||||
import { CheckboxItem } from "./CheckboxItem";
|
||||
import { DEFAULT_EXPORT_PADDING } from "../constants";
|
||||
import { DEFAULT_EXPORT_PADDING, isFirefox } from "../constants";
|
||||
import { nativeFileSystemSupported } from "../data/filesystem";
|
||||
import { ActionManager } from "../actions/manager";
|
||||
|
||||
@ -190,7 +190,9 @@ const ImageExportModal = ({
|
||||
>
|
||||
SVG
|
||||
</ExportButton>
|
||||
{probablySupportsClipboardBlob && (
|
||||
{/* firefox supports clipboard API under a flag,
|
||||
so let's throw and tell people what they can do */}
|
||||
{(probablySupportsClipboardBlob || isFirefox) && (
|
||||
<ExportButton
|
||||
title={t("buttons.copyPngToClipboard")}
|
||||
onClick={() => onExportToClipboard(exportedElements)}
|
||||
|
@ -50,6 +50,9 @@ import { hostSidebarCountersAtom } from "./Sidebar/Sidebar";
|
||||
import { jotaiScope } from "../jotai";
|
||||
import { useAtom } from "jotai";
|
||||
import MainMenu from "./main-menu/MainMenu";
|
||||
import { ActiveConfirmDialog } from "./ActiveConfirmDialog";
|
||||
import { HandButton } from "./HandButton";
|
||||
import { isHandToolActive } from "../appState";
|
||||
|
||||
interface LayerUIProps {
|
||||
actionManager: ActionManager;
|
||||
@ -59,6 +62,7 @@ interface LayerUIProps {
|
||||
setAppState: React.Component<any, AppState>["setState"];
|
||||
elements: readonly NonDeletedExcalidrawElement[];
|
||||
onLockToggle: () => void;
|
||||
onHandToolToggle: () => void;
|
||||
onPenModeToggle: () => void;
|
||||
onInsertElements: (elements: readonly NonDeletedExcalidrawElement[]) => void;
|
||||
showExitZenModeBtn: boolean;
|
||||
@ -86,6 +90,7 @@ const LayerUI = ({
|
||||
elements,
|
||||
canvas,
|
||||
onLockToggle,
|
||||
onHandToolToggle,
|
||||
onPenModeToggle,
|
||||
onInsertElements,
|
||||
showExitZenModeBtn,
|
||||
@ -306,13 +311,20 @@ const LayerUI = ({
|
||||
penDetected={appState.penDetected}
|
||||
/>
|
||||
<LockButton
|
||||
zenModeEnabled={appState.zenModeEnabled}
|
||||
checked={appState.activeTool.locked}
|
||||
onChange={() => onLockToggle()}
|
||||
onChange={onLockToggle}
|
||||
title={t("toolBar.lock")}
|
||||
/>
|
||||
|
||||
<div className="App-toolbar__divider"></div>
|
||||
|
||||
<HandButton
|
||||
checked={isHandToolActive(appState)}
|
||||
onChange={() => onHandToolToggle()}
|
||||
title={t("toolBar.hand")}
|
||||
isMobile
|
||||
/>
|
||||
|
||||
<ShapesSwitcher
|
||||
appState={appState}
|
||||
canvas={canvas}
|
||||
@ -325,9 +337,6 @@ const LayerUI = ({
|
||||
}}
|
||||
onContextMenu={onContextMenu}
|
||||
/>
|
||||
{/* {actionManager.renderAction("eraser", {
|
||||
// size: "small",
|
||||
})} */}
|
||||
</Stack.Row>
|
||||
</Island>
|
||||
</Stack.Row>
|
||||
@ -389,6 +398,7 @@ const LayerUI = ({
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<ActiveConfirmDialog />
|
||||
{renderImageExportDialog()}
|
||||
{renderJSONExportDialog()}
|
||||
{appState.pasteDialog.shown && (
|
||||
@ -411,7 +421,8 @@ const LayerUI = ({
|
||||
renderJSONExportDialog={renderJSONExportDialog}
|
||||
renderImageExportDialog={renderImageExportDialog}
|
||||
setAppState={setAppState}
|
||||
onLockToggle={() => onLockToggle()}
|
||||
onLockToggle={onLockToggle}
|
||||
onHandToolToggle={onHandToolToggle}
|
||||
onPenModeToggle={onPenModeToggle}
|
||||
canvas={canvas}
|
||||
onImageAction={onImageAction}
|
||||
|
@ -187,6 +187,7 @@ export const LibraryMenuHeader: React.FC<{
|
||||
</DropdownMenu.Trigger>
|
||||
<DropdownMenu.Content
|
||||
onClickOutside={() => setIsLibraryMenuOpen(false)}
|
||||
onSelect={() => setIsLibraryMenuOpen(false)}
|
||||
className="library-menu"
|
||||
>
|
||||
{!itemsSelected && (
|
||||
|
@ -9,7 +9,6 @@ type LockIconProps = {
|
||||
name?: string;
|
||||
checked: boolean;
|
||||
onChange?(): void;
|
||||
zenModeEnabled?: boolean;
|
||||
isMobile?: boolean;
|
||||
};
|
||||
|
||||
|
@ -22,6 +22,8 @@ import { LibraryButton } from "./LibraryButton";
|
||||
import { PenModeButton } from "./PenModeButton";
|
||||
import { Stats } from "./Stats";
|
||||
import { actionToggleStats } from "../actions";
|
||||
import { HandButton } from "./HandButton";
|
||||
import { isHandToolActive } from "../appState";
|
||||
|
||||
type MobileMenuProps = {
|
||||
appState: AppState;
|
||||
@ -31,6 +33,7 @@ type MobileMenuProps = {
|
||||
setAppState: React.Component<any, AppState>["setState"];
|
||||
elements: readonly NonDeletedExcalidrawElement[];
|
||||
onLockToggle: () => void;
|
||||
onHandToolToggle: () => void;
|
||||
onPenModeToggle: () => void;
|
||||
canvas: HTMLCanvasElement | null;
|
||||
|
||||
@ -53,6 +56,7 @@ export const MobileMenu = ({
|
||||
actionManager,
|
||||
setAppState,
|
||||
onLockToggle,
|
||||
onHandToolToggle,
|
||||
onPenModeToggle,
|
||||
canvas,
|
||||
onImageAction,
|
||||
@ -91,6 +95,13 @@ export const MobileMenu = ({
|
||||
</Island>
|
||||
{renderTopRightUI && renderTopRightUI(true, appState)}
|
||||
<div className="mobile-misc-tools-container">
|
||||
{!appState.viewModeEnabled && (
|
||||
<LibraryButton
|
||||
appState={appState}
|
||||
setAppState={setAppState}
|
||||
isMobile
|
||||
/>
|
||||
)}
|
||||
<PenModeButton
|
||||
checked={appState.penMode}
|
||||
onChange={onPenModeToggle}
|
||||
@ -104,13 +115,12 @@ export const MobileMenu = ({
|
||||
title={t("toolBar.lock")}
|
||||
isMobile
|
||||
/>
|
||||
{!appState.viewModeEnabled && (
|
||||
<LibraryButton
|
||||
appState={appState}
|
||||
setAppState={setAppState}
|
||||
<HandButton
|
||||
checked={isHandToolActive(appState)}
|
||||
onChange={() => onHandToolToggle()}
|
||||
title={t("toolBar.hand")}
|
||||
isMobile
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</Stack.Row>
|
||||
</Stack.Col>
|
||||
|
@ -19,7 +19,7 @@ type ToolButtonBaseProps = {
|
||||
name?: string;
|
||||
id?: string;
|
||||
size?: ToolButtonSize;
|
||||
keyBindingLabel?: string;
|
||||
keyBindingLabel?: string | null;
|
||||
showAriaLabel?: boolean;
|
||||
hidden?: boolean;
|
||||
visible?: boolean;
|
||||
|
@ -4,16 +4,23 @@ import { Island } from "../Island";
|
||||
import { useDevice } from "../App";
|
||||
import clsx from "clsx";
|
||||
import Stack from "../Stack";
|
||||
import React from "react";
|
||||
import { DropdownMenuContentPropsContext } from "./common";
|
||||
|
||||
const MenuContent = ({
|
||||
children,
|
||||
onClickOutside,
|
||||
className = "",
|
||||
onSelect,
|
||||
style,
|
||||
}: {
|
||||
children?: React.ReactNode;
|
||||
onClickOutside?: () => void;
|
||||
className?: string;
|
||||
/**
|
||||
* Called when any menu item is selected (clicked on).
|
||||
*/
|
||||
onSelect?: (event: Event) => void;
|
||||
style?: React.CSSProperties;
|
||||
}) => {
|
||||
const device = useDevice();
|
||||
@ -24,7 +31,9 @@ const MenuContent = ({
|
||||
const classNames = clsx(`dropdown-menu ${className}`, {
|
||||
"dropdown-menu--mobile": device.isMobile,
|
||||
}).trim();
|
||||
|
||||
return (
|
||||
<DropdownMenuContentPropsContext.Provider value={{ onSelect }}>
|
||||
<div
|
||||
ref={menuRef}
|
||||
className={classNames}
|
||||
@ -45,7 +54,9 @@ const MenuContent = ({
|
||||
</Island>
|
||||
)}
|
||||
</div>
|
||||
</DropdownMenuContentPropsContext.Provider>
|
||||
);
|
||||
};
|
||||
export default MenuContent;
|
||||
MenuContent.displayName = "DropdownMenuContent";
|
||||
|
||||
export default MenuContent;
|
||||
|
@ -1,10 +1,10 @@
|
||||
import React from "react";
|
||||
import {
|
||||
getDrodownMenuItemClassName,
|
||||
useHandleDropdownMenuItemClick,
|
||||
} from "./common";
|
||||
import MenuItemContent from "./DropdownMenuItemContent";
|
||||
|
||||
export const getDrodownMenuItemClassName = (className = "") => {
|
||||
return `dropdown-menu-item dropdown-menu-item-base ${className}`.trim();
|
||||
};
|
||||
|
||||
const DropdownMenuItem = ({
|
||||
icon,
|
||||
onSelect,
|
||||
@ -14,15 +14,17 @@ const DropdownMenuItem = ({
|
||||
...rest
|
||||
}: {
|
||||
icon?: JSX.Element;
|
||||
onSelect: () => void;
|
||||
onSelect: (event: Event) => void;
|
||||
children: React.ReactNode;
|
||||
shortcut?: string;
|
||||
className?: string;
|
||||
} & React.ButtonHTMLAttributes<HTMLButtonElement>) => {
|
||||
} & Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, "onSelect">) => {
|
||||
const handleClick = useHandleDropdownMenuItemClick(rest.onClick, onSelect);
|
||||
|
||||
return (
|
||||
<button
|
||||
{...rest}
|
||||
onClick={onSelect}
|
||||
onClick={handleClick}
|
||||
type="button"
|
||||
className={getDrodownMenuItemClassName(className)}
|
||||
title={rest.title ?? rest["aria-label"]}
|
||||
|
@ -1,20 +1,28 @@
|
||||
import MenuItemContent from "./DropdownMenuItemContent";
|
||||
import React from "react";
|
||||
import { getDrodownMenuItemClassName } from "./DropdownMenuItem";
|
||||
import {
|
||||
getDrodownMenuItemClassName,
|
||||
useHandleDropdownMenuItemClick,
|
||||
} from "./common";
|
||||
|
||||
const DropdownMenuItemLink = ({
|
||||
icon,
|
||||
shortcut,
|
||||
href,
|
||||
children,
|
||||
onSelect,
|
||||
className = "",
|
||||
...rest
|
||||
}: {
|
||||
href: string;
|
||||
icon?: JSX.Element;
|
||||
children: React.ReactNode;
|
||||
shortcut?: string;
|
||||
className?: string;
|
||||
href: string;
|
||||
onSelect?: (event: Event) => void;
|
||||
} & React.AnchorHTMLAttributes<HTMLAnchorElement>) => {
|
||||
const handleClick = useHandleDropdownMenuItemClick(rest.onClick, onSelect);
|
||||
|
||||
return (
|
||||
<a
|
||||
{...rest}
|
||||
@ -23,6 +31,7 @@ const DropdownMenuItemLink = ({
|
||||
rel="noreferrer"
|
||||
className={getDrodownMenuItemClassName(className)}
|
||||
title={rest.title ?? rest["aria-label"]}
|
||||
onClick={handleClick}
|
||||
>
|
||||
<MenuItemContent icon={icon} shortcut={shortcut}>
|
||||
{children}
|
||||
|
31
src/components/dropdownMenu/common.ts
Normal file
31
src/components/dropdownMenu/common.ts
Normal file
@ -0,0 +1,31 @@
|
||||
import React, { useContext } from "react";
|
||||
import { EVENT } from "../../constants";
|
||||
import { composeEventHandlers } from "../../utils";
|
||||
|
||||
export const DropdownMenuContentPropsContext = React.createContext<{
|
||||
onSelect?: (event: Event) => void;
|
||||
}>({});
|
||||
|
||||
export const getDrodownMenuItemClassName = (className = "") => {
|
||||
return `dropdown-menu-item dropdown-menu-item-base ${className}`.trim();
|
||||
};
|
||||
|
||||
export const useHandleDropdownMenuItemClick = (
|
||||
origOnClick:
|
||||
| React.MouseEventHandler<HTMLAnchorElement | HTMLButtonElement>
|
||||
| undefined,
|
||||
onSelect: ((event: Event) => void) | undefined,
|
||||
) => {
|
||||
const DropdownMenuContentProps = useContext(DropdownMenuContentPropsContext);
|
||||
|
||||
return composeEventHandlers(origOnClick, (event) => {
|
||||
const itemSelectEvent = new CustomEvent(EVENT.MENU_ITEM_SELECT, {
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
});
|
||||
onSelect?.(itemSelectEvent);
|
||||
if (!itemSelectEvent.defaultPrevented) {
|
||||
DropdownMenuContentProps.onSelect?.(itemSelectEvent);
|
||||
}
|
||||
});
|
||||
};
|
@ -1532,3 +1532,14 @@ export const publishIcon = createIcon(
|
||||
export const eraser = createIcon(
|
||||
<path d="M480 416C497.7 416 512 430.3 512 448C512 465.7 497.7 480 480 480H150.6C133.7 480 117.4 473.3 105.4 461.3L25.37 381.3C.3786 356.3 .3786 315.7 25.37 290.7L258.7 57.37C283.7 32.38 324.3 32.38 349.3 57.37L486.6 194.7C511.6 219.7 511.6 260.3 486.6 285.3L355.9 416H480zM265.4 416L332.7 348.7L195.3 211.3L70.63 336L150.6 416L265.4 416z" />,
|
||||
);
|
||||
|
||||
export const handIcon = createIcon(
|
||||
<g strokeWidth={1.25}>
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
|
||||
<path d="M8 13v-7.5a1.5 1.5 0 0 1 3 0v6.5"></path>
|
||||
<path d="M11 5.5v-2a1.5 1.5 0 1 1 3 0v8.5"></path>
|
||||
<path d="M14 5.5a1.5 1.5 0 0 1 3 0v6.5"></path>
|
||||
<path d="M17 7.5a1.5 1.5 0 0 1 3 0v8.5a6 6 0 0 1 -6 6h-2h.208a6 6 0 0 1 -5.012 -2.7a69.74 69.74 0 0 1 -.196 -.3c-.312 -.479 -1.407 -2.388 -3.286 -5.728a1.5 1.5 0 0 1 .536 -2.022a1.867 1.867 0 0 1 2.28 .28l1.47 1.47"></path>
|
||||
</g>,
|
||||
tablerIconProps,
|
||||
);
|
||||
|
@ -28,9 +28,9 @@ import {
|
||||
} from "../../actions";
|
||||
|
||||
import "./DefaultItems.scss";
|
||||
import { useState } from "react";
|
||||
import ConfirmDialog from "../ConfirmDialog";
|
||||
import clsx from "clsx";
|
||||
import { useSetAtom } from "jotai";
|
||||
import { activeConfirmDialogAtom } from "../ActiveConfirmDialog";
|
||||
|
||||
export const LoadScene = () => {
|
||||
// FIXME Hack until we tie "t" to lang state
|
||||
@ -122,41 +122,22 @@ export const ClearCanvas = () => {
|
||||
// FIXME Hack until we tie "t" to lang state
|
||||
// eslint-disable-next-line
|
||||
const appState = useExcalidrawAppState();
|
||||
const setActiveConfirmDialog = useSetAtom(activeConfirmDialogAtom);
|
||||
const actionManager = useExcalidrawActionManager();
|
||||
|
||||
const [showDialog, setShowDialog] = useState(false);
|
||||
const toggleDialog = () => setShowDialog(!showDialog);
|
||||
|
||||
if (!actionManager.isActionEnabled(actionClearCanvas)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<DropdownMenuItem
|
||||
icon={TrashIcon}
|
||||
onSelect={toggleDialog}
|
||||
onSelect={() => setActiveConfirmDialog("clearCanvas")}
|
||||
data-testid="clear-canvas-button"
|
||||
aria-label={t("buttons.clearReset")}
|
||||
>
|
||||
{t("buttons.clearReset")}
|
||||
</DropdownMenuItem>
|
||||
|
||||
{/* FIXME this should live outside MainMenu so it stays open
|
||||
if menu is closed */}
|
||||
{showDialog && (
|
||||
<ConfirmDialog
|
||||
onConfirm={() => {
|
||||
actionManager.executeAction(actionClearCanvas);
|
||||
toggleDialog();
|
||||
}}
|
||||
onCancel={toggleDialog}
|
||||
title={t("clearCanvasDialog.title")}
|
||||
>
|
||||
<p className="clear-canvas__content"> {t("alerts.clearReset")}</p>
|
||||
</ConfirmDialog>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
ClearCanvas.displayName = "ClearCanvas";
|
||||
@ -171,7 +152,9 @@ export const ToggleTheme = () => {
|
||||
|
||||
return (
|
||||
<DropdownMenuItem
|
||||
onSelect={() => {
|
||||
onSelect={(event) => {
|
||||
// do not close the menu when changing theme
|
||||
event.preventDefault();
|
||||
return actionManager.executeAction(actionToggleTheme);
|
||||
}}
|
||||
icon={appState.theme === "dark" ? SunIcon : MoonIcon}
|
||||
|
@ -11,14 +11,25 @@ import * as DefaultItems from "./DefaultItems";
|
||||
import { UserList } from "../UserList";
|
||||
import { t } from "../../i18n";
|
||||
import { HamburgerMenuIcon } from "../icons";
|
||||
import { composeEventHandlers } from "../../utils";
|
||||
|
||||
const MainMenu = ({ children }: { children?: React.ReactNode }) => {
|
||||
const MainMenu = ({
|
||||
children,
|
||||
onSelect,
|
||||
}: {
|
||||
children?: React.ReactNode;
|
||||
/**
|
||||
* Called when any menu item is selected (clicked on).
|
||||
*/
|
||||
onSelect?: (event: Event) => void;
|
||||
}) => {
|
||||
const device = useDevice();
|
||||
const appState = useExcalidrawAppState();
|
||||
const setAppState = useExcalidrawSetAppState();
|
||||
const onClickOutside = device.isMobile
|
||||
? undefined
|
||||
: () => setAppState({ openMenu: null });
|
||||
|
||||
return (
|
||||
<DropdownMenu open={appState.openMenu === "canvas"}>
|
||||
<DropdownMenu.Trigger
|
||||
@ -30,7 +41,12 @@ const MainMenu = ({ children }: { children?: React.ReactNode }) => {
|
||||
>
|
||||
{HamburgerMenuIcon}
|
||||
</DropdownMenu.Trigger>
|
||||
<DropdownMenu.Content onClickOutside={onClickOutside}>
|
||||
<DropdownMenu.Content
|
||||
onClickOutside={onClickOutside}
|
||||
onSelect={composeEventHandlers(onSelect, () => {
|
||||
setAppState({ openMenu: null });
|
||||
})}
|
||||
>
|
||||
{children}
|
||||
{device.isMobile && appState.collaborators.size > 0 && (
|
||||
<fieldset className="UserList-Wrapper">
|
||||
|
@ -2,6 +2,14 @@ import cssVariables from "./css/variables.module.scss";
|
||||
import { AppProps } from "./types";
|
||||
import { FontFamilyValues } from "./element/types";
|
||||
|
||||
export const isDarwin = /Mac|iPod|iPhone|iPad/.test(navigator.platform);
|
||||
export const isWindows = /^Win/.test(navigator.platform);
|
||||
export const isAndroid = /\b(android)\b/i.test(navigator.userAgent);
|
||||
export const isFirefox =
|
||||
"netscape" in window &&
|
||||
navigator.userAgent.indexOf("rv:") > 1 &&
|
||||
navigator.userAgent.indexOf("Gecko") > 1;
|
||||
|
||||
export const APP_NAME = "Excalidraw";
|
||||
|
||||
export const DRAGGING_THRESHOLD = 10; // px
|
||||
@ -54,6 +62,7 @@ export enum EVENT {
|
||||
SCROLL = "scroll",
|
||||
// custom events
|
||||
EXCALIDRAW_LINK = "excalidraw-link",
|
||||
MENU_ITEM_SELECT = "menu.itemSelect",
|
||||
}
|
||||
|
||||
export const ENV = {
|
||||
|
@ -8,6 +8,10 @@
|
||||
}
|
||||
|
||||
.excalidraw {
|
||||
--ui-font: Assistant, system-ui, BlinkMacSystemFont, -apple-system, Segoe UI,
|
||||
Roboto, Helvetica, Arial, sans-serif;
|
||||
font-family: var(--ui-font);
|
||||
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
color: var(--text-primary-color);
|
||||
@ -549,6 +553,7 @@
|
||||
border-top-left-radius: var(--border-radius-lg);
|
||||
border-bottom-left-radius: var(--border-radius-lg);
|
||||
border-right: 0;
|
||||
overflow: hidden;
|
||||
|
||||
background-color: var(--island-bg-color);
|
||||
|
||||
|
@ -65,13 +65,18 @@
|
||||
background-color: var(--button-bg, var(--island-bg-color));
|
||||
color: var(--button-color, var(--text-primary-color));
|
||||
|
||||
svg {
|
||||
width: var(--button-width, var(--lg-icon-size));
|
||||
height: var(--button-height, var(--lg-icon-size));
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: var(--button-hover-bg);
|
||||
background-color: var(--button-hover-bg, var(--island-bg-color));
|
||||
border-color: var(--button-hover-border, var(--default-border-color));
|
||||
}
|
||||
|
||||
&:active {
|
||||
background-color: var(--button-active-bg);
|
||||
background-color: var(--button-active-bg, var(--island-bg-color));
|
||||
border-color: var(--button-active-border, var(--color-primary-darkest));
|
||||
}
|
||||
|
||||
@ -85,9 +90,6 @@
|
||||
|
||||
svg {
|
||||
color: var(--button-color, var(--color-primary-darker));
|
||||
|
||||
width: var(--button-width, var(--lg-icon-size));
|
||||
height: var(--button-height, var(--lg-icon-size));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ import {
|
||||
copyBlobToClipboardAsPng,
|
||||
copyTextToSystemClipboard,
|
||||
} from "../clipboard";
|
||||
import { DEFAULT_EXPORT_PADDING, MIME_TYPES } from "../constants";
|
||||
import { DEFAULT_EXPORT_PADDING, isFirefox, MIME_TYPES } from "../constants";
|
||||
import { NonDeletedExcalidrawElement } from "../element/types";
|
||||
import { t } from "../i18n";
|
||||
import { exportToCanvas, exportToSvg } from "../scene/export";
|
||||
@ -97,10 +97,21 @@ export const exportCanvas = async (
|
||||
const blob = canvasToBlob(tempCanvas);
|
||||
await copyBlobToClipboardAsPng(blob);
|
||||
} catch (error: any) {
|
||||
console.warn(error);
|
||||
if (error.name === "CANVAS_POSSIBLY_TOO_BIG") {
|
||||
throw error;
|
||||
}
|
||||
// TypeError *probably* suggests ClipboardItem not defined, which
|
||||
// people on Firefox can enable through a flag, so let's tell them.
|
||||
if (isFirefox && error.name === "TypeError") {
|
||||
throw new Error(
|
||||
`${t("alerts.couldNotCopyToClipboard")}\n\n${t(
|
||||
"hints.firefox_clipboard_write",
|
||||
)}`,
|
||||
);
|
||||
} else {
|
||||
throw new Error(t("alerts.couldNotCopyToClipboard"));
|
||||
}
|
||||
} finally {
|
||||
tempCanvas.remove();
|
||||
}
|
||||
|
@ -55,6 +55,7 @@ export const AllowedExcalidrawActiveTools: Record<
|
||||
freedraw: true,
|
||||
eraser: false,
|
||||
custom: true,
|
||||
hand: true,
|
||||
};
|
||||
|
||||
export type RestoredDataState = {
|
||||
@ -465,7 +466,7 @@ export const restoreAppState = (
|
||||
? nextAppState.activeTool
|
||||
: { type: "selection" },
|
||||
),
|
||||
lastActiveToolBeforeEraser: null,
|
||||
lastActiveTool: null,
|
||||
locked: nextAppState.activeTool.locked ?? false,
|
||||
},
|
||||
// Migrates from previous version where appState.zoom was a number
|
||||
|
@ -11,6 +11,7 @@ export const showSelectedShapeActions = (
|
||||
appState.activeTool.type !== "custom" &&
|
||||
(appState.editingElement ||
|
||||
(appState.activeTool.type !== "selection" &&
|
||||
appState.activeTool.type !== "eraser"))) ||
|
||||
appState.activeTool.type !== "eraser" &&
|
||||
appState.activeTool.type !== "hand"))) ||
|
||||
getSelectedElements(elements, appState).length,
|
||||
);
|
||||
|
@ -12,6 +12,20 @@ describe("Test wrapText", () => {
|
||||
expect(res).toBe("Hello whats up ");
|
||||
});
|
||||
|
||||
it("should work with emojis", () => {
|
||||
const text = "😀";
|
||||
const maxWidth = 1;
|
||||
const res = wrapText(text, font, maxWidth);
|
||||
expect(res).toBe("😀");
|
||||
});
|
||||
|
||||
it("should show the text correctly when min width reached", () => {
|
||||
const text = "Hello😀";
|
||||
const maxWidth = 10;
|
||||
const res = wrapText(text, font, maxWidth);
|
||||
expect(res).toBe("H\ne\nl\nl\no\n😀");
|
||||
});
|
||||
|
||||
describe("When text doesn't contain new lines", () => {
|
||||
const text = "Hello whats up";
|
||||
[
|
||||
|
@ -359,7 +359,8 @@ export const wrapText = (text: string, font: FontString, maxWidth: number) => {
|
||||
// This means its newline so push it
|
||||
if (words.length === 1 && words[0] === "") {
|
||||
lines.push(words[0]);
|
||||
} else {
|
||||
return; // continue
|
||||
}
|
||||
let currentLine = "";
|
||||
let currentLineWidthTillNow = 0;
|
||||
|
||||
@ -375,10 +376,12 @@ export const wrapText = (text: string, font: FontString, maxWidth: number) => {
|
||||
currentLine = "";
|
||||
currentLineWidthTillNow = 0;
|
||||
while (words[index].length > 0) {
|
||||
const currentChar = words[index][0];
|
||||
const currentChar = String.fromCodePoint(
|
||||
words[index].codePointAt(0)!,
|
||||
);
|
||||
const width = charWidth.calculate(currentChar, font);
|
||||
currentLineWidthTillNow += width;
|
||||
words[index] = words[index].slice(1);
|
||||
words[index] = words[index].slice(currentChar.length);
|
||||
|
||||
if (currentLineWidthTillNow >= maxWidth) {
|
||||
// only remove last trailing space which we have added when joining words
|
||||
@ -388,10 +391,6 @@ export const wrapText = (text: string, font: FontString, maxWidth: number) => {
|
||||
push(currentLine);
|
||||
currentLine = currentChar;
|
||||
currentLineWidthTillNow = width;
|
||||
if (currentLineWidthTillNow === maxWidth) {
|
||||
currentLine = "";
|
||||
currentLineWidthTillNow = 0;
|
||||
}
|
||||
} else {
|
||||
currentLine += currentChar;
|
||||
}
|
||||
@ -448,7 +447,6 @@ export const wrapText = (text: string, font: FontString, maxWidth: number) => {
|
||||
}
|
||||
push(currentLine);
|
||||
}
|
||||
}
|
||||
});
|
||||
return lines.join("\n");
|
||||
};
|
||||
|
@ -1,6 +1,4 @@
|
||||
export const isDarwin = /Mac|iPod|iPhone|iPad/.test(window.navigator.platform);
|
||||
export const isWindows = /^Win/.test(window.navigator.platform);
|
||||
export const isAndroid = /\b(android)\b/i.test(navigator.userAgent);
|
||||
import { isDarwin } from "./constants";
|
||||
|
||||
export const CODES = {
|
||||
EQUAL: "Equal",
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"labels": {
|
||||
"paste": "لصق",
|
||||
"pasteAsPlaintext": "",
|
||||
"pasteAsPlaintext": "اللصق كنص عادي",
|
||||
"pasteCharts": "لصق الرسوم البيانية",
|
||||
"selectAll": "تحديد الكل",
|
||||
"multiSelect": "إضافة عنصر للتحديد",
|
||||
@ -66,13 +66,13 @@
|
||||
"cartoonist": "كرتوني",
|
||||
"fileTitle": "إسم الملف",
|
||||
"colorPicker": "منتقي اللون",
|
||||
"canvasColors": "",
|
||||
"canvasColors": "تستخدم على القماش",
|
||||
"canvasBackground": "خلفية اللوحة",
|
||||
"drawingCanvas": "لوحة الرسم",
|
||||
"layers": "الطبقات",
|
||||
"actions": "الإجراءات",
|
||||
"language": "اللغة",
|
||||
"liveCollaboration": "",
|
||||
"liveCollaboration": "التعاون المباشر...",
|
||||
"duplicateSelection": "تكرار",
|
||||
"untitled": "غير معنون",
|
||||
"name": "الاسم",
|
||||
@ -108,7 +108,7 @@
|
||||
"excalidrawLib": "مكتبتنا",
|
||||
"decreaseFontSize": "تصغير حجم الخط",
|
||||
"increaseFontSize": "تكبير حجم الخط",
|
||||
"unbindText": "",
|
||||
"unbindText": "فك ربط النص",
|
||||
"bindText": "",
|
||||
"link": {
|
||||
"edit": "تعديل الرابط",
|
||||
@ -145,7 +145,7 @@
|
||||
"scale": "مقاس",
|
||||
"save": "احفظ للملف الحالي",
|
||||
"saveAs": "حفظ كـ",
|
||||
"load": "",
|
||||
"load": "فتح",
|
||||
"getShareableLink": "احصل على رابط المشاركة",
|
||||
"close": "غلق",
|
||||
"selectLanguage": "اختر اللغة",
|
||||
@ -447,10 +447,16 @@
|
||||
"d9480f": "برتقالي 9"
|
||||
},
|
||||
"welcomeScreen": {
|
||||
"data": "",
|
||||
"switchToPlusApp": "",
|
||||
"menuHints": "",
|
||||
"toolbarHints": "",
|
||||
"helpHints": ""
|
||||
"app": {
|
||||
"center_heading": "",
|
||||
"center_heading_plus": "",
|
||||
"menuHint": ""
|
||||
},
|
||||
"defaults": {
|
||||
"menuHint": "",
|
||||
"center_heading": "",
|
||||
"toolbarHint": "",
|
||||
"helpHint": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -447,10 +447,16 @@
|
||||
"d9480f": ""
|
||||
},
|
||||
"welcomeScreen": {
|
||||
"data": "",
|
||||
"switchToPlusApp": "",
|
||||
"menuHints": "",
|
||||
"toolbarHints": "",
|
||||
"helpHints": ""
|
||||
"app": {
|
||||
"center_heading": "",
|
||||
"center_heading_plus": "",
|
||||
"menuHint": ""
|
||||
},
|
||||
"defaults": {
|
||||
"menuHint": "",
|
||||
"center_heading": "",
|
||||
"toolbarHint": "",
|
||||
"helpHint": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -447,10 +447,16 @@
|
||||
"d9480f": ""
|
||||
},
|
||||
"welcomeScreen": {
|
||||
"data": "",
|
||||
"switchToPlusApp": "",
|
||||
"menuHints": "",
|
||||
"toolbarHints": "",
|
||||
"helpHints": ""
|
||||
"app": {
|
||||
"center_heading": "",
|
||||
"center_heading_plus": "",
|
||||
"menuHint": ""
|
||||
},
|
||||
"defaults": {
|
||||
"menuHint": "",
|
||||
"center_heading": "",
|
||||
"toolbarHint": "",
|
||||
"helpHint": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -447,10 +447,16 @@
|
||||
"d9480f": "Taronja 9"
|
||||
},
|
||||
"welcomeScreen": {
|
||||
"data": "",
|
||||
"switchToPlusApp": "",
|
||||
"menuHints": "",
|
||||
"toolbarHints": "",
|
||||
"helpHints": ""
|
||||
"app": {
|
||||
"center_heading": "",
|
||||
"center_heading_plus": "",
|
||||
"menuHint": ""
|
||||
},
|
||||
"defaults": {
|
||||
"menuHint": "",
|
||||
"center_heading": "",
|
||||
"toolbarHint": "",
|
||||
"helpHint": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -447,10 +447,16 @@
|
||||
"d9480f": "Oranzova"
|
||||
},
|
||||
"welcomeScreen": {
|
||||
"data": "",
|
||||
"switchToPlusApp": "",
|
||||
"menuHints": "",
|
||||
"toolbarHints": "",
|
||||
"helpHints": ""
|
||||
"app": {
|
||||
"center_heading": "",
|
||||
"center_heading_plus": "",
|
||||
"menuHint": ""
|
||||
},
|
||||
"defaults": {
|
||||
"menuHint": "",
|
||||
"center_heading": "",
|
||||
"toolbarHint": "",
|
||||
"helpHint": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -447,10 +447,16 @@
|
||||
"d9480f": ""
|
||||
},
|
||||
"welcomeScreen": {
|
||||
"data": "",
|
||||
"switchToPlusApp": "",
|
||||
"menuHints": "",
|
||||
"toolbarHints": "",
|
||||
"helpHints": ""
|
||||
"app": {
|
||||
"center_heading": "",
|
||||
"center_heading_plus": "",
|
||||
"menuHint": ""
|
||||
},
|
||||
"defaults": {
|
||||
"menuHint": "",
|
||||
"center_heading": "",
|
||||
"toolbarHint": "",
|
||||
"helpHint": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -99,14 +99,14 @@
|
||||
"flipHorizontal": "Horizontal spiegeln",
|
||||
"flipVertical": "Vertikal spiegeln",
|
||||
"viewMode": "Ansichtsmodus",
|
||||
"toggleExportColorScheme": "Exportfarbschema umschalten",
|
||||
"toggleExportColorScheme": "Farbschema für Export umschalten",
|
||||
"share": "Teilen",
|
||||
"showStroke": "Auswahl für Strichfarbe anzeigen",
|
||||
"showBackground": "Hintergrundfarbe auswählen",
|
||||
"toggleTheme": "Thema umschalten",
|
||||
"toggleTheme": "Design umschalten",
|
||||
"personalLib": "Persönliche Bibliothek",
|
||||
"excalidrawLib": "Excalidraw-Bibliothek",
|
||||
"decreaseFontSize": "Schrift verkleinern",
|
||||
"excalidrawLib": "Excalidraw Bibliothek",
|
||||
"decreaseFontSize": "Schriftgröße verkleinern",
|
||||
"increaseFontSize": "Schrift vergrößern",
|
||||
"unbindText": "Text lösen",
|
||||
"bindText": "Text an Container binden",
|
||||
@ -161,8 +161,8 @@
|
||||
"resetLibrary": "Bibliothek zurücksetzen",
|
||||
"createNewRoom": "Neuen Raum erstellen",
|
||||
"fullScreen": "Vollbildanzeige",
|
||||
"darkMode": "Dunkler Modus",
|
||||
"lightMode": "Heller Modus",
|
||||
"darkMode": "Dunkles Design",
|
||||
"lightMode": "Helles Design",
|
||||
"zenMode": "Zen-Modus",
|
||||
"exitZenMode": "Zen-Modus verlassen",
|
||||
"cancel": "Abbrechen",
|
||||
@ -447,10 +447,16 @@
|
||||
"d9480f": "Orange 9"
|
||||
},
|
||||
"welcomeScreen": {
|
||||
"data": "Alle Daten werden lokal in Deinem Browser gespeichert.",
|
||||
"switchToPlusApp": "Möchtest du stattdessen zu Excalidraw+ gehen?",
|
||||
"menuHints": "Exportieren, Einstellungen, Sprachen, ...",
|
||||
"toolbarHints": "Wähle ein Werkzeug & beginne zu zeichnen!",
|
||||
"helpHints": "Kurzbefehle & Hilfe"
|
||||
"app": {
|
||||
"center_heading": "Alle Daten werden lokal in Deinem Browser gespeichert.",
|
||||
"center_heading_plus": "Möchtest du stattdessen zu Excalidraw+ gehen?",
|
||||
"menuHint": "Exportieren, Einstellungen, Sprachen, ..."
|
||||
},
|
||||
"defaults": {
|
||||
"menuHint": "Exportieren, Einstellungen und mehr...",
|
||||
"center_heading": "Diagramme. Einfach. Gemacht.",
|
||||
"toolbarHint": "Wähle ein Werkzeug & beginne zu zeichnen!",
|
||||
"helpHint": "Kurzbefehle & Hilfe"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"labels": {
|
||||
"paste": "Επικόλληση",
|
||||
"pasteAsPlaintext": "",
|
||||
"pasteAsPlaintext": "Επικόλληση ως απλό κείμενο",
|
||||
"pasteCharts": "Επικόλληση γραφημάτων",
|
||||
"selectAll": "Επιλογή όλων",
|
||||
"multiSelect": "Προσθέστε το στοιχείο στην επιλογή",
|
||||
@ -72,7 +72,7 @@
|
||||
"layers": "Στρώματα",
|
||||
"actions": "Ενέργειες",
|
||||
"language": "Γλώσσα",
|
||||
"liveCollaboration": "",
|
||||
"liveCollaboration": "Live συνεργασία...",
|
||||
"duplicateSelection": "Δημιουργία αντιγράφου",
|
||||
"untitled": "Χωρίς τίτλο",
|
||||
"name": "Όνομα",
|
||||
@ -116,8 +116,8 @@
|
||||
"label": "Σύνδεσμος"
|
||||
},
|
||||
"lineEditor": {
|
||||
"edit": "",
|
||||
"exit": ""
|
||||
"edit": "Επεξεργασία γραμμής",
|
||||
"exit": "Έξοδος επεξεργαστή κειμένου"
|
||||
},
|
||||
"elementLock": {
|
||||
"lock": "Κλείδωμα",
|
||||
@ -136,8 +136,8 @@
|
||||
"buttons": {
|
||||
"clearReset": "Επαναφορά του καμβά",
|
||||
"exportJSON": "Εξαγωγή σε αρχείο",
|
||||
"exportImage": "",
|
||||
"export": "",
|
||||
"exportImage": "Εξαγωγή εικόνας...",
|
||||
"export": "Αποθήκευση ως...",
|
||||
"exportToPng": "Εξαγωγή σε PNG",
|
||||
"exportToSvg": "Εξαγωγή σε SVG",
|
||||
"copyToClipboard": "Αντιγραφή στο πρόχειρο",
|
||||
@ -145,7 +145,7 @@
|
||||
"scale": "Κλίμακα",
|
||||
"save": "Αποθήκευση στο τρέχον αρχείο",
|
||||
"saveAs": "Αποθήκευση ως",
|
||||
"load": "",
|
||||
"load": "Άνοιγμα",
|
||||
"getShareableLink": "Δημόσιος σύνδεσμος",
|
||||
"close": "Κλείσιμο",
|
||||
"selectLanguage": "Επιλογή γλώσσας",
|
||||
@ -202,8 +202,8 @@
|
||||
"invalidSVGString": "Μη έγκυρο SVG.",
|
||||
"cannotResolveCollabServer": "Αδυναμία σύνδεσης με τον διακομιστή συνεργασίας. Παρακαλώ ανανεώστε τη σελίδα και προσπαθήστε ξανά.",
|
||||
"importLibraryError": "Αδυναμία φόρτωσης βιβλιοθήκης",
|
||||
"collabSaveFailed": "",
|
||||
"collabSaveFailed_sizeExceeded": ""
|
||||
"collabSaveFailed": "Η αποθήκευση στη βάση δεδομένων δεν ήταν δυνατή. Αν το προβλήματα παραμείνει, θα πρέπει να αποθηκεύσετε το αρχείο σας τοπικά για να βεβαιωθείτε ότι δεν χάνετε την εργασία σας.",
|
||||
"collabSaveFailed_sizeExceeded": "Η αποθήκευση στη βάση δεδομένων δεν ήταν δυνατή, ο καμβάς φαίνεται να είναι πολύ μεγάλος. Θα πρέπει να αποθηκεύσετε το αρχείο τοπικά για να βεβαιωθείτε ότι δεν θα χάσετε την εργασία σας."
|
||||
},
|
||||
"toolBar": {
|
||||
"selection": "Επιλογή",
|
||||
@ -217,7 +217,7 @@
|
||||
"text": "Κείμενο",
|
||||
"library": "Βιβλιοθήκη",
|
||||
"lock": "Κράτησε επιλεγμένο το εργαλείο μετά το σχέδιο",
|
||||
"penMode": "",
|
||||
"penMode": "Λειτουργία μολυβιού - αποτροπή αφής",
|
||||
"link": "Προσθήκη/ Ενημέρωση συνδέσμου για ένα επιλεγμένο σχήμα",
|
||||
"eraser": "Γόμα"
|
||||
},
|
||||
@ -238,7 +238,7 @@
|
||||
"resize": "Μπορείς να περιορίσεις τις αναλογίες κρατώντας το SHIFT ενώ αλλάζεις μέγεθος,\nκράτησε πατημένο το ALT για αλλαγή μεγέθους από το κέντρο",
|
||||
"resizeImage": "Μπορείτε να αλλάξετε το μέγεθος ελεύθερα κρατώντας πατημένο το SHIFT,\nκρατήστε πατημένο το ALT για να αλλάξετε το μέγεθος από το κέντρο",
|
||||
"rotate": "Μπορείς να περιορίσεις τις γωνίες κρατώντας πατημένο το πλήκτρο SHIFT κατά την περιστροφή",
|
||||
"lineEditor_info": "",
|
||||
"lineEditor_info": "Κρατήστε πατημένο Ctrl ή Cmd και πατήστε το πλήκτρο Ctrl ή Cmd + Enter για επεξεργασία σημείων",
|
||||
"lineEditor_pointSelected": "Πατήστε Διαγραφή για αφαίρεση σημείου(ων),\nCtrlOrCmd+D για αντιγραφή, ή σύρετε για μετακίνηση",
|
||||
"lineEditor_nothingSelected": "Επιλέξτε ένα σημείο για να επεξεργαστείτε (κρατήστε πατημένο το SHIFT για να επιλέξετε πολλαπλά),\nή κρατήστε πατημένο το Alt και κάντε κλικ για να προσθέσετε νέα σημεία",
|
||||
"placeImage": "Κάντε κλικ για να τοποθετήσετε την εικόνα ή κάντε κλικ και σύρετε για να ορίσετε το μέγεθός της χειροκίνητα",
|
||||
@ -314,8 +314,8 @@
|
||||
"zoomToFit": "Zoom ώστε να χωρέσουν όλα τα στοιχεία",
|
||||
"zoomToSelection": "Ζουμ στην επιλογή",
|
||||
"toggleElementLock": "Κλείδωμα/Ξεκλείδωμα επιλογής",
|
||||
"movePageUpDown": "",
|
||||
"movePageLeftRight": ""
|
||||
"movePageUpDown": "Μετακίνηση σελίδας πάνω/κάτω",
|
||||
"movePageLeftRight": "Μετακίνηση σελίδας αριστερά/δεξιά"
|
||||
},
|
||||
"clearCanvasDialog": {
|
||||
"title": "Καθαρισμός καμβά"
|
||||
@ -397,7 +397,7 @@
|
||||
"fileSavedToFilename": "Αποθηκεύτηκε στο {filename}",
|
||||
"canvas": "καμβάς",
|
||||
"selection": "επιλογή",
|
||||
"pasteAsSingleElement": ""
|
||||
"pasteAsSingleElement": "Χρησιμοποίησε το {{shortcut}} για να επικολλήσεις ως ένα μόνο στοιχείο,\nή να επικολλήσεις σε έναν υπάρχοντα επεξεργαστή κειμένου"
|
||||
},
|
||||
"colors": {
|
||||
"ffffff": "Λευκό",
|
||||
@ -447,10 +447,16 @@
|
||||
"d9480f": "Πορτοκαλί 9"
|
||||
},
|
||||
"welcomeScreen": {
|
||||
"data": "",
|
||||
"switchToPlusApp": "",
|
||||
"menuHints": "",
|
||||
"toolbarHints": "",
|
||||
"helpHints": ""
|
||||
"app": {
|
||||
"center_heading": "Όλα τα δεδομένα σας αποθηκεύονται τοπικά στο πρόγραμμα περιήγησης.",
|
||||
"center_heading_plus": "Μήπως θέλατε να πάτε στο Excalidraw+;",
|
||||
"menuHint": "Εξαγωγή, προτιμήσεις, γλώσσες, ..."
|
||||
},
|
||||
"defaults": {
|
||||
"menuHint": "Εξαγωγή, προτιμήσεις και άλλες επιλογές...",
|
||||
"center_heading": "Διαγράμματα. Εύκολα. Γρήγορα.",
|
||||
"toolbarHint": "Επιλέξτε ένα εργαλείο και ξεκινήστε να σχεδιάζεται!",
|
||||
"helpHint": "Συντομεύσεις και βοήθεια"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -220,7 +220,8 @@
|
||||
"lock": "Keep selected tool active after drawing",
|
||||
"penMode": "Pen mode - prevent touch",
|
||||
"link": "Add/ Update link for a selected shape",
|
||||
"eraser": "Eraser"
|
||||
"eraser": "Eraser",
|
||||
"hand": "Hand (panning tool)"
|
||||
},
|
||||
"headings": {
|
||||
"canvasActions": "Canvas actions",
|
||||
@ -228,7 +229,7 @@
|
||||
"shapes": "Shapes"
|
||||
},
|
||||
"hints": {
|
||||
"canvasPanning": "To move canvas, hold mouse wheel or spacebar while dragging",
|
||||
"canvasPanning": "To move canvas, hold mouse wheel or spacebar while dragging, or use the hand tool",
|
||||
"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",
|
||||
@ -246,7 +247,8 @@
|
||||
"publishLibrary": "Publish your own library",
|
||||
"bindTextToElement": "Press enter to add text",
|
||||
"deepBoxSelect": "Hold CtrlOrCmd to deep select, and to prevent dragging",
|
||||
"eraserRevert": "Hold Alt to revert the elements marked for deletion"
|
||||
"eraserRevert": "Hold Alt to revert the elements marked for deletion",
|
||||
"firefox_clipboard_write": "This feature can likely be enabled by setting the \"dom.events.asyncClipboard.clipboardItem\" flag to \"true\". To change the browser flags in Firefox, visit the \"about:config\" page."
|
||||
},
|
||||
"canvasError": {
|
||||
"cannotShowPreview": "Cannot show preview",
|
||||
|
@ -447,10 +447,16 @@
|
||||
"d9480f": "Naranja 9"
|
||||
},
|
||||
"welcomeScreen": {
|
||||
"data": "Toda su información es guardada localmente en su navegador.",
|
||||
"switchToPlusApp": "¿Quieres ir a Excalidraw+ en su lugar?",
|
||||
"menuHints": "Exportar, preferencias, idiomas, ...",
|
||||
"toolbarHints": "¡Escoge una herramienta & Empiece a dibujar!",
|
||||
"helpHints": "Atajos & ayuda"
|
||||
"app": {
|
||||
"center_heading": "Toda su información es guardada localmente en su navegador.",
|
||||
"center_heading_plus": "¿Quieres ir a Excalidraw+?",
|
||||
"menuHint": "Exportar, preferencias, idiomas, ..."
|
||||
},
|
||||
"defaults": {
|
||||
"menuHint": "Exportar, preferencias y más...",
|
||||
"center_heading": "Diagramas. Hecho. Simplemente.",
|
||||
"toolbarHint": "¡Elige una herramienta y empieza a dibujar!",
|
||||
"helpHint": "Atajos & ayuda"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"labels": {
|
||||
"paste": "Itsatsi",
|
||||
"pasteAsPlaintext": "",
|
||||
"pasteAsPlaintext": "Itsatsi testu arrunt gisa",
|
||||
"pasteCharts": "Itsatsi grafikoak",
|
||||
"selectAll": "Hautatu dena",
|
||||
"multiSelect": "Gehitu elementua hautapenera",
|
||||
@ -202,8 +202,8 @@
|
||||
"invalidSVGString": "SVG baliogabea.",
|
||||
"cannotResolveCollabServer": "Ezin izan da elkarlaneko zerbitzarira konektatu. Mesedez, berriro kargatu orria eta saiatu berriro.",
|
||||
"importLibraryError": "Ezin izan da liburutegia kargatu",
|
||||
"collabSaveFailed": "",
|
||||
"collabSaveFailed_sizeExceeded": ""
|
||||
"collabSaveFailed": "Ezin izan da backend datu-basean gorde. Arazoak jarraitzen badu, zure fitxategia lokalean gorde beharko zenuke zure lana ez duzula galtzen ziurtatzeko.",
|
||||
"collabSaveFailed_sizeExceeded": "Ezin izan da backend datu-basean gorde, ohiala handiegia dela dirudi. Fitxategia lokalean gorde beharko zenuke zure lana galtzen ez duzula ziurtatzeko."
|
||||
},
|
||||
"toolBar": {
|
||||
"selection": "Hautapena",
|
||||
@ -238,7 +238,7 @@
|
||||
"resize": "Proportzioak mantendu ditzakezu SHIFT sakatuta tamaina aldatzen duzun bitartean.\nsakatu ALT erditik tamaina aldatzeko",
|
||||
"resizeImage": "Tamaina libreki alda dezakezu SHIFT sakatuta,\nsakatu ALT erditik tamaina aldatzeko",
|
||||
"rotate": "Angeluak mantendu ditzakezu SHIFT sakatuta biratzen duzun bitartean",
|
||||
"lineEditor_info": "",
|
||||
"lineEditor_info": "Eutsi sakatuta Ctrl edo Cmd eta egin klik bikoitza edo sakatu Ctrl edo Cmd + Sartu puntuak editatzeko",
|
||||
"lineEditor_pointSelected": "Sakatu Ezabatu puntuak kentzeko,\nKtrl+D bikoizteko, edo arrastatu mugitzeko",
|
||||
"lineEditor_nothingSelected": "Hautatu editatzeko puntu bat (SHIFT sakatuta anitz hautatzeko),\nedo eduki Alt sakatuta eta egin klik puntu berriak gehitzeko",
|
||||
"placeImage": "Egin klik irudia kokatzeko, edo egin klik eta arrastatu bere tamaina eskuz ezartzeko",
|
||||
@ -314,8 +314,8 @@
|
||||
"zoomToFit": "Egin zoom elementu guztiak ikusteko",
|
||||
"zoomToSelection": "Zooma hautapenera",
|
||||
"toggleElementLock": "Blokeatu/desbloketatu hautapena",
|
||||
"movePageUpDown": "",
|
||||
"movePageLeftRight": ""
|
||||
"movePageUpDown": "Mugitu orria gora/behera",
|
||||
"movePageLeftRight": "Mugitu orria ezker/eskuin"
|
||||
},
|
||||
"clearCanvasDialog": {
|
||||
"title": "Garbitu oihala"
|
||||
@ -397,7 +397,7 @@
|
||||
"fileSavedToFilename": "{filename}-n gorde da",
|
||||
"canvas": "oihala",
|
||||
"selection": "hautapena",
|
||||
"pasteAsSingleElement": ""
|
||||
"pasteAsSingleElement": "Erabili {{shortcut}} elementu bakar gisa itsasteko,\nedo itsatsi lehendik dagoen testu-editore batean"
|
||||
},
|
||||
"colors": {
|
||||
"ffffff": "Zuria",
|
||||
@ -447,10 +447,16 @@
|
||||
"d9480f": "Laranja 9"
|
||||
},
|
||||
"welcomeScreen": {
|
||||
"data": "Zure datu guztiak modu lokalean gordetzen dira zure nabigatzailean.",
|
||||
"switchToPlusApp": "Horren ordez Excalidraw+-ra joan nahi al zenuen?",
|
||||
"menuHints": "Esportatu, hobespenak, hizkuntzak,...",
|
||||
"toolbarHints": "Aukeratu tresna bat eta hasi marrazten!",
|
||||
"helpHints": "Lasterbideak eta laguntza"
|
||||
"app": {
|
||||
"center_heading": "Zure datu guztiak lokalean gordetzen dira zure nabigatzailean.",
|
||||
"center_heading_plus": "Horren ordez Excalidraw+-era joan nahi al zenuen?",
|
||||
"menuHint": "Esportatu, hobespenak, hizkuntzak..."
|
||||
},
|
||||
"defaults": {
|
||||
"menuHint": "Esportatu, hobespenak eta gehiago...",
|
||||
"center_heading": "Diagramak. Egina. Sinplea.",
|
||||
"toolbarHint": "Aukeratu tresna bat eta hasi marrazten!",
|
||||
"helpHint": "Lasterbideak eta laguntza"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -447,10 +447,16 @@
|
||||
"d9480f": "نارنجی 9"
|
||||
},
|
||||
"welcomeScreen": {
|
||||
"data": "همه ی داده های شما به صورت محلی در مرورگر ذخیره میشود.",
|
||||
"switchToPlusApp": "آیا ترجیح میدهید به Excalidraw+ بروید؟",
|
||||
"menuHints": "خروجی گرفتن، تنظیمات، زبانها، ...",
|
||||
"toolbarHints": "یک ابزار را انتخاب کنید و ترسیم را شروع کنید!",
|
||||
"helpHints": "میانبرها و کمک"
|
||||
"app": {
|
||||
"center_heading": "",
|
||||
"center_heading_plus": "",
|
||||
"menuHint": ""
|
||||
},
|
||||
"defaults": {
|
||||
"menuHint": "",
|
||||
"center_heading": "",
|
||||
"toolbarHint": "",
|
||||
"helpHint": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -447,10 +447,16 @@
|
||||
"d9480f": "Oranssi 9"
|
||||
},
|
||||
"welcomeScreen": {
|
||||
"data": "",
|
||||
"switchToPlusApp": "",
|
||||
"menuHints": "",
|
||||
"toolbarHints": "",
|
||||
"helpHints": ""
|
||||
"app": {
|
||||
"center_heading": "",
|
||||
"center_heading_plus": "",
|
||||
"menuHint": ""
|
||||
},
|
||||
"defaults": {
|
||||
"menuHint": "",
|
||||
"center_heading": "",
|
||||
"toolbarHint": "",
|
||||
"helpHint": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -447,10 +447,16 @@
|
||||
"d9480f": "Orange 9"
|
||||
},
|
||||
"welcomeScreen": {
|
||||
"data": "Toutes vos données sont sauvegardées en local dans votre navigateur.",
|
||||
"switchToPlusApp": "Vous vouliez plutôt aller à Excalidraw+ ?",
|
||||
"menuHints": "Exportation, préférences, langues, ...",
|
||||
"toolbarHints": "Choisissez un outil et commencez à dessiner !",
|
||||
"helpHints": "Raccourcis et aide"
|
||||
"app": {
|
||||
"center_heading": "Toutes vos données sont sauvegardées en local dans votre navigateur.",
|
||||
"center_heading_plus": "Vouliez-vous plutôt aller à Excalidraw+ à la place ?",
|
||||
"menuHint": "Exportation, préférences, langues, ..."
|
||||
},
|
||||
"defaults": {
|
||||
"menuHint": "Exportation, préférences et plus...",
|
||||
"center_heading": "Diagrammes. Rendus. Simples.",
|
||||
"toolbarHint": "Choisissez un outil et commencez à dessiner !",
|
||||
"helpHint": "Raccourcis et aide"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -202,8 +202,8 @@
|
||||
"invalidSVGString": "SVG inválido.",
|
||||
"cannotResolveCollabServer": "Non se puido conectar ao servidor de colaboración. Por favor recargue a páxina e probe de novo.",
|
||||
"importLibraryError": "Non se puido cargar a biblioteca",
|
||||
"collabSaveFailed": "",
|
||||
"collabSaveFailed_sizeExceeded": ""
|
||||
"collabSaveFailed": "Non se puido gardar na base de datos. Se o problema persiste, deberías gardar o teu arquivo de maneira local para asegurarte de non perdelo teu traballo.",
|
||||
"collabSaveFailed_sizeExceeded": "Non se puido gardar na base de datos, o lenzo semella demasiado grande. Deberías gardar o teu arquivo de maneira local para asegurarte de non perdelo teu traballo."
|
||||
},
|
||||
"toolBar": {
|
||||
"selection": "Selección",
|
||||
@ -447,10 +447,16 @@
|
||||
"d9480f": "Laranxa 9"
|
||||
},
|
||||
"welcomeScreen": {
|
||||
"data": "Toda a información é gardada de maneira local no seu navegador.",
|
||||
"switchToPlusApp": "Queres ir a Excalidraw+ no seu lugar?",
|
||||
"menuHints": "Exportar, preferencias, idiomas, ...",
|
||||
"toolbarHints": "Escolle unha ferramenta & Comeza a debuxar!",
|
||||
"helpHints": "Atallos & axuda"
|
||||
"app": {
|
||||
"center_heading": "Toda a información é gardada de maneira local no seu navegador.",
|
||||
"center_heading_plus": "Queres ir a Excalidraw+ no seu lugar?",
|
||||
"menuHint": "Exportar, preferencias, idiomas, ..."
|
||||
},
|
||||
"defaults": {
|
||||
"menuHint": "Exportar, preferencias, e máis...",
|
||||
"center_heading": "Diagramas. Feito. Sinxelo.",
|
||||
"toolbarHint": "Escolle unha ferramenta & Comeza a debuxar!",
|
||||
"helpHint": "Atallos & axuda"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -447,10 +447,16 @@
|
||||
"d9480f": "כתום 9"
|
||||
},
|
||||
"welcomeScreen": {
|
||||
"data": "",
|
||||
"switchToPlusApp": "",
|
||||
"menuHints": "",
|
||||
"toolbarHints": "",
|
||||
"helpHints": ""
|
||||
"app": {
|
||||
"center_heading": "",
|
||||
"center_heading_plus": "",
|
||||
"menuHint": ""
|
||||
},
|
||||
"defaults": {
|
||||
"menuHint": "",
|
||||
"center_heading": "",
|
||||
"toolbarHint": "",
|
||||
"helpHint": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -447,10 +447,16 @@
|
||||
"d9480f": "नारंगी"
|
||||
},
|
||||
"welcomeScreen": {
|
||||
"data": "आपका सर्व डेटा ब्राउज़र के भीतर स्थानिक जगह पे सुरक्षित किया गया.",
|
||||
"switchToPlusApp": "बजाय आपको Excalidraw+ जगह जाना है?",
|
||||
"menuHints": "निर्यात, पसंद, भाषायें, ...",
|
||||
"toolbarHints": "औजार चुने और चित्रकारी प्रारंभ करे!",
|
||||
"helpHints": "शॉर्ट्कट & सहाय्य"
|
||||
"app": {
|
||||
"center_heading": "",
|
||||
"center_heading_plus": "",
|
||||
"menuHint": ""
|
||||
},
|
||||
"defaults": {
|
||||
"menuHint": "",
|
||||
"center_heading": "",
|
||||
"toolbarHint": "",
|
||||
"helpHint": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -447,10 +447,16 @@
|
||||
"d9480f": "Narancs 9"
|
||||
},
|
||||
"welcomeScreen": {
|
||||
"data": "",
|
||||
"switchToPlusApp": "",
|
||||
"menuHints": "",
|
||||
"toolbarHints": "",
|
||||
"helpHints": ""
|
||||
"app": {
|
||||
"center_heading": "",
|
||||
"center_heading_plus": "",
|
||||
"menuHint": ""
|
||||
},
|
||||
"defaults": {
|
||||
"menuHint": "",
|
||||
"center_heading": "",
|
||||
"toolbarHint": "",
|
||||
"helpHint": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -202,8 +202,8 @@
|
||||
"invalidSVGString": "SVG tidak valid.",
|
||||
"cannotResolveCollabServer": "Tidak dapat terhubung ke server kolab. Muat ulang laman dan coba lagi.",
|
||||
"importLibraryError": "Tidak dapat memuat pustaka",
|
||||
"collabSaveFailed": "",
|
||||
"collabSaveFailed_sizeExceeded": ""
|
||||
"collabSaveFailed": "Tidak dapat menyimpan ke dalam basis data server. Jika masih berlanjut, Anda sebaiknya simpan berkas Anda secara lokal untuk memastikan pekerjaan Anda tidak hilang.",
|
||||
"collabSaveFailed_sizeExceeded": "Tidak dapat menyimpan ke dalam basis data server, tampaknya ukuran kanvas terlalu besar. Anda sebaiknya simpan berkas Anda secara lokal untuk memastikan pekerjaan Anda tidak hilang."
|
||||
},
|
||||
"toolBar": {
|
||||
"selection": "Pilihan",
|
||||
@ -447,10 +447,16 @@
|
||||
"d9480f": "Jingga 9"
|
||||
},
|
||||
"welcomeScreen": {
|
||||
"data": "Semua data Anda tersimpan secara lokal di browser.",
|
||||
"switchToPlusApp": "Apa Anda ingin berpindah ke Excalidraw+?",
|
||||
"menuHints": "Ekspor, preferensi, bahasa, ...",
|
||||
"toolbarHints": "Ambil alat & mulai menggambar!",
|
||||
"helpHints": "Pintasan & bantuan"
|
||||
"app": {
|
||||
"center_heading": "Semua data Anda disimpan secara lokal di peramban Anda.",
|
||||
"center_heading_plus": "Apa Anda ingin berpindah ke Excalidraw+?",
|
||||
"menuHint": "Ekspor, preferensi, bahasa, ..."
|
||||
},
|
||||
"defaults": {
|
||||
"menuHint": "Ekspor, preferensi, dan selebihnya...",
|
||||
"center_heading": "Diagram. Menjadi. Mudah.",
|
||||
"toolbarHint": "Pilih alat & mulai menggambar!",
|
||||
"helpHint": "Pintasan & bantuan"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -202,8 +202,8 @@
|
||||
"invalidSVGString": "SVG non valido.",
|
||||
"cannotResolveCollabServer": "Impossibile connettersi al server di collab. Ricarica la pagina e riprova.",
|
||||
"importLibraryError": "Impossibile caricare la libreria",
|
||||
"collabSaveFailed": "",
|
||||
"collabSaveFailed_sizeExceeded": ""
|
||||
"collabSaveFailed": "Impossibile salvare nel database di backend. Se i problemi persistono, dovresti salvare il tuo file localmente per assicurarti di non perdere il tuo lavoro.",
|
||||
"collabSaveFailed_sizeExceeded": "Impossibile salvare nel database di backend, la tela sembra essere troppo grande. Dovresti salvare il file localmente per assicurarti di non perdere il tuo lavoro."
|
||||
},
|
||||
"toolBar": {
|
||||
"selection": "Selezione",
|
||||
@ -447,10 +447,16 @@
|
||||
"d9480f": "Arancio 9"
|
||||
},
|
||||
"welcomeScreen": {
|
||||
"data": "Tutti i tuoi dati sono salvati localmente nel browser.",
|
||||
"switchToPlusApp": "Volevi invece andare su Excalidraw+?",
|
||||
"menuHints": "Esporta, preferenze, lingue, ...",
|
||||
"toolbarHints": "Scegli uno strumento & Inizia a disegnare!",
|
||||
"helpHints": "Scorciatoie & aiuto"
|
||||
"app": {
|
||||
"center_heading": "Tutti i tuoi dati sono salvati localmente nel browser.",
|
||||
"center_heading_plus": "Volevi invece andare su Excalidraw+?",
|
||||
"menuHint": "Esporta, preferenze, lingue, ..."
|
||||
},
|
||||
"defaults": {
|
||||
"menuHint": "Esporta, preferenze, e altro...",
|
||||
"center_heading": "Diagrammi. Fatto. Semplice.",
|
||||
"toolbarHint": "Scegli uno strumento & Inizia a disegnare!",
|
||||
"helpHint": "Scorciatoie & aiuto"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -447,10 +447,16 @@
|
||||
"d9480f": "オレンジ 9"
|
||||
},
|
||||
"welcomeScreen": {
|
||||
"data": "すべてのデータはブラウザにローカル保存されます。",
|
||||
"switchToPlusApp": "代わりにExcalidraw+を開きますか?",
|
||||
"menuHints": "エクスポート, 設定, 言語, ...",
|
||||
"toolbarHints": "ツールを選んで描き始めよう!",
|
||||
"helpHints": "ショートカットとヘルプ"
|
||||
"app": {
|
||||
"center_heading": "すべてのデータはブラウザにローカル保存されます。",
|
||||
"center_heading_plus": "代わりにExcalidraw+を開きますか?",
|
||||
"menuHint": "エクスポート、設定、言語..."
|
||||
},
|
||||
"defaults": {
|
||||
"menuHint": "エクスポート、設定、その他...",
|
||||
"center_heading": "ダイアグラムを簡単に。",
|
||||
"toolbarHint": "ツールを選んで描き始めよう!",
|
||||
"helpHint": "ショートカットとヘルプ"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -72,7 +72,7 @@
|
||||
"layers": "Tissiyin",
|
||||
"actions": "Tigawin",
|
||||
"language": "Tutlayt",
|
||||
"liveCollaboration": "",
|
||||
"liveCollaboration": "Amɛiwen s srid...",
|
||||
"duplicateSelection": "Sisleg",
|
||||
"untitled": "War azwel",
|
||||
"name": "Isem",
|
||||
@ -314,8 +314,8 @@
|
||||
"zoomToFit": "Simɣur akken ad twliḍ akk iferdisen",
|
||||
"zoomToSelection": "Simɣur ɣer tefrayt",
|
||||
"toggleElementLock": "Sekkeṛ/kkes asekker i tefrayt",
|
||||
"movePageUpDown": "",
|
||||
"movePageLeftRight": ""
|
||||
"movePageUpDown": "Smutti asebter d asawen/akessar",
|
||||
"movePageLeftRight": "Smutti asebter s azelmaḍ/ayfus"
|
||||
},
|
||||
"clearCanvasDialog": {
|
||||
"title": "Sfeḍ taɣzut n usuneɣ"
|
||||
@ -447,10 +447,16 @@
|
||||
"d9480f": "Aččinawi 9"
|
||||
},
|
||||
"welcomeScreen": {
|
||||
"data": "Akk isefka-inek•inem ttwakelsen s wudem adigan deg yiminig-inek•inem.",
|
||||
"switchToPlusApp": "Tebɣiḍ ad tedduḍ ɣer Excalidraw+ deg umḍiq?",
|
||||
"menuHints": "Asifeḍ, ismenyifen, tutlayin, ...",
|
||||
"toolbarHints": "Fren afecku tebduḍ asuneɣ!",
|
||||
"helpHints": "Inegzumen akked tallelt"
|
||||
"app": {
|
||||
"center_heading": "",
|
||||
"center_heading_plus": "",
|
||||
"menuHint": ""
|
||||
},
|
||||
"defaults": {
|
||||
"menuHint": "",
|
||||
"center_heading": "",
|
||||
"toolbarHint": "",
|
||||
"helpHint": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -447,10 +447,16 @@
|
||||
"d9480f": ""
|
||||
},
|
||||
"welcomeScreen": {
|
||||
"data": "",
|
||||
"switchToPlusApp": "",
|
||||
"menuHints": "",
|
||||
"toolbarHints": "",
|
||||
"helpHints": ""
|
||||
"app": {
|
||||
"center_heading": "",
|
||||
"center_heading_plus": "",
|
||||
"menuHint": ""
|
||||
},
|
||||
"defaults": {
|
||||
"menuHint": "",
|
||||
"center_heading": "",
|
||||
"toolbarHint": "",
|
||||
"helpHint": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"labels": {
|
||||
"paste": "붙여넣기",
|
||||
"pasteAsPlaintext": "",
|
||||
"pasteAsPlaintext": "일반 텍스트로 붙여넣기",
|
||||
"pasteCharts": "차트 붙여넣기",
|
||||
"selectAll": "전체 선택",
|
||||
"multiSelect": "선택 영역에 추가하기",
|
||||
@ -72,7 +72,7 @@
|
||||
"layers": "레이어",
|
||||
"actions": "동작",
|
||||
"language": "언어",
|
||||
"liveCollaboration": "",
|
||||
"liveCollaboration": "실시간 협업...",
|
||||
"duplicateSelection": "복제",
|
||||
"untitled": "제목 없음",
|
||||
"name": "이름",
|
||||
@ -136,8 +136,8 @@
|
||||
"buttons": {
|
||||
"clearReset": "캔버스 초기화",
|
||||
"exportJSON": "파일로 내보내기",
|
||||
"exportImage": "",
|
||||
"export": "",
|
||||
"exportImage": "이미지 내보내기",
|
||||
"export": "다른 이름으로 저장...",
|
||||
"exportToPng": "PNG로 내보내기",
|
||||
"exportToSvg": "SVG로 내보내기",
|
||||
"copyToClipboard": "클립보드로 복사",
|
||||
@ -145,7 +145,7 @@
|
||||
"scale": "크기",
|
||||
"save": "현재 파일에 저장",
|
||||
"saveAs": "다른 이름으로 저장",
|
||||
"load": "",
|
||||
"load": "열기",
|
||||
"getShareableLink": "공유 가능한 링크 생성",
|
||||
"close": "닫기",
|
||||
"selectLanguage": "언어 선택",
|
||||
@ -202,8 +202,8 @@
|
||||
"invalidSVGString": "유효하지 않은 SVG입니다.",
|
||||
"cannotResolveCollabServer": "협업 서버에 접속하는데 실패했습니다. 페이지를 새로고침하고 다시 시도해보세요.",
|
||||
"importLibraryError": "라이브러리를 불러오지 못했습니다.",
|
||||
"collabSaveFailed": "",
|
||||
"collabSaveFailed_sizeExceeded": ""
|
||||
"collabSaveFailed": "데이터베이스에 저장하지 못했습니다. 문제가 계속 된다면, 작업 내용을 잃지 않도록 로컬 저장소에 저장해 주세요.",
|
||||
"collabSaveFailed_sizeExceeded": "데이터베이스에 저장하지 못했습니다. 캔버스가 너무 큰 거 같습니다. 문제가 계속 된다면, 작업 내용을 잃지 않도록 로컬 저장소에 저장해 주세요."
|
||||
},
|
||||
"toolBar": {
|
||||
"selection": "선택",
|
||||
@ -217,7 +217,7 @@
|
||||
"text": "텍스트",
|
||||
"library": "라이브러리",
|
||||
"lock": "선택된 도구 유지하기",
|
||||
"penMode": "",
|
||||
"penMode": "펜 모드 - 터치 방지",
|
||||
"link": "선택한 도형에 대해서 링크를 추가/업데이트",
|
||||
"eraser": "지우개"
|
||||
},
|
||||
@ -238,7 +238,7 @@
|
||||
"resize": "SHIFT 키를 누르면서 조정하면 크기의 비율이 제한됩니다.\nALT를 누르면서 조정하면 중앙을 기준으로 크기를 조정합니다.",
|
||||
"resizeImage": "SHIFT를 눌러서 자유롭게 크기를 변경하거나,\nALT를 눌러서 중앙을 고정하고 크기를 변경하기",
|
||||
"rotate": "SHIFT 키를 누르면서 회전하면 각도를 제한할 수 있습니다.",
|
||||
"lineEditor_info": "",
|
||||
"lineEditor_info": "포인트를 편집하려면 Ctrl/Cmd을 누르고 더블 클릭을 하거나 Ctrl/Cmd + Enter를 누르세요",
|
||||
"lineEditor_pointSelected": "Delete 키로 꼭짓점을 제거하거나,\nCtrlOrCmd+D 로 복제하거나, 드래그 해서 이동시키기",
|
||||
"lineEditor_nothingSelected": "꼭짓점을 선택해서 수정하거나 (SHIFT를 눌러서 여러개 선택),\nAlt를 누르고 클릭해서 새로운 꼭짓점 추가하기",
|
||||
"placeImage": "클릭해서 이미지를 배치하거나, 클릭하고 드래그해서 사이즈를 조정하기",
|
||||
@ -314,8 +314,8 @@
|
||||
"zoomToFit": "모든 요소가 보이도록 확대/축소",
|
||||
"zoomToSelection": "선택 영역으로 확대/축소",
|
||||
"toggleElementLock": "선택한 항목을 잠금/잠금 해제",
|
||||
"movePageUpDown": "",
|
||||
"movePageLeftRight": ""
|
||||
"movePageUpDown": "페이지 움직이기 위/아래",
|
||||
"movePageLeftRight": "페이지 움직이기 좌/우"
|
||||
},
|
||||
"clearCanvasDialog": {
|
||||
"title": "캔버스 지우기"
|
||||
@ -397,7 +397,7 @@
|
||||
"fileSavedToFilename": "{filename} 로 저장되었습니다",
|
||||
"canvas": "캔버스",
|
||||
"selection": "선택한 요소",
|
||||
"pasteAsSingleElement": ""
|
||||
"pasteAsSingleElement": "단일 요소로 붙여넣거나, 기존 텍스트 에디터에 붙여넣으려면 {{shortcut}} 을 사용하세요."
|
||||
},
|
||||
"colors": {
|
||||
"ffffff": "화이트",
|
||||
@ -447,10 +447,16 @@
|
||||
"d9480f": "주황색 9"
|
||||
},
|
||||
"welcomeScreen": {
|
||||
"data": "",
|
||||
"switchToPlusApp": "",
|
||||
"menuHints": "",
|
||||
"toolbarHints": "",
|
||||
"helpHints": ""
|
||||
"app": {
|
||||
"center_heading": "당신의 모든 데이터는 브라우저에 저장되었습니다.",
|
||||
"center_heading_plus": "대신 Excalidraw+로 이동하시겠습니까?",
|
||||
"menuHint": "내보내기, 설정, 언어, ..."
|
||||
},
|
||||
"defaults": {
|
||||
"menuHint": "내보내기, 설정, 더 보기...",
|
||||
"center_heading": "",
|
||||
"toolbarHint": "도구 선택 & 그리기 시작",
|
||||
"helpHint": "단축키 & 도움말"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -447,10 +447,16 @@
|
||||
"d9480f": "پرتەقاڵی 9"
|
||||
},
|
||||
"welcomeScreen": {
|
||||
"data": "هەموو داتاکانت لە ناو بڕاوزەرەکەتدا پاشەکەوت کراوە.",
|
||||
"switchToPlusApp": "دەتویست بچیت بۆ Excalidraw+؟",
|
||||
"menuHints": "هەناردەکردن، پەسندکردنەکان، زمانەکان، ...",
|
||||
"toolbarHints": "ئامرازێک هەڵبژێرە و دەستبکە بە وێنەکێشان!",
|
||||
"helpHints": "قەدبڕەکان و یارمەتی"
|
||||
"app": {
|
||||
"center_heading": "",
|
||||
"center_heading_plus": "",
|
||||
"menuHint": ""
|
||||
},
|
||||
"defaults": {
|
||||
"menuHint": "",
|
||||
"center_heading": "",
|
||||
"toolbarHint": "",
|
||||
"helpHint": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -447,10 +447,16 @@
|
||||
"d9480f": "Oranžinė 9"
|
||||
},
|
||||
"welcomeScreen": {
|
||||
"data": "Visi tavo duomenys išsaugoti lokaliai naršyklėje.",
|
||||
"switchToPlusApp": "Ar vietoj to norėjai patekti į Excalidraw+?",
|
||||
"menuHints": "Eksportavimas, parinktys, kalbos, ...",
|
||||
"toolbarHints": "Pasirink įrankį ir Pradėk piešti!",
|
||||
"helpHints": "Spartieji klavišai ir pagalba"
|
||||
"app": {
|
||||
"center_heading": "",
|
||||
"center_heading_plus": "",
|
||||
"menuHint": ""
|
||||
},
|
||||
"defaults": {
|
||||
"menuHint": "",
|
||||
"center_heading": "",
|
||||
"toolbarHint": "",
|
||||
"helpHint": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -447,10 +447,16 @@
|
||||
"d9480f": "Oranžs 9"
|
||||
},
|
||||
"welcomeScreen": {
|
||||
"data": "Visi jūsu dati tiek glabāti uz vietas jūsu pārlūkā.",
|
||||
"switchToPlusApp": "",
|
||||
"menuHints": "Eksportēšana, iestatījumi, valodas...",
|
||||
"toolbarHints": "Izvēlieties rīku un sāciet zīmēt!",
|
||||
"helpHints": "Saīsnes un palīdzība"
|
||||
"app": {
|
||||
"center_heading": "",
|
||||
"center_heading_plus": "",
|
||||
"menuHint": ""
|
||||
},
|
||||
"defaults": {
|
||||
"menuHint": "",
|
||||
"center_heading": "",
|
||||
"toolbarHint": "",
|
||||
"helpHint": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -447,10 +447,16 @@
|
||||
"d9480f": "नारंगी 9"
|
||||
},
|
||||
"welcomeScreen": {
|
||||
"data": "तुमचा सर्व डेटा ब्राउज़र मधे स्थानिक जागेत सुरक्षित झाला.",
|
||||
"switchToPlusApp": "त्याएवजी तुम्हाला Excalidraw+ पर्याय हवा आहे का?",
|
||||
"menuHints": "निर्यात, पसंती, भाषा, ...",
|
||||
"toolbarHints": "साधन निवडा आणि चित्रीकरण सुरु करा!",
|
||||
"helpHints": "शॉर्टकट & सहाय"
|
||||
"app": {
|
||||
"center_heading": "",
|
||||
"center_heading_plus": "",
|
||||
"menuHint": ""
|
||||
},
|
||||
"defaults": {
|
||||
"menuHint": "",
|
||||
"center_heading": "",
|
||||
"toolbarHint": "",
|
||||
"helpHint": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -447,10 +447,16 @@
|
||||
"d9480f": ""
|
||||
},
|
||||
"welcomeScreen": {
|
||||
"data": "",
|
||||
"switchToPlusApp": "",
|
||||
"menuHints": "",
|
||||
"toolbarHints": "",
|
||||
"helpHints": ""
|
||||
"app": {
|
||||
"center_heading": "",
|
||||
"center_heading_plus": "",
|
||||
"menuHint": ""
|
||||
},
|
||||
"defaults": {
|
||||
"menuHint": "",
|
||||
"center_heading": "",
|
||||
"toolbarHint": "",
|
||||
"helpHint": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -447,10 +447,16 @@
|
||||
"d9480f": "Oransje 9"
|
||||
},
|
||||
"welcomeScreen": {
|
||||
"data": "Alle dine data lagres lokalt i din nettleser.",
|
||||
"switchToPlusApp": "Ønsker du å gå til Excalidraw+ i stedet?",
|
||||
"menuHints": "Eksporter, innstillinger, språk, ...",
|
||||
"toolbarHints": "Velg et verktøy og start å tegne!",
|
||||
"helpHints": "Snarveier & hjelp"
|
||||
"app": {
|
||||
"center_heading": "Alle dine data lagres lokalt i din nettleser.",
|
||||
"center_heading_plus": "Ønsker du å gå til Excalidraw+ i stedet?",
|
||||
"menuHint": "Eksporter, innstillinger, språk, ..."
|
||||
},
|
||||
"defaults": {
|
||||
"menuHint": "Eksporter, innstillinger og mer...",
|
||||
"center_heading": "Diagrammer. Gjort. Enkelt.",
|
||||
"toolbarHint": "Velg et verktøy og start å tegne!",
|
||||
"helpHint": "Snarveier & hjelp"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -343,7 +343,7 @@
|
||||
},
|
||||
"noteDescription": {
|
||||
"pre": "",
|
||||
"link": "",
|
||||
"link": "openbare repository",
|
||||
"post": ""
|
||||
},
|
||||
"noteGuidelines": {
|
||||
@ -447,10 +447,16 @@
|
||||
"d9480f": "Oranje 9"
|
||||
},
|
||||
"welcomeScreen": {
|
||||
"data": "",
|
||||
"switchToPlusApp": "",
|
||||
"menuHints": "",
|
||||
"toolbarHints": "",
|
||||
"helpHints": ""
|
||||
"app": {
|
||||
"center_heading": "",
|
||||
"center_heading_plus": "",
|
||||
"menuHint": ""
|
||||
},
|
||||
"defaults": {
|
||||
"menuHint": "",
|
||||
"center_heading": "",
|
||||
"toolbarHint": "",
|
||||
"helpHint": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -447,10 +447,16 @@
|
||||
"d9480f": "Oransj 9"
|
||||
},
|
||||
"welcomeScreen": {
|
||||
"data": "",
|
||||
"switchToPlusApp": "",
|
||||
"menuHints": "",
|
||||
"toolbarHints": "",
|
||||
"helpHints": ""
|
||||
"app": {
|
||||
"center_heading": "",
|
||||
"center_heading_plus": "",
|
||||
"menuHint": ""
|
||||
},
|
||||
"defaults": {
|
||||
"menuHint": "",
|
||||
"center_heading": "",
|
||||
"toolbarHint": "",
|
||||
"helpHint": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -447,10 +447,16 @@
|
||||
"d9480f": "Irange 9"
|
||||
},
|
||||
"welcomeScreen": {
|
||||
"data": "Totas las donadas son enregistradas dins vòstre navegador.",
|
||||
"switchToPlusApp": "Volètz puslèu utilizar Excalidraw+ ?",
|
||||
"menuHints": "Exportar, preferéncias, lengas, ...",
|
||||
"toolbarHints": "Prenètz un esplech e començatz de dessenhar !",
|
||||
"helpHints": "Acorchis e ajuda"
|
||||
"app": {
|
||||
"center_heading": "Totas las donadas son enregistradas dins vòstre navegador.",
|
||||
"center_heading_plus": "Voliatz puslèu utilizar Excalidraw+ a la plaça ?",
|
||||
"menuHint": "Exportar, preferéncias, lengas, ..."
|
||||
},
|
||||
"defaults": {
|
||||
"menuHint": "Exportar, preferéncias, e mai...",
|
||||
"center_heading": "Diagram. Tot. Simplament.",
|
||||
"toolbarHint": "Prenètz un esplech e començatz de dessenhar !",
|
||||
"helpHint": "Acorchis e ajuda"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -447,10 +447,16 @@
|
||||
"d9480f": "ਸੰਤਰੀ 9"
|
||||
},
|
||||
"welcomeScreen": {
|
||||
"data": "",
|
||||
"switchToPlusApp": "",
|
||||
"menuHints": "",
|
||||
"toolbarHints": "",
|
||||
"helpHints": ""
|
||||
"app": {
|
||||
"center_heading": "",
|
||||
"center_heading_plus": "",
|
||||
"menuHint": ""
|
||||
},
|
||||
"defaults": {
|
||||
"menuHint": "",
|
||||
"center_heading": "",
|
||||
"toolbarHint": "",
|
||||
"helpHint": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,50 +1,50 @@
|
||||
{
|
||||
"ar-SA": 86,
|
||||
"ar-SA": 87,
|
||||
"bg-BG": 55,
|
||||
"bn-BD": 60,
|
||||
"ca-ES": 94,
|
||||
"cs-CZ": 75,
|
||||
"da-DK": 33,
|
||||
"de-DE": 100,
|
||||
"el-GR": 95,
|
||||
"el-GR": 100,
|
||||
"en": 100,
|
||||
"es-ES": 100,
|
||||
"eu-ES": 98,
|
||||
"fa-IR": 98,
|
||||
"eu-ES": 100,
|
||||
"fa-IR": 96,
|
||||
"fi-FI": 93,
|
||||
"fr-FR": 100,
|
||||
"gl-ES": 99,
|
||||
"he-IL": 91,
|
||||
"hi-IN": 71,
|
||||
"hu-HU": 90,
|
||||
"id-ID": 99,
|
||||
"it-IT": 99,
|
||||
"gl-ES": 100,
|
||||
"he-IL": 90,
|
||||
"hi-IN": 69,
|
||||
"hu-HU": 89,
|
||||
"id-ID": 100,
|
||||
"it-IT": 100,
|
||||
"ja-JP": 100,
|
||||
"kab-KAB": 94,
|
||||
"kab-KAB": 93,
|
||||
"kk-KZ": 21,
|
||||
"ko-KR": 95,
|
||||
"ku-TR": 98,
|
||||
"lt-LT": 66,
|
||||
"lv-LV": 99,
|
||||
"mr-IN": 100,
|
||||
"my-MM": 42,
|
||||
"ko-KR": 99,
|
||||
"ku-TR": 96,
|
||||
"lt-LT": 64,
|
||||
"lv-LV": 98,
|
||||
"mr-IN": 98,
|
||||
"my-MM": 41,
|
||||
"nb-NO": 100,
|
||||
"nl-NL": 91,
|
||||
"nn-NO": 91,
|
||||
"nn-NO": 90,
|
||||
"oc-FR": 98,
|
||||
"pa-IN": 83,
|
||||
"pl-PL": 85,
|
||||
"pt-BR": 100,
|
||||
"pt-PT": 98,
|
||||
"pt-BR": 98,
|
||||
"pt-PT": 100,
|
||||
"ro-RO": 100,
|
||||
"ru-RU": 100,
|
||||
"ru-RU": 98,
|
||||
"si-LK": 8,
|
||||
"sk-SK": 99,
|
||||
"sk-SK": 100,
|
||||
"sl-SI": 100,
|
||||
"sv-SE": 98,
|
||||
"sv-SE": 96,
|
||||
"ta-IN": 93,
|
||||
"tr-TR": 98,
|
||||
"uk-UA": 97,
|
||||
"uk-UA": 96,
|
||||
"vi-VN": 20,
|
||||
"zh-CN": 100,
|
||||
"zh-HK": 26,
|
||||
|
@ -447,10 +447,16 @@
|
||||
"d9480f": "Pomarańczowy 9"
|
||||
},
|
||||
"welcomeScreen": {
|
||||
"data": "",
|
||||
"switchToPlusApp": "",
|
||||
"menuHints": "",
|
||||
"toolbarHints": "",
|
||||
"helpHints": ""
|
||||
"app": {
|
||||
"center_heading": "",
|
||||
"center_heading_plus": "",
|
||||
"menuHint": ""
|
||||
},
|
||||
"defaults": {
|
||||
"menuHint": "",
|
||||
"center_heading": "",
|
||||
"toolbarHint": "",
|
||||
"helpHint": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -447,10 +447,16 @@
|
||||
"d9480f": "Laranja 9"
|
||||
},
|
||||
"welcomeScreen": {
|
||||
"data": "Todos os dados são salvos localmente no seu navegador.",
|
||||
"switchToPlusApp": "Você queria ir para o Excalidraw+ em vez disso?",
|
||||
"menuHints": "Exportar, preferências, idiomas, ...",
|
||||
"toolbarHints": "Escolha uma ferramenta & Comece a desenhar!",
|
||||
"helpHints": "Atalhos & ajuda"
|
||||
"app": {
|
||||
"center_heading": "",
|
||||
"center_heading_plus": "",
|
||||
"menuHint": ""
|
||||
},
|
||||
"defaults": {
|
||||
"menuHint": "",
|
||||
"center_heading": "",
|
||||
"toolbarHint": "",
|
||||
"helpHint": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"labels": {
|
||||
"paste": "Colar",
|
||||
"pasteAsPlaintext": "",
|
||||
"pasteAsPlaintext": "Colar como texto simples",
|
||||
"pasteCharts": "Colar gráficos",
|
||||
"selectAll": "Selecionar tudo",
|
||||
"multiSelect": "Adicionar elemento à seleção",
|
||||
@ -202,8 +202,8 @@
|
||||
"invalidSVGString": "SVG inválido.",
|
||||
"cannotResolveCollabServer": "Não foi possível fazer a ligação ao servidor colaborativo. Por favor, volte a carregar a página e tente novamente.",
|
||||
"importLibraryError": "Não foi possível carregar a biblioteca",
|
||||
"collabSaveFailed": "",
|
||||
"collabSaveFailed_sizeExceeded": ""
|
||||
"collabSaveFailed": "Não foi possível guardar na base de dados de backend. Se os problemas persistirem, guarde o ficheiro localmente para garantir que não perde o seu trabalho.",
|
||||
"collabSaveFailed_sizeExceeded": "Não foi possível guardar na base de dados de backend, o ecrã parece estar muito grande. Deve guardar o ficheiro localmente para garantir que não perde o seu trabalho."
|
||||
},
|
||||
"toolBar": {
|
||||
"selection": "Seleção",
|
||||
@ -238,7 +238,7 @@
|
||||
"resize": "Pode restringir as proporções mantendo a tecla SHIFT premida enquanto redimensiona,\nmantenha a tecla ALT premida para redimensionar a partir do centro",
|
||||
"resizeImage": "Pode redimensionar livremente mantendo pressionada a tecla SHIFT,\nmantenha pressionada a tecla ALT para redimensionar do centro",
|
||||
"rotate": "Pode restringir os ângulos mantendo a tecla SHIFT premida enquanto roda",
|
||||
"lineEditor_info": "",
|
||||
"lineEditor_info": "Pressione CtrlOrCmd e faça um duplo-clique ou pressione CtrlOrCmd + Enter para editar pontos",
|
||||
"lineEditor_pointSelected": "Carregue na tecla Delete para remover o(s) ponto(s), CtrlOuCmd+D para duplicar, ou arraste para mover",
|
||||
"lineEditor_nothingSelected": "Seleccione um ponto para editar (carregue em SHIFT para seleccionar vários),\nou carregue em Alt e clique para acrescentar novos pontos",
|
||||
"placeImage": "Clique para colocar a imagem ou clique e arraste para definir o seu tamanho manualmente",
|
||||
@ -314,8 +314,8 @@
|
||||
"zoomToFit": "Ajustar para todos os elementos caberem",
|
||||
"zoomToSelection": "Ampliar a seleção",
|
||||
"toggleElementLock": "Trancar/destrancar selecção",
|
||||
"movePageUpDown": "",
|
||||
"movePageLeftRight": ""
|
||||
"movePageUpDown": "Mover página para cima / baixo",
|
||||
"movePageLeftRight": "Mover página para esquerda / direita"
|
||||
},
|
||||
"clearCanvasDialog": {
|
||||
"title": "Apagar tela"
|
||||
@ -397,7 +397,7 @@
|
||||
"fileSavedToFilename": "Guardado como {filename}",
|
||||
"canvas": "área de desenho",
|
||||
"selection": "seleção",
|
||||
"pasteAsSingleElement": ""
|
||||
"pasteAsSingleElement": "Usar {{shortcut}} para colar como um único elemento,\nou colar num editor de texto existente"
|
||||
},
|
||||
"colors": {
|
||||
"ffffff": "Branco",
|
||||
@ -447,10 +447,16 @@
|
||||
"d9480f": "Laranja 9"
|
||||
},
|
||||
"welcomeScreen": {
|
||||
"data": "Todos os dados estão guardados no seu navegador local.",
|
||||
"switchToPlusApp": "Queria antes ir para o Excalidraw+?",
|
||||
"menuHints": "Exportar, preferências, idiomas...",
|
||||
"toolbarHints": "Escolha uma ferramenta e comece a desenhar!",
|
||||
"helpHints": "Atalhos e ajuda"
|
||||
"app": {
|
||||
"center_heading": "Todos os dados são guardados no seu navegador local.",
|
||||
"center_heading_plus": "Queria antes ir para o Excalidraw+?",
|
||||
"menuHint": "Exportar, preferências, idiomas..."
|
||||
},
|
||||
"defaults": {
|
||||
"menuHint": "Exportar, preferências e outros...",
|
||||
"center_heading": "Diagramas. Feito. Simples.",
|
||||
"toolbarHint": "Escolha uma ferramenta e comece a desenhar!",
|
||||
"helpHint": "Atalhos e ajuda"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -447,10 +447,16 @@
|
||||
"d9480f": "Portocaliu 9"
|
||||
},
|
||||
"welcomeScreen": {
|
||||
"data": "Toate datele tale sunt salvate local în navigatorul tău.",
|
||||
"switchToPlusApp": "Ai vrut să mergi în schimb la Excalidraw+?",
|
||||
"menuHints": "Exportare, preferințe, limbi, ...",
|
||||
"toolbarHints": "Alege un instrument și începe să desenezi!",
|
||||
"helpHints": "Comenzi rapide și ajutor"
|
||||
"app": {
|
||||
"center_heading": "Toate datele tale sunt salvate local în navigatorul tău.",
|
||||
"center_heading_plus": "Ai vrut să mergi în schimb la Excalidraw+?",
|
||||
"menuHint": "Exportare, preferințe, limbi, ..."
|
||||
},
|
||||
"defaults": {
|
||||
"menuHint": "Exportare, preferințe și mai multe...",
|
||||
"center_heading": "Diagrame. Făcute. Simple.",
|
||||
"toolbarHint": "Alege un instrument și începe să desenezi!",
|
||||
"helpHint": "Comenzi rapide și ajutor"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -447,10 +447,16 @@
|
||||
"d9480f": "Оранжевый 9"
|
||||
},
|
||||
"welcomeScreen": {
|
||||
"data": "Все ваши данные сохраняются локально в вашем браузере.",
|
||||
"switchToPlusApp": "Хотите перейти на Excalidraw+?",
|
||||
"menuHints": "Экспорт, настройки, языки, ...",
|
||||
"toolbarHints": "Выберите инструмент и начните рисовать!",
|
||||
"helpHints": "Сочетания клавиш и помощь"
|
||||
"app": {
|
||||
"center_heading": "",
|
||||
"center_heading_plus": "",
|
||||
"menuHint": ""
|
||||
},
|
||||
"defaults": {
|
||||
"menuHint": "",
|
||||
"center_heading": "",
|
||||
"toolbarHint": "",
|
||||
"helpHint": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -447,10 +447,16 @@
|
||||
"d9480f": ""
|
||||
},
|
||||
"welcomeScreen": {
|
||||
"data": "",
|
||||
"switchToPlusApp": "",
|
||||
"menuHints": "",
|
||||
"toolbarHints": "",
|
||||
"helpHints": ""
|
||||
"app": {
|
||||
"center_heading": "",
|
||||
"center_heading_plus": "",
|
||||
"menuHint": ""
|
||||
},
|
||||
"defaults": {
|
||||
"menuHint": "",
|
||||
"center_heading": "",
|
||||
"toolbarHint": "",
|
||||
"helpHint": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -202,8 +202,8 @@
|
||||
"invalidSVGString": "Nevalidné SVG.",
|
||||
"cannotResolveCollabServer": "Nepodarilo sa pripojiť ku kolaboračnému serveru. Prosím obnovte stránku a skúste to znovu.",
|
||||
"importLibraryError": "Nepodarilo sa načítať knižnicu",
|
||||
"collabSaveFailed": "",
|
||||
"collabSaveFailed_sizeExceeded": ""
|
||||
"collabSaveFailed": "Uloženie do databázy sa nepodarilo. Ak tento problém pretrváva uložte si váš súbor lokálne aby ste nestratili vašu prácu.",
|
||||
"collabSaveFailed_sizeExceeded": "Uloženie do databázy sa nepodarilo, pretože veľkosť plátna je príliš veľká. Uložte si váš súbor lokálne aby ste nestratili vašu prácu."
|
||||
},
|
||||
"toolBar": {
|
||||
"selection": "Výber",
|
||||
@ -447,10 +447,16 @@
|
||||
"d9480f": "Oranžová 9"
|
||||
},
|
||||
"welcomeScreen": {
|
||||
"data": "Všetky vaše dáta sú uložené lokálne vo vašom prehliadači.",
|
||||
"switchToPlusApp": "Chceli ste namiesto toho prejsť do Excalidraw+?",
|
||||
"menuHints": "Exportovanie, nastavenia, jazyky, ...",
|
||||
"toolbarHints": "Zvoľte nástroj a začnite kresliť!",
|
||||
"helpHints": "Klávesové skratky a pomocník"
|
||||
"app": {
|
||||
"center_heading": "Všetky vaše dáta sú uložené lokálne vo vašom prehliadači.",
|
||||
"center_heading_plus": "Chceli ste namiesto toho prejsť do Excalidraw+?",
|
||||
"menuHint": "Exportovanie, nastavenia, jazyky, ..."
|
||||
},
|
||||
"defaults": {
|
||||
"menuHint": "Exportovanie, nastavenia a ďalšie...",
|
||||
"center_heading": "Diagramy. Jednoducho.",
|
||||
"toolbarHint": "Zvoľte nástroj a začnite kresliť!",
|
||||
"helpHint": "Klávesové skratky a pomocník"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -447,10 +447,16 @@
|
||||
"d9480f": "Oranžna 9"
|
||||
},
|
||||
"welcomeScreen": {
|
||||
"data": "Vsi vaši podatki so shranjeni lokalno v vašem brskalniku.",
|
||||
"switchToPlusApp": "Ste namesto tega želeli odpreti Excalidraw+?",
|
||||
"menuHints": "Izvoz, nastavitve, jeziki, ...",
|
||||
"toolbarHints": "Izberi orodje in začni z risanjem!",
|
||||
"helpHints": "Bljižnice in pomoč"
|
||||
"app": {
|
||||
"center_heading": "Vsi vaši podatki so shranjeni lokalno v vašem brskalniku.",
|
||||
"center_heading_plus": "Ste namesto tega želeli odpreti Excalidraw+?",
|
||||
"menuHint": "Izvoz, nastavitve, jeziki, ..."
|
||||
},
|
||||
"defaults": {
|
||||
"menuHint": "Izvoz, nastavitve in več ...",
|
||||
"center_heading": "Diagrami. Enostavno.",
|
||||
"toolbarHint": "Izberi orodje in začni z risanjem!",
|
||||
"helpHint": "Bližnjice in pomoč"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -447,10 +447,16 @@
|
||||
"d9480f": "Orange 9"
|
||||
},
|
||||
"welcomeScreen": {
|
||||
"data": "All din data sparas lokalt i din webbläsare.",
|
||||
"switchToPlusApp": "Ville du gå till Excalidraw+ istället?",
|
||||
"menuHints": "Export, inställningar, språk, ...",
|
||||
"toolbarHints": "Välj ett verktyg och börja rita!",
|
||||
"helpHints": "Genvägar och hjälp"
|
||||
"app": {
|
||||
"center_heading": "",
|
||||
"center_heading_plus": "",
|
||||
"menuHint": ""
|
||||
},
|
||||
"defaults": {
|
||||
"menuHint": "",
|
||||
"center_heading": "",
|
||||
"toolbarHint": "",
|
||||
"helpHint": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -447,10 +447,16 @@
|
||||
"d9480f": "ஆரஞ்சு 9"
|
||||
},
|
||||
"welcomeScreen": {
|
||||
"data": "",
|
||||
"switchToPlusApp": "",
|
||||
"menuHints": "",
|
||||
"toolbarHints": "",
|
||||
"helpHints": ""
|
||||
"app": {
|
||||
"center_heading": "",
|
||||
"center_heading_plus": "",
|
||||
"menuHint": ""
|
||||
},
|
||||
"defaults": {
|
||||
"menuHint": "",
|
||||
"center_heading": "",
|
||||
"toolbarHint": "",
|
||||
"helpHint": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -202,8 +202,8 @@
|
||||
"invalidSVGString": "Geçersiz SVG.",
|
||||
"cannotResolveCollabServer": "İş birliği sunucusuna bağlanılamıyor. Lütfen sayfayı yenileyip tekrar deneyin.",
|
||||
"importLibraryError": "Kütüphane yüklenemedi",
|
||||
"collabSaveFailed": "",
|
||||
"collabSaveFailed_sizeExceeded": ""
|
||||
"collabSaveFailed": "Backend veritabanına kaydedilemedi. Eğer problem devam ederse, çalışmanızı korumak için dosyayı yerel olarak kaydetmelisiniz.",
|
||||
"collabSaveFailed_sizeExceeded": "Backend veritabanına kaydedilemedi; tuval çok büyük. Çalışmanızı korumak için dosyayı yerel olarak kaydetmelisiniz."
|
||||
},
|
||||
"toolBar": {
|
||||
"selection": "Seçme",
|
||||
@ -314,8 +314,8 @@
|
||||
"zoomToFit": "Tüm öğeleri sığdırmak için yakınlaştır",
|
||||
"zoomToSelection": "Seçime yakınlaş",
|
||||
"toggleElementLock": "Seçimi Kilitle/çöz",
|
||||
"movePageUpDown": "",
|
||||
"movePageLeftRight": ""
|
||||
"movePageUpDown": "Sayfayı yukarı/aşağı kaydır",
|
||||
"movePageLeftRight": "Sayfayı sola/sağa kaydır"
|
||||
},
|
||||
"clearCanvasDialog": {
|
||||
"title": "Tuvali temizle"
|
||||
@ -397,7 +397,7 @@
|
||||
"fileSavedToFilename": "{filename} kaydedildi",
|
||||
"canvas": "tuval",
|
||||
"selection": "seçim",
|
||||
"pasteAsSingleElement": ""
|
||||
"pasteAsSingleElement": "Tekil obje olarak yapıştırmak için veya var olan bir metin editörüne yapıştırmak için {{shortcut}} kullanın"
|
||||
},
|
||||
"colors": {
|
||||
"ffffff": "Beyaz",
|
||||
@ -447,10 +447,16 @@
|
||||
"d9480f": "Turuncu 9"
|
||||
},
|
||||
"welcomeScreen": {
|
||||
"data": "Tüm veri internet gezgininize yerel olarak kaydedildi.",
|
||||
"switchToPlusApp": "Excalidraw+ kullanmak ister miydiniz?",
|
||||
"menuHints": "Dışa aktar, seçenkeler, diller, ...",
|
||||
"toolbarHints": "Bir araç seçin & Çizime başlayın!",
|
||||
"helpHints": "Kısayollar & yardım"
|
||||
"app": {
|
||||
"center_heading": "",
|
||||
"center_heading_plus": "",
|
||||
"menuHint": ""
|
||||
},
|
||||
"defaults": {
|
||||
"menuHint": "",
|
||||
"center_heading": "",
|
||||
"toolbarHint": "",
|
||||
"helpHint": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -447,10 +447,16 @@
|
||||
"d9480f": "Помаранчевий 9"
|
||||
},
|
||||
"welcomeScreen": {
|
||||
"data": "",
|
||||
"switchToPlusApp": "",
|
||||
"menuHints": "",
|
||||
"toolbarHints": "Оберіть інструмент і почніть малювати!",
|
||||
"helpHints": "Комбінації клавіш і допомога"
|
||||
"app": {
|
||||
"center_heading": "",
|
||||
"center_heading_plus": "",
|
||||
"menuHint": ""
|
||||
},
|
||||
"defaults": {
|
||||
"menuHint": "",
|
||||
"center_heading": "",
|
||||
"toolbarHint": "",
|
||||
"helpHint": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -447,10 +447,16 @@
|
||||
"d9480f": ""
|
||||
},
|
||||
"welcomeScreen": {
|
||||
"data": "",
|
||||
"switchToPlusApp": "",
|
||||
"menuHints": "",
|
||||
"toolbarHints": "",
|
||||
"helpHints": ""
|
||||
"app": {
|
||||
"center_heading": "",
|
||||
"center_heading_plus": "",
|
||||
"menuHint": ""
|
||||
},
|
||||
"defaults": {
|
||||
"menuHint": "",
|
||||
"center_heading": "",
|
||||
"toolbarHint": "",
|
||||
"helpHint": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -447,10 +447,16 @@
|
||||
"d9480f": "橙 9"
|
||||
},
|
||||
"welcomeScreen": {
|
||||
"data": "您的所有数据都储存在浏览器本地。",
|
||||
"switchToPlusApp": "是否前往 Excalidraw+ ?",
|
||||
"menuHints": "导出、首选项、语言...",
|
||||
"toolbarHints": "选择工具并开始绘图!",
|
||||
"helpHints": "快捷键和帮助"
|
||||
"app": {
|
||||
"center_heading": "您的所有数据都储存在浏览器本地。",
|
||||
"center_heading_plus": "是否前往 Excalidraw+ ?",
|
||||
"menuHint": "导出、首选项、语言……"
|
||||
},
|
||||
"defaults": {
|
||||
"menuHint": "导出、首选项……",
|
||||
"center_heading": "图,化繁为简。",
|
||||
"toolbarHint": "选择工具并开始绘图!",
|
||||
"helpHint": "快捷键和帮助"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -447,10 +447,16 @@
|
||||
"d9480f": ""
|
||||
},
|
||||
"welcomeScreen": {
|
||||
"data": "",
|
||||
"switchToPlusApp": "",
|
||||
"menuHints": "",
|
||||
"toolbarHints": "",
|
||||
"helpHints": ""
|
||||
"app": {
|
||||
"center_heading": "",
|
||||
"center_heading_plus": "",
|
||||
"menuHint": ""
|
||||
},
|
||||
"defaults": {
|
||||
"menuHint": "",
|
||||
"center_heading": "",
|
||||
"toolbarHint": "",
|
||||
"helpHint": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -447,10 +447,16 @@
|
||||
"d9480f": "橘 9"
|
||||
},
|
||||
"welcomeScreen": {
|
||||
"data": "您的所有資料都儲存在本機瀏覽器。",
|
||||
"switchToPlusApp": "您是否是要前往 Excalidraw+ ?",
|
||||
"menuHints": "輸出、偏好設定、語言...",
|
||||
"toolbarHints": "選個工具開始畫圖吧!",
|
||||
"helpHints": "快速鍵與說明"
|
||||
"app": {
|
||||
"center_heading": "所有資料皆已在瀏覽器中儲存於本機",
|
||||
"center_heading_plus": "您是否是要前往 Excalidraw+ ?",
|
||||
"menuHint": "輸出、偏好設定、語言..."
|
||||
},
|
||||
"defaults": {
|
||||
"menuHint": "輸出、偏好設定及其他...",
|
||||
"center_heading": "圖表。製作。超簡單。",
|
||||
"toolbarHint": "選個工具開始畫圖吧!",
|
||||
"helpHint": "快速鍵與說明"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,12 @@ The change should be grouped under one of the below section and must contain PR
|
||||
Please add the latest change on the top under the correct section.
|
||||
-->
|
||||
|
||||
## Unreleased
|
||||
|
||||
### Features
|
||||
|
||||
- `MainMenu`, `MainMenu.Item`, and `MainMenu.ItemLink` components now all support `onSelect(event: Event): void` callback. If you call `event.preventDefault()`, it will prevent the menu from closing when an item is selected (clicked on). [#6152](https://github.com/excalidraw/excalidraw/pull/6152)
|
||||
|
||||
## 0.14.1 (2023-01-16)
|
||||
|
||||
### Fixes
|
||||
|
@ -4,7 +4,7 @@ exports[`contextMenu element right-clicking on a group should select whole group
|
||||
Object {
|
||||
"activeTool": Object {
|
||||
"customType": null,
|
||||
"lastActiveToolBeforeEraser": null,
|
||||
"lastActiveTool": null,
|
||||
"locked": false,
|
||||
"type": "selection",
|
||||
},
|
||||
@ -434,7 +434,7 @@ exports[`contextMenu element selecting 'Add to library' in context menu adds ele
|
||||
Object {
|
||||
"activeTool": Object {
|
||||
"customType": null,
|
||||
"lastActiveToolBeforeEraser": null,
|
||||
"lastActiveTool": null,
|
||||
"locked": false,
|
||||
"type": "selection",
|
||||
},
|
||||
@ -620,7 +620,7 @@ exports[`contextMenu element selecting 'Bring forward' in context menu brings el
|
||||
Object {
|
||||
"activeTool": Object {
|
||||
"customType": null,
|
||||
"lastActiveToolBeforeEraser": null,
|
||||
"lastActiveTool": null,
|
||||
"locked": false,
|
||||
"type": "selection",
|
||||
},
|
||||
@ -975,7 +975,7 @@ exports[`contextMenu element selecting 'Bring to front' in context menu brings e
|
||||
Object {
|
||||
"activeTool": Object {
|
||||
"customType": null,
|
||||
"lastActiveToolBeforeEraser": null,
|
||||
"lastActiveTool": null,
|
||||
"locked": false,
|
||||
"type": "selection",
|
||||
},
|
||||
@ -1330,7 +1330,7 @@ exports[`contextMenu element selecting 'Copy styles' in context menu copies styl
|
||||
Object {
|
||||
"activeTool": Object {
|
||||
"customType": null,
|
||||
"lastActiveToolBeforeEraser": null,
|
||||
"lastActiveTool": null,
|
||||
"locked": false,
|
||||
"type": "selection",
|
||||
},
|
||||
@ -1516,7 +1516,7 @@ exports[`contextMenu element selecting 'Delete' in context menu deletes element:
|
||||
Object {
|
||||
"activeTool": Object {
|
||||
"customType": null,
|
||||
"lastActiveToolBeforeEraser": null,
|
||||
"lastActiveTool": null,
|
||||
"locked": false,
|
||||
"type": "selection",
|
||||
},
|
||||
@ -1738,7 +1738,7 @@ exports[`contextMenu element selecting 'Duplicate' in context menu duplicates el
|
||||
Object {
|
||||
"activeTool": Object {
|
||||
"customType": null,
|
||||
"lastActiveToolBeforeEraser": null,
|
||||
"lastActiveTool": null,
|
||||
"locked": false,
|
||||
"type": "selection",
|
||||
},
|
||||
@ -2023,7 +2023,7 @@ exports[`contextMenu element selecting 'Group selection' in context menu groups
|
||||
Object {
|
||||
"activeTool": Object {
|
||||
"customType": null,
|
||||
"lastActiveToolBeforeEraser": null,
|
||||
"lastActiveTool": null,
|
||||
"locked": false,
|
||||
"type": "selection",
|
||||
},
|
||||
@ -2396,7 +2396,7 @@ exports[`contextMenu element selecting 'Paste styles' in context menu pastes sty
|
||||
Object {
|
||||
"activeTool": Object {
|
||||
"customType": null,
|
||||
"lastActiveToolBeforeEraser": null,
|
||||
"lastActiveTool": null,
|
||||
"locked": false,
|
||||
"type": "selection",
|
||||
},
|
||||
@ -3243,7 +3243,7 @@ exports[`contextMenu element selecting 'Send backward' in context menu sends ele
|
||||
Object {
|
||||
"activeTool": Object {
|
||||
"customType": null,
|
||||
"lastActiveToolBeforeEraser": null,
|
||||
"lastActiveTool": null,
|
||||
"locked": false,
|
||||
"type": "selection",
|
||||
},
|
||||
@ -3598,7 +3598,7 @@ exports[`contextMenu element selecting 'Send to back' in context menu sends elem
|
||||
Object {
|
||||
"activeTool": Object {
|
||||
"customType": null,
|
||||
"lastActiveToolBeforeEraser": null,
|
||||
"lastActiveTool": null,
|
||||
"locked": false,
|
||||
"type": "selection",
|
||||
},
|
||||
@ -3953,7 +3953,7 @@ exports[`contextMenu element selecting 'Ungroup selection' in context menu ungro
|
||||
Object {
|
||||
"activeTool": Object {
|
||||
"customType": null,
|
||||
"lastActiveToolBeforeEraser": null,
|
||||
"lastActiveTool": null,
|
||||
"locked": false,
|
||||
"type": "selection",
|
||||
},
|
||||
@ -4392,7 +4392,7 @@ exports[`contextMenu element shows 'Group selection' in context menu for multipl
|
||||
Object {
|
||||
"activeTool": Object {
|
||||
"customType": null,
|
||||
"lastActiveToolBeforeEraser": null,
|
||||
"lastActiveTool": null,
|
||||
"locked": false,
|
||||
"type": "selection",
|
||||
},
|
||||
@ -4933,7 +4933,7 @@ exports[`contextMenu element shows 'Ungroup selection' in context menu for group
|
||||
Object {
|
||||
"activeTool": Object {
|
||||
"customType": null,
|
||||
"lastActiveToolBeforeEraser": null,
|
||||
"lastActiveTool": null,
|
||||
"locked": false,
|
||||
"type": "selection",
|
||||
},
|
||||
@ -5559,7 +5559,7 @@ exports[`contextMenu element shows context menu for canvas: [end of test] appSta
|
||||
Object {
|
||||
"activeTool": Object {
|
||||
"customType": null,
|
||||
"lastActiveToolBeforeEraser": null,
|
||||
"lastActiveTool": null,
|
||||
"locked": false,
|
||||
"type": "selection",
|
||||
},
|
||||
@ -5773,7 +5773,7 @@ exports[`contextMenu element shows context menu for element: [end of test] appSt
|
||||
Object {
|
||||
"activeTool": Object {
|
||||
"customType": null,
|
||||
"lastActiveToolBeforeEraser": null,
|
||||
"lastActiveTool": null,
|
||||
"locked": false,
|
||||
"type": "selection",
|
||||
},
|
||||
@ -6110,7 +6110,7 @@ exports[`contextMenu element shows context menu for element: [end of test] appSt
|
||||
Object {
|
||||
"activeTool": Object {
|
||||
"customType": null,
|
||||
"lastActiveToolBeforeEraser": null,
|
||||
"lastActiveTool": null,
|
||||
"locked": false,
|
||||
"type": "selection",
|
||||
},
|
||||
|
@ -4,7 +4,7 @@ exports[`given element A and group of elements B and given both are selected whe
|
||||
Object {
|
||||
"activeTool": Object {
|
||||
"customType": null,
|
||||
"lastActiveToolBeforeEraser": null,
|
||||
"lastActiveTool": null,
|
||||
"locked": false,
|
||||
"type": "selection",
|
||||
},
|
||||
@ -540,7 +540,7 @@ exports[`given element A and group of elements B and given both are selected whe
|
||||
Object {
|
||||
"activeTool": Object {
|
||||
"customType": null,
|
||||
"lastActiveToolBeforeEraser": null,
|
||||
"lastActiveTool": null,
|
||||
"locked": false,
|
||||
"type": "selection",
|
||||
},
|
||||
@ -1082,7 +1082,7 @@ exports[`regression tests Cmd/Ctrl-click exclusively select element under pointe
|
||||
Object {
|
||||
"activeTool": Object {
|
||||
"customType": null,
|
||||
"lastActiveToolBeforeEraser": null,
|
||||
"lastActiveTool": null,
|
||||
"locked": false,
|
||||
"type": "selection",
|
||||
},
|
||||
@ -1989,7 +1989,7 @@ exports[`regression tests Drags selected element when hitting only bounding box
|
||||
Object {
|
||||
"activeTool": Object {
|
||||
"customType": null,
|
||||
"lastActiveToolBeforeEraser": null,
|
||||
"lastActiveTool": null,
|
||||
"locked": false,
|
||||
"type": "selection",
|
||||
},
|
||||
@ -2219,7 +2219,7 @@ exports[`regression tests adjusts z order when grouping: [end of test] appState
|
||||
Object {
|
||||
"activeTool": Object {
|
||||
"customType": null,
|
||||
"lastActiveToolBeforeEraser": null,
|
||||
"lastActiveTool": null,
|
||||
"locked": false,
|
||||
"type": "selection",
|
||||
},
|
||||
@ -2752,7 +2752,7 @@ exports[`regression tests alt-drag duplicates an element: [end of test] appState
|
||||
Object {
|
||||
"activeTool": Object {
|
||||
"customType": null,
|
||||
"lastActiveToolBeforeEraser": null,
|
||||
"lastActiveTool": null,
|
||||
"locked": false,
|
||||
"type": "selection",
|
||||
},
|
||||
@ -3041,7 +3041,7 @@ exports[`regression tests arrow keys: [end of test] appState 1`] = `
|
||||
Object {
|
||||
"activeTool": Object {
|
||||
"customType": null,
|
||||
"lastActiveToolBeforeEraser": null,
|
||||
"lastActiveTool": null,
|
||||
"locked": false,
|
||||
"type": "selection",
|
||||
},
|
||||
@ -3225,7 +3225,7 @@ exports[`regression tests can drag element that covers another element, while an
|
||||
Object {
|
||||
"activeTool": Object {
|
||||
"customType": null,
|
||||
"lastActiveToolBeforeEraser": null,
|
||||
"lastActiveTool": null,
|
||||
"locked": false,
|
||||
"type": "selection",
|
||||
},
|
||||
@ -3741,7 +3741,7 @@ exports[`regression tests change the properties of a shape: [end of test] appSta
|
||||
Object {
|
||||
"activeTool": Object {
|
||||
"customType": null,
|
||||
"lastActiveToolBeforeEraser": null,
|
||||
"lastActiveTool": null,
|
||||
"locked": false,
|
||||
"type": "selection",
|
||||
},
|
||||
@ -4009,7 +4009,7 @@ exports[`regression tests click on an element and drag it: [dragged] appState 1`
|
||||
Object {
|
||||
"activeTool": Object {
|
||||
"customType": null,
|
||||
"lastActiveToolBeforeEraser": null,
|
||||
"lastActiveTool": null,
|
||||
"locked": false,
|
||||
"type": "selection",
|
||||
},
|
||||
@ -4239,7 +4239,7 @@ exports[`regression tests click on an element and drag it: [end of test] appStat
|
||||
Object {
|
||||
"activeTool": Object {
|
||||
"customType": null,
|
||||
"lastActiveToolBeforeEraser": null,
|
||||
"lastActiveTool": null,
|
||||
"locked": false,
|
||||
"type": "selection",
|
||||
},
|
||||
@ -4515,7 +4515,7 @@ exports[`regression tests click to select a shape: [end of test] appState 1`] =
|
||||
Object {
|
||||
"activeTool": Object {
|
||||
"customType": null,
|
||||
"lastActiveToolBeforeEraser": null,
|
||||
"lastActiveTool": null,
|
||||
"locked": false,
|
||||
"type": "selection",
|
||||
},
|
||||
@ -4803,7 +4803,7 @@ exports[`regression tests click-drag to select a group: [end of test] appState 1
|
||||
Object {
|
||||
"activeTool": Object {
|
||||
"customType": null,
|
||||
"lastActiveToolBeforeEraser": null,
|
||||
"lastActiveTool": null,
|
||||
"locked": false,
|
||||
"type": "selection",
|
||||
},
|
||||
@ -5221,7 +5221,7 @@ exports[`regression tests deselects group of selected elements on pointer down w
|
||||
Object {
|
||||
"activeTool": Object {
|
||||
"customType": null,
|
||||
"lastActiveToolBeforeEraser": null,
|
||||
"lastActiveTool": null,
|
||||
"locked": false,
|
||||
"type": "selection",
|
||||
},
|
||||
@ -5562,7 +5562,7 @@ exports[`regression tests deselects group of selected elements on pointer up whe
|
||||
Object {
|
||||
"activeTool": Object {
|
||||
"customType": null,
|
||||
"lastActiveToolBeforeEraser": null,
|
||||
"lastActiveTool": null,
|
||||
"locked": false,
|
||||
"type": "selection",
|
||||
},
|
||||
@ -5876,7 +5876,7 @@ exports[`regression tests deselects selected element on pointer down when pointe
|
||||
Object {
|
||||
"activeTool": Object {
|
||||
"customType": null,
|
||||
"lastActiveToolBeforeEraser": null,
|
||||
"lastActiveTool": null,
|
||||
"locked": false,
|
||||
"type": "selection",
|
||||
},
|
||||
@ -6114,7 +6114,7 @@ exports[`regression tests deselects selected element, on pointer up, when click
|
||||
Object {
|
||||
"activeTool": Object {
|
||||
"customType": null,
|
||||
"lastActiveToolBeforeEraser": null,
|
||||
"lastActiveTool": null,
|
||||
"locked": false,
|
||||
"type": "selection",
|
||||
},
|
||||
@ -6300,7 +6300,7 @@ exports[`regression tests double click to edit a group: [end of test] appState 1
|
||||
Object {
|
||||
"activeTool": Object {
|
||||
"customType": null,
|
||||
"lastActiveToolBeforeEraser": null,
|
||||
"lastActiveTool": null,
|
||||
"locked": false,
|
||||
"type": "selection",
|
||||
},
|
||||
@ -6828,7 +6828,7 @@ exports[`regression tests drags selected elements from point inside common bound
|
||||
Object {
|
||||
"activeTool": Object {
|
||||
"customType": null,
|
||||
"lastActiveToolBeforeEraser": null,
|
||||
"lastActiveTool": null,
|
||||
"locked": false,
|
||||
"type": "selection",
|
||||
},
|
||||
@ -7193,7 +7193,7 @@ exports[`regression tests draw every type of shape: [end of test] appState 1`] =
|
||||
Object {
|
||||
"activeTool": Object {
|
||||
"customType": null,
|
||||
"lastActiveToolBeforeEraser": null,
|
||||
"lastActiveTool": null,
|
||||
"locked": false,
|
||||
"type": "freedraw",
|
||||
},
|
||||
@ -9545,7 +9545,7 @@ exports[`regression tests given a group of selected elements with an element tha
|
||||
Object {
|
||||
"activeTool": Object {
|
||||
"customType": null,
|
||||
"lastActiveToolBeforeEraser": null,
|
||||
"lastActiveTool": null,
|
||||
"locked": false,
|
||||
"type": "selection",
|
||||
},
|
||||
@ -9964,7 +9964,7 @@ exports[`regression tests given a selected element A and a not selected element
|
||||
Object {
|
||||
"activeTool": Object {
|
||||
"customType": null,
|
||||
"lastActiveToolBeforeEraser": null,
|
||||
"lastActiveTool": null,
|
||||
"locked": false,
|
||||
"type": "selection",
|
||||
},
|
||||
@ -10253,7 +10253,7 @@ exports[`regression tests given selected element A with lower z-index than unsel
|
||||
Object {
|
||||
"activeTool": Object {
|
||||
"customType": null,
|
||||
"lastActiveToolBeforeEraser": null,
|
||||
"lastActiveTool": null,
|
||||
"locked": false,
|
||||
"type": "selection",
|
||||
},
|
||||
@ -10501,7 +10501,7 @@ exports[`regression tests given selected element A with lower z-index than unsel
|
||||
Object {
|
||||
"activeTool": Object {
|
||||
"customType": null,
|
||||
"lastActiveToolBeforeEraser": null,
|
||||
"lastActiveTool": null,
|
||||
"locked": false,
|
||||
"type": "selection",
|
||||
},
|
||||
@ -10822,7 +10822,7 @@ exports[`regression tests key 2 selects rectangle tool: [end of test] appState 1
|
||||
Object {
|
||||
"activeTool": Object {
|
||||
"customType": null,
|
||||
"lastActiveToolBeforeEraser": null,
|
||||
"lastActiveTool": null,
|
||||
"locked": false,
|
||||
"type": "selection",
|
||||
},
|
||||
@ -11006,7 +11006,7 @@ exports[`regression tests key 3 selects diamond tool: [end of test] appState 1`]
|
||||
Object {
|
||||
"activeTool": Object {
|
||||
"customType": null,
|
||||
"lastActiveToolBeforeEraser": null,
|
||||
"lastActiveTool": null,
|
||||
"locked": false,
|
||||
"type": "selection",
|
||||
},
|
||||
@ -11190,7 +11190,7 @@ exports[`regression tests key 4 selects ellipse tool: [end of test] appState 1`]
|
||||
Object {
|
||||
"activeTool": Object {
|
||||
"customType": null,
|
||||
"lastActiveToolBeforeEraser": null,
|
||||
"lastActiveTool": null,
|
||||
"locked": false,
|
||||
"type": "selection",
|
||||
},
|
||||
@ -11374,7 +11374,7 @@ exports[`regression tests key 5 selects arrow tool: [end of test] appState 1`] =
|
||||
Object {
|
||||
"activeTool": Object {
|
||||
"customType": null,
|
||||
"lastActiveToolBeforeEraser": null,
|
||||
"lastActiveTool": null,
|
||||
"locked": false,
|
||||
"type": "selection",
|
||||
},
|
||||
@ -11611,7 +11611,7 @@ exports[`regression tests key 6 selects line tool: [end of test] appState 1`] =
|
||||
Object {
|
||||
"activeTool": Object {
|
||||
"customType": null,
|
||||
"lastActiveToolBeforeEraser": null,
|
||||
"lastActiveTool": null,
|
||||
"locked": false,
|
||||
"type": "selection",
|
||||
},
|
||||
@ -11848,7 +11848,7 @@ exports[`regression tests key 7 selects freedraw tool: [end of test] appState 1`
|
||||
Object {
|
||||
"activeTool": Object {
|
||||
"customType": null,
|
||||
"lastActiveToolBeforeEraser": null,
|
||||
"lastActiveTool": null,
|
||||
"locked": false,
|
||||
"type": "freedraw",
|
||||
},
|
||||
@ -12076,7 +12076,7 @@ exports[`regression tests key a selects arrow tool: [end of test] appState 1`] =
|
||||
Object {
|
||||
"activeTool": Object {
|
||||
"customType": null,
|
||||
"lastActiveToolBeforeEraser": null,
|
||||
"lastActiveTool": null,
|
||||
"locked": false,
|
||||
"type": "selection",
|
||||
},
|
||||
@ -12313,7 +12313,7 @@ exports[`regression tests key d selects diamond tool: [end of test] appState 1`]
|
||||
Object {
|
||||
"activeTool": Object {
|
||||
"customType": null,
|
||||
"lastActiveToolBeforeEraser": null,
|
||||
"lastActiveTool": null,
|
||||
"locked": false,
|
||||
"type": "selection",
|
||||
},
|
||||
@ -12497,7 +12497,7 @@ exports[`regression tests key l selects line tool: [end of test] appState 1`] =
|
||||
Object {
|
||||
"activeTool": Object {
|
||||
"customType": null,
|
||||
"lastActiveToolBeforeEraser": null,
|
||||
"lastActiveTool": null,
|
||||
"locked": false,
|
||||
"type": "selection",
|
||||
},
|
||||
@ -12734,7 +12734,7 @@ exports[`regression tests key o selects ellipse tool: [end of test] appState 1`]
|
||||
Object {
|
||||
"activeTool": Object {
|
||||
"customType": null,
|
||||
"lastActiveToolBeforeEraser": null,
|
||||
"lastActiveTool": null,
|
||||
"locked": false,
|
||||
"type": "selection",
|
||||
},
|
||||
@ -12918,7 +12918,7 @@ exports[`regression tests key p selects freedraw tool: [end of test] appState 1`
|
||||
Object {
|
||||
"activeTool": Object {
|
||||
"customType": null,
|
||||
"lastActiveToolBeforeEraser": null,
|
||||
"lastActiveTool": null,
|
||||
"locked": false,
|
||||
"type": "freedraw",
|
||||
},
|
||||
@ -13146,7 +13146,7 @@ exports[`regression tests key r selects rectangle tool: [end of test] appState 1
|
||||
Object {
|
||||
"activeTool": Object {
|
||||
"customType": null,
|
||||
"lastActiveToolBeforeEraser": null,
|
||||
"lastActiveTool": null,
|
||||
"locked": false,
|
||||
"type": "selection",
|
||||
},
|
||||
@ -13330,7 +13330,7 @@ exports[`regression tests make a group and duplicate it: [end of test] appState
|
||||
Object {
|
||||
"activeTool": Object {
|
||||
"customType": null,
|
||||
"lastActiveToolBeforeEraser": null,
|
||||
"lastActiveTool": null,
|
||||
"locked": false,
|
||||
"type": "selection",
|
||||
},
|
||||
@ -14169,7 +14169,7 @@ exports[`regression tests noop interaction after undo shouldn't create history e
|
||||
Object {
|
||||
"activeTool": Object {
|
||||
"customType": null,
|
||||
"lastActiveToolBeforeEraser": null,
|
||||
"lastActiveTool": null,
|
||||
"locked": false,
|
||||
"type": "selection",
|
||||
},
|
||||
@ -14458,7 +14458,7 @@ exports[`regression tests pinch-to-zoom works: [end of test] appState 1`] = `
|
||||
Object {
|
||||
"activeTool": Object {
|
||||
"customType": null,
|
||||
"lastActiveToolBeforeEraser": null,
|
||||
"lastActiveTool": null,
|
||||
"locked": false,
|
||||
"type": "selection",
|
||||
},
|
||||
@ -14569,7 +14569,7 @@ exports[`regression tests rerenders UI on language change: [end of test] appStat
|
||||
Object {
|
||||
"activeTool": Object {
|
||||
"customType": null,
|
||||
"lastActiveToolBeforeEraser": null,
|
||||
"lastActiveTool": null,
|
||||
"locked": false,
|
||||
"type": "rectangle",
|
||||
},
|
||||
@ -14678,7 +14678,7 @@ exports[`regression tests shift click on selected element should deselect it on
|
||||
Object {
|
||||
"activeTool": Object {
|
||||
"customType": null,
|
||||
"lastActiveToolBeforeEraser": null,
|
||||
"lastActiveTool": null,
|
||||
"locked": false,
|
||||
"type": "selection",
|
||||
},
|
||||
@ -14865,7 +14865,7 @@ exports[`regression tests shift-click to multiselect, then drag: [end of test] a
|
||||
Object {
|
||||
"activeTool": Object {
|
||||
"customType": null,
|
||||
"lastActiveToolBeforeEraser": null,
|
||||
"lastActiveTool": null,
|
||||
"locked": false,
|
||||
"type": "selection",
|
||||
},
|
||||
@ -15233,7 +15233,7 @@ exports[`regression tests should group elements and ungroup them: [end of test]
|
||||
Object {
|
||||
"activeTool": Object {
|
||||
"customType": null,
|
||||
"lastActiveToolBeforeEraser": null,
|
||||
"lastActiveTool": null,
|
||||
"locked": false,
|
||||
"type": "selection",
|
||||
},
|
||||
@ -15864,7 +15864,7 @@ exports[`regression tests should show fill icons when element has non transparen
|
||||
Object {
|
||||
"activeTool": Object {
|
||||
"customType": null,
|
||||
"lastActiveToolBeforeEraser": null,
|
||||
"lastActiveTool": null,
|
||||
"locked": false,
|
||||
"type": "selection",
|
||||
},
|
||||
@ -16090,7 +16090,7 @@ exports[`regression tests single-clicking on a subgroup of a selected group shou
|
||||
Object {
|
||||
"activeTool": Object {
|
||||
"customType": null,
|
||||
"lastActiveToolBeforeEraser": null,
|
||||
"lastActiveTool": null,
|
||||
"locked": false,
|
||||
"type": "selection",
|
||||
},
|
||||
@ -17053,7 +17053,7 @@ exports[`regression tests spacebar + drag scrolls the canvas: [end of test] appS
|
||||
Object {
|
||||
"activeTool": Object {
|
||||
"customType": null,
|
||||
"lastActiveToolBeforeEraser": null,
|
||||
"lastActiveTool": null,
|
||||
"locked": false,
|
||||
"type": "selection",
|
||||
},
|
||||
@ -17162,7 +17162,7 @@ exports[`regression tests supports nested groups: [end of test] appState 1`] = `
|
||||
Object {
|
||||
"activeTool": Object {
|
||||
"customType": null,
|
||||
"lastActiveToolBeforeEraser": null,
|
||||
"lastActiveTool": null,
|
||||
"locked": false,
|
||||
"type": "selection",
|
||||
},
|
||||
@ -18021,7 +18021,7 @@ exports[`regression tests switches from group of selected elements to another el
|
||||
Object {
|
||||
"activeTool": Object {
|
||||
"customType": null,
|
||||
"lastActiveToolBeforeEraser": null,
|
||||
"lastActiveTool": null,
|
||||
"locked": false,
|
||||
"type": "selection",
|
||||
},
|
||||
@ -18493,7 +18493,7 @@ exports[`regression tests switches selected element on pointer down: [end of tes
|
||||
Object {
|
||||
"activeTool": Object {
|
||||
"customType": null,
|
||||
"lastActiveToolBeforeEraser": null,
|
||||
"lastActiveTool": null,
|
||||
"locked": false,
|
||||
"type": "selection",
|
||||
},
|
||||
@ -18834,7 +18834,7 @@ exports[`regression tests two-finger scroll works: [end of test] appState 1`] =
|
||||
Object {
|
||||
"activeTool": Object {
|
||||
"customType": null,
|
||||
"lastActiveToolBeforeEraser": null,
|
||||
"lastActiveTool": null,
|
||||
"locked": false,
|
||||
"type": "selection",
|
||||
},
|
||||
@ -18945,7 +18945,7 @@ exports[`regression tests undo/redo drawing an element: [end of test] appState 1
|
||||
Object {
|
||||
"activeTool": Object {
|
||||
"customType": null,
|
||||
"lastActiveToolBeforeEraser": null,
|
||||
"lastActiveTool": null,
|
||||
"locked": false,
|
||||
"type": "selection",
|
||||
},
|
||||
@ -19516,7 +19516,7 @@ exports[`regression tests updates fontSize & fontFamily appState: [end of test]
|
||||
Object {
|
||||
"activeTool": Object {
|
||||
"customType": null,
|
||||
"lastActiveToolBeforeEraser": null,
|
||||
"lastActiveTool": null,
|
||||
"locked": false,
|
||||
"type": "text",
|
||||
},
|
||||
@ -19625,7 +19625,7 @@ exports[`regression tests zoom hotkeys: [end of test] appState 1`] = `
|
||||
Object {
|
||||
"activeTool": Object {
|
||||
"customType": null,
|
||||
"lastActiveToolBeforeEraser": null,
|
||||
"lastActiveTool": null,
|
||||
"locked": false,
|
||||
"type": "selection",
|
||||
},
|
||||
|
@ -4,7 +4,7 @@ exports[`exportToSvg with default arguments 1`] = `
|
||||
Object {
|
||||
"activeTool": Object {
|
||||
"customType": null,
|
||||
"lastActiveToolBeforeEraser": null,
|
||||
"lastActiveTool": null,
|
||||
"locked": false,
|
||||
"type": "selection",
|
||||
},
|
||||
|
22
src/types.ts
22
src/types.ts
@ -81,9 +81,9 @@ export type BinaryFileMetadata = Omit<BinaryFileData, "dataURL">;
|
||||
|
||||
export type BinaryFiles = Record<ExcalidrawElement["id"], BinaryFileData>;
|
||||
|
||||
export type LastActiveToolBeforeEraser =
|
||||
export type LastActiveTool =
|
||||
| {
|
||||
type: typeof SHAPES[number]["value"] | "eraser";
|
||||
type: typeof SHAPES[number]["value"] | "eraser" | "hand";
|
||||
customType: null;
|
||||
}
|
||||
| {
|
||||
@ -112,19 +112,23 @@ export type AppState = {
|
||||
// (e.g. text element when typing into the input)
|
||||
editingElement: NonDeletedExcalidrawElement | null;
|
||||
editingLinearElement: LinearElementEditor | null;
|
||||
activeTool:
|
||||
| {
|
||||
type: typeof SHAPES[number]["value"] | "eraser";
|
||||
lastActiveToolBeforeEraser: LastActiveToolBeforeEraser;
|
||||
activeTool: {
|
||||
/**
|
||||
* indicates a previous tool we should revert back to if we deselect the
|
||||
* currently active tool. At the moment applies to `eraser` and `hand` tool.
|
||||
*/
|
||||
lastActiveTool: LastActiveTool;
|
||||
locked: boolean;
|
||||
} & (
|
||||
| {
|
||||
type: typeof SHAPES[number]["value"] | "eraser" | "hand";
|
||||
customType: null;
|
||||
}
|
||||
| {
|
||||
type: "custom";
|
||||
customType: string;
|
||||
lastActiveToolBeforeEraser: LastActiveToolBeforeEraser;
|
||||
locked: boolean;
|
||||
};
|
||||
}
|
||||
);
|
||||
penMode: boolean;
|
||||
penDetected: boolean;
|
||||
exportBackground: boolean;
|
||||
|
36
src/utils.ts
36
src/utils.ts
@ -6,16 +6,17 @@ import {
|
||||
DEFAULT_VERSION,
|
||||
EVENT,
|
||||
FONT_FAMILY,
|
||||
isDarwin,
|
||||
MIME_TYPES,
|
||||
THEME,
|
||||
WINDOWS_EMOJI_FALLBACK_FONT,
|
||||
} from "./constants";
|
||||
import { FontFamilyValues, FontString } from "./element/types";
|
||||
import { AppState, DataURL, LastActiveToolBeforeEraser, Zoom } from "./types";
|
||||
import { AppState, DataURL, LastActiveTool, Zoom } from "./types";
|
||||
import { unstable_batchedUpdates } from "react-dom";
|
||||
import { isDarwin } from "./keys";
|
||||
import { SHAPES } from "./shapes";
|
||||
import React from "react";
|
||||
import { isEraserActive, isHandToolActive } from "./appState";
|
||||
|
||||
let mockDateTime: string | null = null;
|
||||
|
||||
@ -219,9 +220,9 @@ export const distance = (x: number, y: number) => Math.abs(x - y);
|
||||
export const updateActiveTool = (
|
||||
appState: Pick<AppState, "activeTool">,
|
||||
data: (
|
||||
| { type: typeof SHAPES[number]["value"] | "eraser" }
|
||||
| { type: typeof SHAPES[number]["value"] | "eraser" | "hand" }
|
||||
| { type: "custom"; customType: string }
|
||||
) & { lastActiveToolBeforeEraser?: LastActiveToolBeforeEraser },
|
||||
) & { lastActiveToolBeforeEraser?: LastActiveTool },
|
||||
): AppState["activeTool"] => {
|
||||
if (data.type === "custom") {
|
||||
return {
|
||||
@ -233,9 +234,9 @@ export const updateActiveTool = (
|
||||
|
||||
return {
|
||||
...appState.activeTool,
|
||||
lastActiveToolBeforeEraser:
|
||||
lastActiveTool:
|
||||
data.lastActiveToolBeforeEraser === undefined
|
||||
? appState.activeTool.lastActiveToolBeforeEraser
|
||||
? appState.activeTool.lastActiveTool
|
||||
: data.lastActiveToolBeforeEraser,
|
||||
type: data.type,
|
||||
customType: null,
|
||||
@ -305,7 +306,9 @@ export const setCursorForShape = (
|
||||
}
|
||||
if (appState.activeTool.type === "selection") {
|
||||
resetCursor(canvas);
|
||||
} else if (appState.activeTool.type === "eraser") {
|
||||
} else if (isHandToolActive(appState)) {
|
||||
canvas.style.cursor = CURSOR_TYPE.GRAB;
|
||||
} else if (isEraserActive(appState)) {
|
||||
setEraserCursor(canvas, appState.theme);
|
||||
// do nothing if image tool is selected which suggests there's
|
||||
// a image-preview set as the cursor
|
||||
@ -740,3 +743,22 @@ export const isShallowEqual = <T extends Record<string, any>>(
|
||||
}
|
||||
return aKeys.every((key) => objA[key] === objB[key]);
|
||||
};
|
||||
|
||||
// taken from Radix UI
|
||||
// https://github.com/radix-ui/primitives/blob/main/packages/core/primitive/src/primitive.tsx
|
||||
export const composeEventHandlers = <E>(
|
||||
originalEventHandler?: (event: E) => void,
|
||||
ourEventHandler?: (event: E) => void,
|
||||
{ checkForDefaultPrevented = true } = {},
|
||||
) => {
|
||||
return function handleEvent(event: E) {
|
||||
originalEventHandler?.(event);
|
||||
|
||||
if (
|
||||
!checkForDefaultPrevented ||
|
||||
!(event as unknown as Event).defaultPrevented
|
||||
) {
|
||||
return ourEventHandler?.(event);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user