Skip to content
This repository was archived by the owner on Jul 20, 2025. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/components/Menu/MenuContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const MenuContext = createContext<MenuContextValue | undefined>(undefined);
export const useMenuContext = () => {
const context = useContext(MenuContext);
if (!context && process.env.NODE_ENV === "development") {
console.warn("useMenuContext must be used within a Menu component");
throw new Error("useMenuContext must be used within a MenuProvider");
}
return context;
};
Expand Down
148 changes: 89 additions & 59 deletions src/components/Menu/MenuDrawer.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from "react";
import { twJoin } from "tailwind-merge";
import { Portal } from "../Portal";

interface MenuDrawerProps {
isOpen: boolean;
Expand Down Expand Up @@ -36,73 +37,102 @@ export const MenuDrawer: React.FC<MenuDrawerProps> = ({
right: "text-right",
};

return (
<>
{fullHeight && isOpen && onBackdropClick && (
<div className="fixed inset-0 z-40 bg-black/50 transition-opacity duration-300" onClick={onBackdropClick} />
)}

const renderHeader = () => {
if (!(showBackButton || title)) return null;
return (
<div
className={twJoin(
"transform transition-transform duration-300 ease-in-out",
"bg-[#FFFFFF] dark:bg-[#252525]",
fullHeight
? twJoin("fixed inset-y-0 right-0 z-50", fullWidth ? "w-full" : "w-full max-w-sm")
: "absolute inset-0 rounded-lg",
isOpen ? "translate-x-0" : "translate-x-full",
className,
)}
className={twJoin("flex items-center p-4", showDivider && "border-b border-[#38708533] dark:border-[#404040]")}
>
{/* Header */}
{(showBackButton || title) && (
<div
className={twJoin(
"flex items-center p-4",
showDivider && "border-b border-[#38708533] dark:border-[#404040]",
)}
{showBackButton && (
<button
onClick={onClose}
className="flex h-8 w-8 flex-shrink-0 items-center justify-center rounded transition-colors hover:bg-accent-secondary/10"
aria-label="Go back"
>
{showBackButton && (
<button
onClick={onClose}
className="flex h-8 w-8 flex-shrink-0 items-center justify-center rounded transition-colors hover:bg-accent-secondary/10"
aria-label="Go back"
{backIcon || (
<svg
width="16"
height="16"
viewBox="0 0 16 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
className="text-accent-primary"
>
{backIcon || (
<svg
width="16"
height="16"
viewBox="0 0 16 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
className="text-accent-primary"
>
<path
d="M10 12L6 8L10 4"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
)}
</button>
<path
d="M10 12L6 8L10 4"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
)}
{title && (
<h3
className={twJoin(
"flex-1 text-sm font-medium text-accent-primary",
showBackButton ? "ml-3" : "",
titleAlignment[titleAlign],
)}
>
{title}
</h3>
</button>
)}
{title && (
<h3
className={twJoin(
"flex-1 text-sm font-medium text-accent-primary",
showBackButton ? "ml-3" : "",
titleAlignment[titleAlign],
)}
</div>
>
{title}
</h3>
)}

<div className="flex-1 overflow-y-auto">{children}</div>
</div>
);
};

const renderContent = () => (
<>
{renderHeader()}
<div className="flex-1 overflow-y-auto">{children}</div>
</>
);

// For fullHeight drawers (mobile), use Portal to ensure proper theme sync
if (fullHeight) {
return (
<Portal mounted={true} rootClassName="menu-drawer-portal">
{onBackdropClick && (
<div
className={twJoin(
"fixed inset-0 z-40 bg-black/50 transition-opacity duration-300",
isOpen ? "opacity-100" : "pointer-events-none opacity-0",
)}
onClick={onBackdropClick}
/>
)}

<div
className={twJoin(
"transform transition-transform duration-300 ease-in-out",
"bg-[#FFFFFF] dark:bg-[#252525]",
"fixed inset-y-0 right-0 z-50",
fullWidth ? "w-full" : "w-full max-w-sm",
isOpen ? "translate-x-0" : "translate-x-full",
className,
)}
>
{renderContent()}
</div>
</Portal>
);
}

// For non-fullHeight drawers (desktop), render normally
return (
<div
className={twJoin(
"transform transition-transform duration-300 ease-in-out",
"bg-[#FFFFFF] dark:bg-[#252525]",
"absolute inset-0 rounded-lg",
isOpen ? "translate-x-0" : "translate-x-full",
className,
)}
>
{renderContent()}
</div>
);
};
1 change: 1 addition & 0 deletions src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export * from "./widgets/new-design/AmountSubsection";
export * from "./widgets/new-design/FinalityProviderSubsection";
export * from "./widgets/new-design/FeesSection";
export * from "./widgets/new-design/PreviewModal";
export * from "./widgets/new-design/SettingMenu";

export * from "./components/ListLegacy";

Expand Down
103 changes: 103 additions & 0 deletions src/widgets/new-design/SettingMenu/SettingMenu.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import { Meta, StoryFn } from "@storybook/react";
import { useState } from "react";
import { SettingMenu } from "./SettingMenu";
import { Button } from "@/components/Button";

export default {
title: "Widgets/New Design/SettingMenu",
component: SettingMenu,
argTypes: {
onOpenChange: { action: "open state changed" },
},
tags: ["autodocs"],
} as Meta<typeof SettingMenu>;

const ThemeIcon = () => (
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="40" height="40" rx="20" fill="#387085" fillOpacity="0.12" />
<path
d="M20 30C25.52 30 30 25.52 30 20C30 14.48 25.52 10 20 10C14.48 10 10 14.48 10 20C10 25.52 14.48 30 20 30ZM21 12.07C24.94 12.56 28 15.92 28 20C28 24.08 24.95 27.44 21 27.93V12.07Z"
fill="#387085"
/>
</svg>
);

const ReportABugIcon = () => (
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="40" height="40" rx="20" fill="#387085" fillOpacity="0.12" />
<path
d="M23.73 11H16.27L11 16.27V23.73L16.27 29H23.73L29 23.73V16.27L23.73 11ZM20 25.3C19.28 25.3 18.7 24.72 18.7 24C18.7 23.28 19.28 22.7 20 22.7C20.72 22.7 21.3 23.28 21.3 24C21.3 24.72 20.72 25.3 20 25.3ZM21 21H19V15H21V21Z"
fill="#387085"
/>
</svg>
);

export const Default: StoryFn = () => {
const [selectedTheme, setSelectedTheme] = useState<"light" | "dark" | "auto">("auto");

const handleThemeChange = (theme: "light" | "dark" | "auto") => {
setSelectedTheme(theme);
console.log(`${theme} theme selected`);
};

const getThemeDescription = () => {
switch (selectedTheme) {
case "light":
return "Light";
case "dark":
return "Dark";
case "auto":
return "Auto";
default:
return "Auto";
}
};

return (
<div className="p-4">
<SettingMenu>
<SettingMenu.Title>Settings</SettingMenu.Title>

<SettingMenu.Group background="secondary">
<SettingMenu.SubMenu icon={<ThemeIcon />}>
Theme
<SettingMenu.Description>{getThemeDescription()}</SettingMenu.Description>
<SettingMenu.Item selected={selectedTheme === "light"} onClick={() => handleThemeChange("light")}>
Light
</SettingMenu.Item>
<SettingMenu.Item selected={selectedTheme === "dark"} onClick={() => handleThemeChange("dark")}>
Dark
</SettingMenu.Item>
<SettingMenu.Item selected={selectedTheme === "auto"} onClick={() => handleThemeChange("auto")}>
Auto
</SettingMenu.Item>
</SettingMenu.SubMenu>

<SettingMenu.Item icon={<ReportABugIcon />} onClick={() => console.log("Report bug clicked")}>
Report a Bug
</SettingMenu.Item>
</SettingMenu.Group>

<SettingMenu.Spacer />

<SettingMenu.Group background="secondary">
<SettingMenu.Item onClick={() => window.open("https://example.com/terms", "_blank")}>
Terms of Use
</SettingMenu.Item>

<SettingMenu.Item onClick={() => window.open("https://example.com/privacy", "_blank")}>
Privacy Policy
</SettingMenu.Item>
</SettingMenu.Group>

<SettingMenu.Spacer />

<SettingMenu.CustomContent className="my-4 flex justify-center">
<Button className="!bg-[#D5FCE8] !text-black" variant="contained">
Switch to BABY Staking
</Button>
</SettingMenu.CustomContent>
</SettingMenu>
</div>
);
};
Loading