mirror of
https://github.com/Mail-0/Zero.git
synced 2026-07-01 08:16:28 +00:00
* [#21] feat(ui): enhance sidebar collapse functionality * refactor: shadcn compose button and types * fix(lint): resolve linter errors * refactor: use index for types
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
"use client";
|
||||
|
||||
import { Check, ChevronsUpDown, Plus } from "lucide-react";
|
||||
import * as React from "react";
|
||||
import { Account } from "@/types";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
import {
|
||||
DropdownMenu,
|
||||
@@ -24,19 +25,17 @@ import {
|
||||
SidebarMenuButton,
|
||||
useSidebar,
|
||||
} from "@/components/ui/sidebar";
|
||||
import React from "react";
|
||||
|
||||
interface AccountSwitcherProps {
|
||||
accounts: {
|
||||
name: string;
|
||||
logo: React.ComponentType<{ className?: string }>;
|
||||
email: string;
|
||||
}[];
|
||||
accounts: Account[];
|
||||
}
|
||||
|
||||
export function AccountSwitcher({ accounts }: AccountSwitcherProps) {
|
||||
const [selectedAccount, setSelectedAccount] = React.useState(accounts[0]);
|
||||
|
||||
const { isMobile } = useSidebar();
|
||||
const { isMobile, state } = useSidebar();
|
||||
const collapsed = state === "collapsed";
|
||||
|
||||
return (
|
||||
<DropdownMenu>
|
||||
@@ -47,7 +46,12 @@ export function AccountSwitcher({ accounts }: AccountSwitcherProps) {
|
||||
size="lg"
|
||||
className="data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground"
|
||||
>
|
||||
<div className="flex aspect-square size-10 shrink-0 items-center justify-center rounded-lg bg-primary/10 text-sidebar-primary-foreground">
|
||||
<div
|
||||
className={cn(
|
||||
"flex aspect-square size-10 shrink-0 items-center justify-center rounded-lg bg-primary/10 text-sidebar-primary-foreground",
|
||||
collapsed && "w-full",
|
||||
)}
|
||||
>
|
||||
<selectedAccount.logo className="size-5" />
|
||||
</div>
|
||||
<div className="flex min-w-0 flex-col gap-0.5 leading-none">
|
||||
|
||||
@@ -16,8 +16,8 @@ import {
|
||||
Pencil,
|
||||
} from "lucide-react";
|
||||
import { Gmail, Outlook, Vercel } from "@/components/icons/icons";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import * as React from "react";
|
||||
import { SidebarData } from "@/types";
|
||||
import React from "react";
|
||||
|
||||
import {
|
||||
Sidebar,
|
||||
@@ -25,6 +25,9 @@ import {
|
||||
SidebarFooter,
|
||||
SidebarHeader,
|
||||
SidebarRail,
|
||||
SidebarMenu,
|
||||
SidebarMenuItem,
|
||||
SidebarMenuButton,
|
||||
} from "@/components/ui/sidebar";
|
||||
import { useSidebar } from "@/components/ui/sidebar";
|
||||
import { AccountSwitcher } from "./account-switcher";
|
||||
@@ -33,9 +36,7 @@ import { SidebarToggle } from "./sidebar-toggle";
|
||||
import { NavMain } from "./nav-main";
|
||||
import { NavUser } from "./nav-user";
|
||||
|
||||
// This is sample data that matches the screenshot
|
||||
|
||||
const data = {
|
||||
const data: SidebarData = {
|
||||
user: {
|
||||
name: "nizzy",
|
||||
email: "nizabizaher@gmail.com",
|
||||
@@ -153,7 +154,29 @@ const data = {
|
||||
|
||||
export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
|
||||
const [composeOpen, setComposeOpen] = React.useState(false);
|
||||
const { isMobile } = useSidebar();
|
||||
const sidebarContext = useSidebar();
|
||||
const { isMobile } = sidebarContext;
|
||||
|
||||
const handleComposeClick = React.useCallback(() => {
|
||||
setComposeOpen(true);
|
||||
}, []);
|
||||
|
||||
// Memoized compose button component
|
||||
const ComposeButton = React.memo(function ComposeButton() {
|
||||
return (
|
||||
<SidebarMenu>
|
||||
<SidebarMenuItem>
|
||||
<SidebarMenuButton
|
||||
className="m-2 w-fit bg-primary px-3 text-primary-foreground transition-[margin] hover:bg-primary/90 hover:text-primary-foreground active:bg-primary/90 active:text-primary-foreground group-data-[collapsible=icon]:mx-0"
|
||||
onClick={handleComposeClick}
|
||||
>
|
||||
<Pencil className="size-4" />
|
||||
<span>Compose</span>
|
||||
</SidebarMenuButton>
|
||||
</SidebarMenuItem>
|
||||
</SidebarMenu>
|
||||
);
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -161,19 +184,20 @@ export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
|
||||
<Sidebar collapsible="icon" {...props}>
|
||||
<SidebarHeader>
|
||||
<AccountSwitcher accounts={data.accounts} />
|
||||
<ComposeButton />
|
||||
</SidebarHeader>
|
||||
<SidebarContent>
|
||||
<Button className="mx-3.5 mt-2 w-fit" onClick={() => setComposeOpen(true)}>
|
||||
<Pencil className="size-4" />
|
||||
Compose
|
||||
</Button>
|
||||
<NavMain items={data.navMain} />
|
||||
</SidebarContent>
|
||||
<SidebarFooter>
|
||||
<NavUser />
|
||||
</SidebarFooter>
|
||||
<SidebarRail />
|
||||
<MailCompose open={composeOpen} onClose={() => setComposeOpen(false)} />
|
||||
<MailCompose
|
||||
open={composeOpen}
|
||||
onClose={() => setComposeOpen(false)}
|
||||
aria-label="Compose email dialog"
|
||||
/>
|
||||
</Sidebar>
|
||||
</>
|
||||
);
|
||||
|
||||
30
types/index.ts
Normal file
30
types/index.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
export interface User {
|
||||
name: string;
|
||||
email: string;
|
||||
avatar: string;
|
||||
}
|
||||
|
||||
export interface Account {
|
||||
name: string;
|
||||
logo: React.ComponentType<{ className?: string }>;
|
||||
email: string;
|
||||
}
|
||||
|
||||
export interface NavItem {
|
||||
title: string;
|
||||
url: string;
|
||||
icon?: React.ComponentType<{ className?: string }>;
|
||||
isActive?: boolean;
|
||||
badge?: number;
|
||||
}
|
||||
|
||||
export interface NavSection {
|
||||
title: string;
|
||||
items: NavItem[];
|
||||
}
|
||||
|
||||
export interface SidebarData {
|
||||
user: User;
|
||||
accounts: Account[];
|
||||
navMain: NavSection[];
|
||||
}
|
||||
Reference in New Issue
Block a user