mirror of
https://github.com/excalidraw/excalidraw.git
synced 2025-05-03 10:00:07 -04:00
Add clear canvas button on navigation menu
This commit is contained in:
parent
4a60fe3d22
commit
d423b0c330
8 changed files with 1482 additions and 1921 deletions
|
@ -878,6 +878,7 @@ const ExcalidrawWrapper = () => {
|
||||||
theme={appTheme}
|
theme={appTheme}
|
||||||
setTheme={(theme) => setAppTheme(theme)}
|
setTheme={(theme) => setAppTheme(theme)}
|
||||||
refresh={() => forceRefresh((prev) => !prev)}
|
refresh={() => forceRefresh((prev) => !prev)}
|
||||||
|
excalidrawAPI={excalidrawAPI} // Pass excalidrawAPI to AppMainMenu
|
||||||
/>
|
/>
|
||||||
<AppWelcomeScreen
|
<AppWelcomeScreen
|
||||||
onCollabDialogOpen={onCollabDialogOpen}
|
onCollabDialogOpen={onCollabDialogOpen}
|
||||||
|
|
BIN
excalidraw-app/components/.AppMainMenu.tsx.swp
Normal file
BIN
excalidraw-app/components/.AppMainMenu.tsx.swp
Normal file
Binary file not shown.
|
@ -1,19 +1,11 @@
|
||||||
import {
|
import React, { useState } from "react";
|
||||||
loginIcon,
|
|
||||||
ExcalLogo,
|
|
||||||
eyeIcon,
|
|
||||||
} from "@excalidraw/excalidraw/components/icons";
|
|
||||||
import { MainMenu } from "@excalidraw/excalidraw/index";
|
import { MainMenu } from "@excalidraw/excalidraw/index";
|
||||||
import React from "react";
|
import { ExcalLogo, eyeIcon, loginIcon, TrashIcon } from "../../packages/excalidraw/components/icons";
|
||||||
|
|
||||||
import { isDevEnv } from "@excalidraw/common";
|
import { isDevEnv } from "@excalidraw/common";
|
||||||
|
|
||||||
import type { Theme } from "@excalidraw/element/types";
|
|
||||||
|
|
||||||
import { LanguageList } from "../app-language/LanguageList";
|
import { LanguageList } from "../app-language/LanguageList";
|
||||||
import { isExcalidrawPlusSignedUser } from "../app_constants";
|
import { isExcalidrawPlusSignedUser } from "../app_constants";
|
||||||
|
|
||||||
import { saveDebugState } from "./DebugCanvas";
|
import { saveDebugState } from "./DebugCanvas";
|
||||||
|
import type { Theme } from "../../packages/element/src/types";
|
||||||
|
|
||||||
export const AppMainMenu: React.FC<{
|
export const AppMainMenu: React.FC<{
|
||||||
onCollabDialogOpen: () => any;
|
onCollabDialogOpen: () => any;
|
||||||
|
@ -22,70 +14,104 @@ export const AppMainMenu: React.FC<{
|
||||||
theme: Theme | "system";
|
theme: Theme | "system";
|
||||||
setTheme: (theme: Theme | "system") => void;
|
setTheme: (theme: Theme | "system") => void;
|
||||||
refresh: () => void;
|
refresh: () => void;
|
||||||
|
excalidrawAPI: any;
|
||||||
}> = React.memo((props) => {
|
}> = React.memo((props) => {
|
||||||
|
const [isDialogOpen, setIsDialogOpen] = useState(false);
|
||||||
|
|
||||||
|
const handleClearCanvas = () => {
|
||||||
|
setIsDialogOpen(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const confirmClearCanvas = () => {
|
||||||
|
if (props.excalidrawAPI) {
|
||||||
|
props.excalidrawAPI.updateScene({ elements: [] });
|
||||||
|
}
|
||||||
|
setIsDialogOpen(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const cancelClearCanvas = () => {
|
||||||
|
setIsDialogOpen(false);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MainMenu>
|
<>
|
||||||
<MainMenu.DefaultItems.LoadScene />
|
<MainMenu>
|
||||||
<MainMenu.DefaultItems.SaveToActiveFile />
|
<MainMenu.DefaultItems.LoadScene />
|
||||||
<MainMenu.DefaultItems.Export />
|
<MainMenu.DefaultItems.SaveToActiveFile />
|
||||||
<MainMenu.DefaultItems.SaveAsImage />
|
<MainMenu.DefaultItems.Export />
|
||||||
{props.isCollabEnabled && (
|
<MainMenu.DefaultItems.SaveAsImage />
|
||||||
<MainMenu.DefaultItems.LiveCollaborationTrigger
|
{props.isCollabEnabled && (
|
||||||
isCollaborating={props.isCollaborating}
|
<MainMenu.DefaultItems.LiveCollaborationTrigger
|
||||||
onSelect={() => props.onCollabDialogOpen()}
|
isCollaborating={props.isCollaborating}
|
||||||
/>
|
onSelect={() => props.onCollabDialogOpen()}
|
||||||
)}
|
/>
|
||||||
<MainMenu.DefaultItems.CommandPalette className="highlighted" />
|
)}
|
||||||
<MainMenu.DefaultItems.SearchMenu />
|
<MainMenu.DefaultItems.CommandPalette className="highlighted" />
|
||||||
<MainMenu.DefaultItems.Help />
|
<MainMenu.DefaultItems.SearchMenu />
|
||||||
<MainMenu.DefaultItems.ClearCanvas />
|
<MainMenu.DefaultItems.Help />
|
||||||
<MainMenu.Separator />
|
<MainMenu.DefaultItems.ClearCanvas />
|
||||||
<MainMenu.ItemLink
|
<MainMenu.Item icon={TrashIcon} onClick={handleClearCanvas}>
|
||||||
icon={ExcalLogo}
|
Clear Canvas
|
||||||
href={`${
|
|
||||||
import.meta.env.VITE_APP_PLUS_LP
|
|
||||||
}/plus?utm_source=excalidraw&utm_medium=app&utm_content=hamburger`}
|
|
||||||
className=""
|
|
||||||
>
|
|
||||||
Excalidraw+
|
|
||||||
</MainMenu.ItemLink>
|
|
||||||
<MainMenu.DefaultItems.Socials />
|
|
||||||
<MainMenu.ItemLink
|
|
||||||
icon={loginIcon}
|
|
||||||
href={`${import.meta.env.VITE_APP_PLUS_APP}${
|
|
||||||
isExcalidrawPlusSignedUser ? "" : "/sign-up"
|
|
||||||
}?utm_source=signin&utm_medium=app&utm_content=hamburger`}
|
|
||||||
className="highlighted"
|
|
||||||
>
|
|
||||||
{isExcalidrawPlusSignedUser ? "Sign in" : "Sign up"}
|
|
||||||
</MainMenu.ItemLink>
|
|
||||||
{isDevEnv() && (
|
|
||||||
<MainMenu.Item
|
|
||||||
icon={eyeIcon}
|
|
||||||
onClick={() => {
|
|
||||||
if (window.visualDebug) {
|
|
||||||
delete window.visualDebug;
|
|
||||||
saveDebugState({ enabled: false });
|
|
||||||
} else {
|
|
||||||
window.visualDebug = { data: [] };
|
|
||||||
saveDebugState({ enabled: true });
|
|
||||||
}
|
|
||||||
props?.refresh();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Visual Debug
|
|
||||||
</MainMenu.Item>
|
</MainMenu.Item>
|
||||||
|
<MainMenu.Separator />
|
||||||
|
<MainMenu.ItemLink
|
||||||
|
icon={ExcalLogo}
|
||||||
|
href={`${
|
||||||
|
import.meta.env.VITE_APP_PLUS_LP
|
||||||
|
}/plus?utm_source=excalidraw&utm_medium=app&utm_content=hamburger`}
|
||||||
|
className=""
|
||||||
|
>
|
||||||
|
Excalidraw+
|
||||||
|
</MainMenu.ItemLink>
|
||||||
|
<MainMenu.DefaultItems.Socials />
|
||||||
|
<MainMenu.ItemLink
|
||||||
|
icon={loginIcon}
|
||||||
|
href={`${import.meta.env.VITE_APP_PLUS_APP}${
|
||||||
|
isExcalidrawPlusSignedUser ? "" : "/sign-up"
|
||||||
|
}?utm_source=signin&utm_medium=app&utm_content=hamburger`}
|
||||||
|
className="highlighted"
|
||||||
|
>
|
||||||
|
{isExcalidrawPlusSignedUser ? "Sign in" : "Sign up"}
|
||||||
|
</MainMenu.ItemLink>
|
||||||
|
{isDevEnv() && (
|
||||||
|
<MainMenu.Item
|
||||||
|
icon={eyeIcon}
|
||||||
|
onClick={() => {
|
||||||
|
if (window.visualDebug) {
|
||||||
|
delete window.visualDebug;
|
||||||
|
saveDebugState({ enabled: false });
|
||||||
|
} else {
|
||||||
|
window.visualDebug = { data: [] };
|
||||||
|
saveDebugState({ enabled: true });
|
||||||
|
}
|
||||||
|
props?.refresh();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Visual Debug
|
||||||
|
</MainMenu.Item>
|
||||||
|
)}
|
||||||
|
<MainMenu.Separator />
|
||||||
|
<MainMenu.DefaultItems.ToggleTheme
|
||||||
|
allowSystemTheme
|
||||||
|
theme={props.theme}
|
||||||
|
onSelect={props.setTheme}
|
||||||
|
/>
|
||||||
|
<MainMenu.ItemCustom>
|
||||||
|
<LanguageList style={{ width: "100%" }} />
|
||||||
|
</MainMenu.ItemCustom>
|
||||||
|
<MainMenu.DefaultItems.ChangeCanvasBackground />
|
||||||
|
</MainMenu>
|
||||||
|
|
||||||
|
{isDialogOpen && (
|
||||||
|
<div className="confirm-dialog">
|
||||||
|
<div className="confirm-dialog-content">
|
||||||
|
<h3>Clear Canvas</h3>
|
||||||
|
<p>Are you sure you want to clear the canvas? This action cannot be undone.</p>
|
||||||
|
<button onClick={confirmClearCanvas}>Confirm</button>
|
||||||
|
<button onClick={cancelClearCanvas}>Cancel</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
<MainMenu.Separator />
|
</>
|
||||||
<MainMenu.DefaultItems.ToggleTheme
|
|
||||||
allowSystemTheme
|
|
||||||
theme={props.theme}
|
|
||||||
onSelect={props.setTheme}
|
|
||||||
/>
|
|
||||||
<MainMenu.ItemCustom>
|
|
||||||
<LanguageList style={{ width: "100%" }} />
|
|
||||||
</MainMenu.ItemCustom>
|
|
||||||
<MainMenu.DefaultItems.ChangeCanvasBackground />
|
|
||||||
</MainMenu>
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
@ -115,3 +115,46 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.confirm-dialog {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background-color: rgba(0, 0, 0, 0.5); /* Backdrop */
|
||||||
|
z-index: 1000; /* Ensure it appears above other elements */
|
||||||
|
}
|
||||||
|
|
||||||
|
.confirm-dialog-content {
|
||||||
|
background: white;
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.confirm-dialog-content h3 {
|
||||||
|
margin: 0 0 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.confirm-dialog-content button {
|
||||||
|
margin: 5px;
|
||||||
|
padding: 10px 20px;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.confirm-dialog-content button:first-of-type {
|
||||||
|
background-color: #007bff;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.confirm-dialog-content button:last-of-type {
|
||||||
|
background-color: #f8f9fa;
|
||||||
|
color: #212529;
|
||||||
|
}
|
||||||
|
|
BIN
excalidraw-app/tests/.AppMainMenu.test.tsx.swp
Normal file
BIN
excalidraw-app/tests/.AppMainMenu.test.tsx.swp
Normal file
Binary file not shown.
35
excalidraw-app/tests/AppMainMenu.test.tsx
Normal file
35
excalidraw-app/tests/AppMainMenu.test.tsx
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
import { render, screen, fireEvent } from "@testing-library/react";
|
||||||
|
import { AppMainMenu } from "../components/AppMainMenu";
|
||||||
|
import type { Theme } from "@excalidraw/element/types";
|
||||||
|
|
||||||
|
describe("AppMainMenu", () => {
|
||||||
|
it("should show a confirmation dialog and clear the canvas when confirmed", () => {
|
||||||
|
const mockUpdateScene = jest.fn();
|
||||||
|
const mockExcalidrawAPI = { updateScene: mockUpdateScene };
|
||||||
|
|
||||||
|
render(<AppMainMenu excalidrawAPI={mockExcalidrawAPI} onCollabDialogOpen={function () {
|
||||||
|
throw new Error("Function not implemented.");
|
||||||
|
} } isCollaborating={false} isCollabEnabled={false} theme={"light"} setTheme={function (theme: Theme | "system"): void {
|
||||||
|
throw new Error("Function not implemented.");
|
||||||
|
} } refresh={function (): void {
|
||||||
|
throw new Error("Function not implemented.");
|
||||||
|
} } />);
|
||||||
|
|
||||||
|
// Click the "Clear Canvas" button
|
||||||
|
const clearCanvasButton = screen.getByText("Clear Canvas");
|
||||||
|
fireEvent.click(clearCanvasButton);
|
||||||
|
|
||||||
|
// Verify the confirmation dialog appears
|
||||||
|
expect(screen.getByText("Clear Canvas")).toBeInTheDocument();
|
||||||
|
expect(
|
||||||
|
screen.getByText("Are you sure you want to clear the canvas?")
|
||||||
|
).toBeInTheDocument();
|
||||||
|
|
||||||
|
// Confirm the action
|
||||||
|
const confirmButton = screen.getByText("Confirm");
|
||||||
|
fireEvent.click(confirmButton);
|
||||||
|
|
||||||
|
// Verify the canvas is cleared
|
||||||
|
expect(mockUpdateScene).toHaveBeenCalledWith({ elements: [] });
|
||||||
|
});
|
||||||
|
});
|
|
@ -84,5 +84,9 @@
|
||||||
},
|
},
|
||||||
"resolutions": {
|
"resolutions": {
|
||||||
"strip-ansi": "6.0.1"
|
"strip-ansi": "6.0.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"clsx": "2.1.1",
|
||||||
|
"react": "19.1.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue