You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
298 lines
7.5 KiB
TypeScript
298 lines
7.5 KiB
TypeScript
import { getShortcutFromShortcutName } from "../../actions/shortcuts";
|
|
import { useI18n } from "../../i18n";
|
|
import {
|
|
useExcalidrawSetAppState,
|
|
useExcalidrawActionManager,
|
|
useExcalidrawElements,
|
|
useAppProps,
|
|
} from "../App";
|
|
import {
|
|
ExportIcon,
|
|
ExportImageIcon,
|
|
HelpIcon,
|
|
LoadIcon,
|
|
MoonIcon,
|
|
save,
|
|
SunIcon,
|
|
TrashIcon,
|
|
usersIcon,
|
|
} from "../icons";
|
|
import { GithubIcon, DiscordIcon, XBrandIcon } from "../icons";
|
|
import DropdownMenuItem from "../dropdownMenu/DropdownMenuItem";
|
|
import DropdownMenuItemLink from "../dropdownMenu/DropdownMenuItemLink";
|
|
import {
|
|
actionClearCanvas,
|
|
actionLoadScene,
|
|
actionSaveToActiveFile,
|
|
actionShortcuts,
|
|
actionToggleTheme,
|
|
} from "../../actions";
|
|
|
|
import "./DefaultItems.scss";
|
|
import clsx from "clsx";
|
|
import { useSetAtom } from "jotai";
|
|
import { activeConfirmDialogAtom } from "../ActiveConfirmDialog";
|
|
import { jotaiScope } from "../../jotai";
|
|
import { useUIAppState } from "../../context/ui-appState";
|
|
import { openConfirmModal } from "../OverwriteConfirm/OverwriteConfirmState";
|
|
import Trans from "../Trans";
|
|
|
|
export const LoadScene = () => {
|
|
const { t } = useI18n();
|
|
const actionManager = useExcalidrawActionManager();
|
|
const elements = useExcalidrawElements();
|
|
|
|
if (!actionManager.isActionEnabled(actionLoadScene)) {
|
|
return null;
|
|
}
|
|
|
|
const handleSelect = async () => {
|
|
if (
|
|
!elements.length ||
|
|
(await openConfirmModal({
|
|
title: t("overwriteConfirm.modal.loadFromFile.title"),
|
|
actionLabel: t("overwriteConfirm.modal.loadFromFile.button"),
|
|
color: "warning",
|
|
description: (
|
|
<Trans
|
|
i18nKey="overwriteConfirm.modal.loadFromFile.description"
|
|
bold={(text) => <strong>{text}</strong>}
|
|
br={() => <br />}
|
|
/>
|
|
),
|
|
}))
|
|
) {
|
|
actionManager.executeAction(actionLoadScene);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<DropdownMenuItem
|
|
icon={LoadIcon}
|
|
onSelect={handleSelect}
|
|
data-testid="load-button"
|
|
shortcut={getShortcutFromShortcutName("loadScene")}
|
|
aria-label={t("buttons.load")}
|
|
>
|
|
{t("buttons.load")}
|
|
</DropdownMenuItem>
|
|
);
|
|
};
|
|
LoadScene.displayName = "LoadScene";
|
|
|
|
export const SaveToActiveFile = () => {
|
|
const { t } = useI18n();
|
|
const actionManager = useExcalidrawActionManager();
|
|
|
|
if (!actionManager.isActionEnabled(actionSaveToActiveFile)) {
|
|
return null;
|
|
}
|
|
|
|
return (
|
|
<DropdownMenuItem
|
|
shortcut={getShortcutFromShortcutName("saveScene")}
|
|
data-testid="save-button"
|
|
onSelect={() => actionManager.executeAction(actionSaveToActiveFile)}
|
|
icon={save}
|
|
aria-label={`${t("buttons.save")}`}
|
|
>{`${t("buttons.save")}`}</DropdownMenuItem>
|
|
);
|
|
};
|
|
SaveToActiveFile.displayName = "SaveToActiveFile";
|
|
|
|
export const SaveAsImage = () => {
|
|
const setAppState = useExcalidrawSetAppState();
|
|
const { t } = useI18n();
|
|
return (
|
|
<DropdownMenuItem
|
|
icon={ExportImageIcon}
|
|
data-testid="image-export-button"
|
|
onSelect={() => setAppState({ openDialog: { name: "imageExport" } })}
|
|
shortcut={getShortcutFromShortcutName("imageExport")}
|
|
aria-label={t("buttons.exportImage")}
|
|
>
|
|
{t("buttons.exportImage")}
|
|
</DropdownMenuItem>
|
|
);
|
|
};
|
|
SaveAsImage.displayName = "SaveAsImage";
|
|
|
|
export const Help = () => {
|
|
const { t } = useI18n();
|
|
|
|
const actionManager = useExcalidrawActionManager();
|
|
|
|
return (
|
|
<DropdownMenuItem
|
|
data-testid="help-menu-item"
|
|
icon={HelpIcon}
|
|
onSelect={() => actionManager.executeAction(actionShortcuts)}
|
|
shortcut="?"
|
|
aria-label={t("helpDialog.title")}
|
|
>
|
|
{t("helpDialog.title")}
|
|
</DropdownMenuItem>
|
|
);
|
|
};
|
|
Help.displayName = "Help";
|
|
|
|
export const ClearCanvas = () => {
|
|
const { t } = useI18n();
|
|
|
|
const setActiveConfirmDialog = useSetAtom(
|
|
activeConfirmDialogAtom,
|
|
jotaiScope,
|
|
);
|
|
const actionManager = useExcalidrawActionManager();
|
|
|
|
if (!actionManager.isActionEnabled(actionClearCanvas)) {
|
|
return null;
|
|
}
|
|
|
|
return (
|
|
<DropdownMenuItem
|
|
icon={TrashIcon}
|
|
onSelect={() => setActiveConfirmDialog("clearCanvas")}
|
|
data-testid="clear-canvas-button"
|
|
aria-label={t("buttons.clearReset")}
|
|
>
|
|
{t("buttons.clearReset")}
|
|
</DropdownMenuItem>
|
|
);
|
|
};
|
|
ClearCanvas.displayName = "ClearCanvas";
|
|
|
|
export const ToggleTheme = () => {
|
|
const { t } = useI18n();
|
|
const appState = useUIAppState();
|
|
const actionManager = useExcalidrawActionManager();
|
|
|
|
if (!actionManager.isActionEnabled(actionToggleTheme)) {
|
|
return null;
|
|
}
|
|
|
|
return (
|
|
<DropdownMenuItem
|
|
onSelect={(event) => {
|
|
// do not close the menu when changing theme
|
|
event.preventDefault();
|
|
return actionManager.executeAction(actionToggleTheme);
|
|
}}
|
|
icon={appState.theme === "dark" ? SunIcon : MoonIcon}
|
|
data-testid="toggle-dark-mode"
|
|
shortcut={getShortcutFromShortcutName("toggleTheme")}
|
|
aria-label={
|
|
appState.theme === "dark"
|
|
? t("buttons.lightMode")
|
|
: t("buttons.darkMode")
|
|
}
|
|
>
|
|
{appState.theme === "dark"
|
|
? t("buttons.lightMode")
|
|
: t("buttons.darkMode")}
|
|
</DropdownMenuItem>
|
|
);
|
|
};
|
|
ToggleTheme.displayName = "ToggleTheme";
|
|
|
|
export const ChangeCanvasBackground = () => {
|
|
const { t } = useI18n();
|
|
const appState = useUIAppState();
|
|
const actionManager = useExcalidrawActionManager();
|
|
const appProps = useAppProps();
|
|
|
|
if (
|
|
appState.viewModeEnabled ||
|
|
!appProps.UIOptions.canvasActions.changeViewBackgroundColor
|
|
) {
|
|
return null;
|
|
}
|
|
return (
|
|
<div style={{ marginTop: "0.5rem" }}>
|
|
<div
|
|
data-testid="canvas-background-label"
|
|
style={{ fontSize: ".75rem", marginBottom: ".5rem" }}
|
|
>
|
|
{t("labels.canvasBackground")}
|
|
</div>
|
|
<div style={{ padding: "0 0.625rem" }}>
|
|
{actionManager.renderAction("changeViewBackgroundColor")}
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
ChangeCanvasBackground.displayName = "ChangeCanvasBackground";
|
|
|
|
export const Export = () => {
|
|
const { t } = useI18n();
|
|
const setAppState = useExcalidrawSetAppState();
|
|
return (
|
|
<DropdownMenuItem
|
|
icon={ExportIcon}
|
|
onSelect={() => {
|
|
setAppState({ openDialog: { name: "jsonExport" } });
|
|
}}
|
|
data-testid="json-export-button"
|
|
aria-label={t("buttons.export")}
|
|
>
|
|
{t("buttons.export")}
|
|
</DropdownMenuItem>
|
|
);
|
|
};
|
|
Export.displayName = "Export";
|
|
|
|
export const Socials = () => {
|
|
const { t } = useI18n();
|
|
|
|
return (
|
|
<>
|
|
<DropdownMenuItemLink
|
|
icon={GithubIcon}
|
|
href="https://github.com/excalidraw/excalidraw"
|
|
aria-label="GitHub"
|
|
>
|
|
GitHub
|
|
</DropdownMenuItemLink>
|
|
<DropdownMenuItemLink
|
|
icon={XBrandIcon}
|
|
href="https://x.com/excalidraw"
|
|
aria-label="X"
|
|
>
|
|
{t("labels.followUs")}
|
|
</DropdownMenuItemLink>
|
|
<DropdownMenuItemLink
|
|
icon={DiscordIcon}
|
|
href="https://discord.gg/UexuTaE"
|
|
aria-label="Discord"
|
|
>
|
|
{t("labels.discordChat")}
|
|
</DropdownMenuItemLink>
|
|
</>
|
|
);
|
|
};
|
|
Socials.displayName = "Socials";
|
|
|
|
export const LiveCollaborationTrigger = ({
|
|
onSelect,
|
|
isCollaborating,
|
|
}: {
|
|
onSelect: () => void;
|
|
isCollaborating: boolean;
|
|
}) => {
|
|
const { t } = useI18n();
|
|
return (
|
|
<DropdownMenuItem
|
|
data-testid="collab-button"
|
|
icon={usersIcon}
|
|
className={clsx({
|
|
"active-collab": isCollaborating,
|
|
})}
|
|
onSelect={onSelect}
|
|
>
|
|
{t("labels.liveCollaboration")}
|
|
</DropdownMenuItem>
|
|
);
|
|
};
|
|
|
|
LiveCollaborationTrigger.displayName = "LiveCollaborationTrigger";
|