ultisuite-client/components/gmail/sidebar/sidebar-nav-panel.tsx
2026-05-20 18:22:36 +02:00

304 lines
10 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use client"
import { ChevronDown, Plus, Bot } from "lucide-react"
import { cn } from "@/lib/utils"
import { Icon } from "@iconify/react"
import { FOLDER_SECTION_ICON } from "@/lib/folder-nav-icons"
import {
CATEGORY_IDS_IN_PLUS_ONLY,
MAIL_SIDEBAR_DOSSIERS_SECTION_STICKY_Z,
hasPlusOnlyExtras,
sidebarSecondaryActions,
} from "@/components/gmail/sidebar/sidebar-nav-constants"
import { navRowRoundedWhenActive } from "@/components/gmail/sidebar/sidebar-nav-primitives"
import { CategoryNavRow } from "@/components/gmail/sidebar/category-nav-row"
import { SidebarNavItem } from "@/components/gmail/sidebar/sidebar-nav-item"
import { SidebarLabelItemRow } from "@/components/gmail/sidebar/sidebar-label-item-row"
import {
renderCollapsedFolderList,
renderExpandedFolderSubtree,
} from "@/components/gmail/sidebar/sidebar-folder-tree"
import { MAIL_SIDEBAR_BLUR_SURFACE_CLASS } from "@/lib/mail-chrome-classes"
import type { useSidebarState } from "@/components/gmail/sidebar/use-sidebar-state"
type SidebarState = ReturnType<typeof useSidebarState>
export function SidebarNavPanel({
selectedFolder,
onSelectFolder,
folderUnreadCounts,
splitView = false,
state,
}: {
selectedFolder: string
onSelectFolder: (folder: string) => void
folderUnreadCounts: Record<string, number>
splitView?: boolean
state: SidebarState
}) {
const {
isExpanded,
navRailInset,
touchNav,
visibleMainItems,
primaryVisibleCategories,
plusOnlyVisibleCategories,
disabledSystemNavItems,
navMoreOpen,
setNavMoreOpen,
setLabelRowEnabled,
folderTree,
folderRowProps,
collapsedFolderOpts,
visibleNavLabelRows,
labelRowProps,
setNewFolderParent,
setNewFolderName,
setFolderDialogOpen,
setNewLabelName,
setLabelDialogOpen,
} = state
return (
<div
className={cn(
"min-h-0 flex-1 overflow-y-auto overflow-x-hidden",
"[scrollbar-width:none] [-ms-overflow-style:none] [&::-webkit-scrollbar]:hidden"
)}
>
<nav
className={cn(
"flex min-h-full flex-col",
navRailInset,
!splitView && "sm:pt-3"
)}
>
{visibleMainItems.map((item) => (
<SidebarNavItem
key={item.id}
item={item}
isSelected={selectedFolder === item.id}
unreadCount={folderUnreadCounts[item.id] ?? 0}
isExpanded={isExpanded}
onSelectFolder={onSelectFolder}
/>
))}
{primaryVisibleCategories.map((item) => (
<CategoryNavRow
key={item.id}
item={item}
isSelected={selectedFolder === item.id}
isExpanded={isExpanded}
unreadCount={folderUnreadCounts[item.id] ?? 0}
onSelectFolder={onSelectFolder}
touchNav={touchNav}
onDisableNavLabel={(id) => setLabelRowEnabled(id, false)}
onEnableNavLabel={(id) => setLabelRowEnabled(id, true)}
/>
))}
{hasPlusOnlyExtras && (
<button
type="button"
title={!isExpanded ? (navMoreOpen ? "Moins" : "Plus") : undefined}
aria-expanded={navMoreOpen}
aria-label={
!isExpanded
? navMoreOpen
? "Moins dentrées"
: "Plus dentrées"
: undefined
}
onClick={() =>
setNavMoreOpen((wasOpen) => {
if (!wasOpen) return true
if (CATEGORY_IDS_IN_PLUS_ONLY.has(selectedFolder)) {
onSelectFolder("inbox")
return false
}
return false
})
}
className={cn(
"flex h-8 w-full shrink-0 cursor-pointer items-center gap-4 pl-6 pr-3 text-gray-700 transition-colors hover:bg-mail-nav-hover",
navRowRoundedWhenActive(false)
)}
>
<ChevronDown
className={cn(
"h-5 w-5 shrink-0 transition-transform duration-200",
navMoreOpen && "rotate-180"
)}
/>
{isExpanded && (
<span className="text-sm">{navMoreOpen ? "Moins" : "Plus"}</span>
)}
</button>
)}
{navMoreOpen && (
<>
{plusOnlyVisibleCategories.map((item) => (
<CategoryNavRow
key={item.id}
item={item}
isSelected={selectedFolder === item.id}
isExpanded={isExpanded}
unreadCount={folderUnreadCounts[item.id] ?? 0}
onSelectFolder={onSelectFolder}
touchNav={touchNav}
onDisableNavLabel={(id) => setLabelRowEnabled(id, false)}
onEnableNavLabel={(id) => setLabelRowEnabled(id, true)}
/>
))}
{isExpanded && (
<div className="mt-1 flex flex-col gap-px">
{sidebarSecondaryActions.map((a) => {
const ActionIcon = a.icon
return (
<button
key={a.id}
type="button"
className="flex min-h-8 w-full cursor-pointer items-center gap-2 rounded-md py-1.5 pl-6 pr-3 text-left text-xs text-gray-600 transition-colors hover:bg-gray-50 hover:text-gray-800"
>
<ActionIcon className="h-3.5 w-3.5 shrink-0 opacity-70" aria-hidden />
<span className="min-w-0 leading-snug">{a.label}</span>
</button>
)
})}
</div>
)}
{isExpanded && disabledSystemNavItems.length > 0 && (
<div className="mt-2 pt-2">
<div className="mb-1 pl-6 pr-3 text-[11px] font-medium uppercase tracking-wide text-gray-500">
Désactivées
</div>
{disabledSystemNavItems.map((item) => (
<CategoryNavRow
key={item.id}
item={item}
isSelected={false}
isExpanded={isExpanded}
unreadCount={folderUnreadCounts[item.id] ?? 0}
onSelectFolder={onSelectFolder}
touchNav={touchNav}
onDisableNavLabel={(id) => setLabelRowEnabled(id, false)}
onEnableNavLabel={(id) => setLabelRowEnabled(id, true)}
variant="hidden"
/>
))}
</div>
)}
</>
)}
<div className="mt-3 pt-1">
<div
className={cn(
"sticky top-0 flex h-8 w-full min-w-0 shrink-0 items-center gap-2 pl-6 pr-3",
MAIL_SIDEBAR_BLUR_SURFACE_CLASS
)}
style={{ zIndex: MAIL_SIDEBAR_DOSSIERS_SECTION_STICKY_Z }}
title={!isExpanded ? "Dossiers" : undefined}
>
<Icon
icon={FOLDER_SECTION_ICON}
className="h-5 w-5 shrink-0 text-gray-600"
aria-hidden
/>
{isExpanded && (
<span className="min-w-0 flex-1 truncate text-left text-sm font-medium text-gray-700">
Dossiers
</span>
)}
{isExpanded && (
<button
type="button"
className="flex h-5 w-5 shrink-0 cursor-pointer items-center justify-center rounded-full text-gray-500 hover:bg-mail-nav-hover hover:text-gray-700"
aria-label="Ajouter un dossier"
title="Ajouter un dossier"
onClick={() => {
setNewFolderParent("__root__")
setNewFolderName("")
setFolderDialogOpen(true)
}}
>
<Plus className="h-5 w-5 shrink-0" />
</button>
)}
</div>
{isExpanded
? renderExpandedFolderSubtree(folderTree, 0, folderRowProps)
: renderCollapsedFolderList(folderTree, collapsedFolderOpts)}
</div>
<div className="mt-3 pt-1">
<div
className={cn(
"sticky top-0 z-30 flex h-8 w-full min-w-0 shrink-0 items-center gap-2 pl-6 pr-3",
MAIL_SIDEBAR_BLUR_SURFACE_CLASS
)}
title={!isExpanded ? "Libellés" : undefined}
>
<Icon
icon="mdi:label-outline"
className="h-5 w-5 shrink-0 text-gray-600"
aria-hidden
/>
{isExpanded && (
<span className="min-w-0 flex-1 truncate text-left text-sm font-medium text-gray-700">
Libellés
</span>
)}
{isExpanded && (
<button
type="button"
className="flex h-5 w-5 shrink-0 cursor-pointer items-center justify-center rounded-full text-gray-500 hover:bg-mail-nav-hover hover:text-gray-700"
aria-label="Ajouter un libellé"
title="Ajouter un libellé"
onClick={() => {
setNewLabelName("")
setLabelDialogOpen(true)
}}
>
<Plus className="h-5 w-5 shrink-0" />
</button>
)}
</div>
{visibleNavLabelRows.map((item) => (
<SidebarLabelItemRow
key={item.id}
item={item}
unreadCount={folderUnreadCounts[item.id] ?? 0}
isExpanded={isExpanded}
{...labelRowProps}
/>
))}
</div>
<div
className={cn(
"relative z-[41] mt-auto pt-2",
MAIL_SIDEBAR_BLUR_SURFACE_CLASS,
"max-sm:pb-16 sm:-mr-3.5 sm:w-[calc(100%+0.875rem)] sm:sticky sm:bottom-0 sm:border-t sm:border-gray-200 sm:pb-3"
)}
>
<button
type="button"
title={!isExpanded ? "Sortbot" : undefined}
className={cn(
"flex h-8 w-full shrink-0 cursor-pointer items-center gap-4 pl-6 pr-3 text-sm text-gray-700 transition-colors hover:bg-mail-nav-hover",
navRowRoundedWhenActive(false)
)}
>
<Bot className="h-5 w-5 shrink-0 text-gray-600" aria-hidden />
{isExpanded && <span>Sortbot</span>}
</button>
</div>
</nav>
</div>
)
}