This commit is contained in:
BrunoMatosTech 2025-05-02 23:11:03 +08:00 committed by GitHub
commit b13be9e076
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 1483 additions and 1921 deletions

View file

@ -878,6 +878,7 @@ const ExcalidrawWrapper = () => {
theme={appTheme}
setTheme={(theme) => setAppTheme(theme)}
refresh={() => forceRefresh((prev) => !prev)}
excalidrawAPI={excalidrawAPI} // Pass excalidrawAPI to AppMainMenu
/>
<AppWelcomeScreen
onCollabDialogOpen={onCollabDialogOpen}

Binary file not shown.

View file

@ -1,19 +1,11 @@
import {
loginIcon,
ExcalLogo,
eyeIcon,
} from "@excalidraw/excalidraw/components/icons";
import React, { useState } from "react";
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 type { Theme } from "@excalidraw/element/types";
import { LanguageList } from "../app-language/LanguageList";
import { isExcalidrawPlusSignedUser } from "../app_constants";
import { saveDebugState } from "./DebugCanvas";
import type { Theme } from "@excalidraw/element/types";
export const AppMainMenu: React.FC<{
onCollabDialogOpen: () => any;
@ -22,70 +14,104 @@ export const AppMainMenu: React.FC<{
theme: Theme | "system";
setTheme: (theme: Theme | "system") => void;
refresh: () => void;
excalidrawAPI: any;
}> = 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 (
<MainMenu>
<MainMenu.DefaultItems.LoadScene />
<MainMenu.DefaultItems.SaveToActiveFile />
<MainMenu.DefaultItems.Export />
<MainMenu.DefaultItems.SaveAsImage />
{props.isCollabEnabled && (
<MainMenu.DefaultItems.LiveCollaborationTrigger
isCollaborating={props.isCollaborating}
onSelect={() => props.onCollabDialogOpen()}
/>
)}
<MainMenu.DefaultItems.CommandPalette className="highlighted" />
<MainMenu.DefaultItems.SearchMenu />
<MainMenu.DefaultItems.Help />
<MainMenu.DefaultItems.ClearCanvas />
<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>
<MainMenu.DefaultItems.LoadScene />
<MainMenu.DefaultItems.SaveToActiveFile />
<MainMenu.DefaultItems.Export />
<MainMenu.DefaultItems.SaveAsImage />
{props.isCollabEnabled && (
<MainMenu.DefaultItems.LiveCollaborationTrigger
isCollaborating={props.isCollaborating}
onSelect={() => props.onCollabDialogOpen()}
/>
)}
<MainMenu.DefaultItems.CommandPalette className="highlighted" />
<MainMenu.DefaultItems.SearchMenu />
<MainMenu.DefaultItems.Help />
<MainMenu.DefaultItems.ClearCanvas />
<MainMenu.Item icon={TrashIcon} onClick={handleClearCanvas}>
Clear Canvas
</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>
</>
);
});

View file

@ -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;
}

Binary file not shown.

View 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: [] });
});
});

View file

@ -84,5 +84,9 @@
},
"resolutions": {
"strip-ansi": "6.0.1"
},
"dependencies": {
"clsx": "2.1.1",
"react": "19.1.0"
}
}

View file

@ -37,6 +37,7 @@ const getConfig = (outdir) => ({
loader: {
".woff2": "file",
},
external: ["react", "react-dom"], // Explicitly externalize react and react-dom
});
function buildDev(config) {

3150
yarn.lock

File diff suppressed because it is too large Load diff