298 lines
9.6 KiB
TypeScript
298 lines
9.6 KiB
TypeScript
"use client"
|
|
|
|
import { useRef, useState } from "react"
|
|
import { Folder, MoreVertical } from "lucide-react"
|
|
import { Icon } from "@iconify/react"
|
|
import { cn, formatCount } from "@/lib/utils"
|
|
import { useEmailDropTarget } from "@/lib/drag-context"
|
|
import { isSystemNavLabelId } from "@/lib/sidebar-nav-data"
|
|
import {
|
|
DropdownMenu,
|
|
DropdownMenuContent,
|
|
DropdownMenuItem,
|
|
DropdownMenuTrigger,
|
|
} from "@/components/ui/dropdown-menu"
|
|
import {
|
|
SidebarNavOptionsSheet,
|
|
SidebarNavSheetAction,
|
|
} from "@/components/gmail/sidebar/sidebar-nav-options-sheet"
|
|
import { useSidebarTouchOptionsMenu } from "@/components/gmail/sidebar/use-sidebar-touch-options"
|
|
import type { CategoryNavSourceItem } from "@/components/gmail/sidebar/sidebar-nav-constants"
|
|
import {
|
|
navRowRoundedWhenActive,
|
|
SidebarNavIconSlot,
|
|
SidebarOverflowColumn,
|
|
sidebarOverflowMenuButtonClass,
|
|
} from "@/components/gmail/sidebar/sidebar-nav-primitives"
|
|
|
|
export function CategoryNavRow({
|
|
item,
|
|
isSelected,
|
|
isExpanded,
|
|
unreadCount,
|
|
onSelectFolder,
|
|
onDisableNavLabel,
|
|
onEnableNavLabel,
|
|
touchNav,
|
|
variant = "listed",
|
|
}: {
|
|
item: CategoryNavSourceItem
|
|
isSelected: boolean
|
|
isExpanded: boolean
|
|
unreadCount: number
|
|
onSelectFolder: (id: string) => void
|
|
onDisableNavLabel: (id: string) => void
|
|
onEnableNavLabel: (id: string) => void
|
|
touchNav: boolean
|
|
variant?: "listed" | "hidden"
|
|
}) {
|
|
const { isOver, dropHandlers } = useEmailDropTarget(item.id, item.label)
|
|
const [menuOpen, setMenuOpen] = useState(false)
|
|
const menuTriggerRef = useRef<HTMLButtonElement>(null)
|
|
const isHiddenRow = variant === "hidden"
|
|
const showCategoryMenu = isSystemNavLabelId(item.id) && isExpanded
|
|
const hasUnread = unreadCount > 0
|
|
const touchMenuEnabled = touchNav && (isHiddenRow || showCategoryMenu)
|
|
const { sheetOpen, setSheetOpen, touchRowProps, touchRowClassName, closeSheet } =
|
|
useSidebarTouchOptionsMenu(touchMenuEnabled)
|
|
|
|
const handleMenuOpenChange = (open: boolean) => {
|
|
setMenuOpen(open)
|
|
if (!open) {
|
|
queueMicrotask(() => menuTriggerRef.current?.blur())
|
|
}
|
|
}
|
|
|
|
const rowHoverHeld =
|
|
!isHiddenRow && !isSelected && !isOver && (menuOpen || sheetOpen)
|
|
|
|
const rowIcon = item.icon ? (
|
|
<Icon
|
|
icon={item.icon}
|
|
className={cn(
|
|
"h-5 w-5 shrink-0",
|
|
isHiddenRow && "opacity-70",
|
|
hasUnread && !isSelected && !isHiddenRow && "text-gray-900"
|
|
)}
|
|
aria-hidden
|
|
/>
|
|
) : (
|
|
<Folder
|
|
className={cn(
|
|
"h-5 w-5 shrink-0",
|
|
isHiddenRow && "opacity-70",
|
|
hasUnread && !isSelected && !isHiddenRow && "text-gray-900"
|
|
)}
|
|
aria-hidden
|
|
/>
|
|
)
|
|
|
|
if (isHiddenRow) {
|
|
return (
|
|
<>
|
|
<div
|
|
{...dropHandlers}
|
|
{...touchRowProps}
|
|
className={cn(
|
|
"flex h-8 w-full min-w-0 shrink-0 items-center pl-6 pr-2 text-gray-500 transition-colors",
|
|
isOver ? "rounded-r-full" : "rounded-r-none",
|
|
isOver && "bg-mail-nav-drop text-foreground",
|
|
touchRowClassName
|
|
)}
|
|
>
|
|
<button
|
|
type="button"
|
|
onClick={() => onSelectFolder(item.id)}
|
|
className="flex h-8 min-w-0 flex-1 items-center gap-4 rounded-r-none py-0 pr-1 text-left outline-none hover:rounded-r-full hover:bg-gray-50"
|
|
>
|
|
{rowIcon}
|
|
<div className="flex min-w-0 flex-1 items-baseline gap-4">
|
|
<span
|
|
className={cn(
|
|
"min-w-0 flex-1 truncate text-sm leading-5",
|
|
hasUnread && "font-semibold text-gray-900"
|
|
)}
|
|
>
|
|
{item.label}
|
|
</span>
|
|
{unreadCount > 0 && (
|
|
<span
|
|
className={cn(
|
|
"shrink-0 text-xs tabular-nums leading-none text-gray-700",
|
|
hasUnread && "font-semibold"
|
|
)}
|
|
>
|
|
{formatCount(unreadCount)}
|
|
</span>
|
|
)}
|
|
</div>
|
|
</button>
|
|
{!touchNav && (
|
|
<DropdownMenu open={menuOpen} onOpenChange={handleMenuOpenChange}>
|
|
<DropdownMenuTrigger asChild>
|
|
<button
|
|
ref={menuTriggerRef}
|
|
type="button"
|
|
className={sidebarOverflowMenuButtonClass}
|
|
aria-label={`Options pour ${item.label}`}
|
|
onClick={(e) => e.stopPropagation()}
|
|
>
|
|
<MoreVertical className="h-4 w-4" />
|
|
</button>
|
|
</DropdownMenuTrigger>
|
|
<DropdownMenuContent align="end" className="min-w-40">
|
|
<DropdownMenuItem
|
|
onClick={() => {
|
|
onEnableNavLabel(item.id)
|
|
setMenuOpen(false)
|
|
}}
|
|
>
|
|
Réactiver le libellé
|
|
</DropdownMenuItem>
|
|
</DropdownMenuContent>
|
|
</DropdownMenu>
|
|
)}
|
|
</div>
|
|
{touchNav && (
|
|
<SidebarNavOptionsSheet
|
|
open={sheetOpen}
|
|
onOpenChange={setSheetOpen}
|
|
title={item.label}
|
|
>
|
|
<SidebarNavSheetAction
|
|
onClick={() => {
|
|
onEnableNavLabel(item.id)
|
|
closeSheet()
|
|
}}
|
|
>
|
|
Réactiver le libellé
|
|
</SidebarNavSheetAction>
|
|
</SidebarNavOptionsSheet>
|
|
)}
|
|
</>
|
|
)
|
|
}
|
|
|
|
return (
|
|
<>
|
|
<div
|
|
{...dropHandlers}
|
|
{...touchRowProps}
|
|
className={cn(
|
|
"group/catnav flex h-8 w-full min-w-0 shrink-0 cursor-pointer items-center pl-6 pr-2 transition-colors",
|
|
navRowRoundedWhenActive(isSelected || isOver || rowHoverHeld),
|
|
isSelected
|
|
? "bg-mail-nav-selected text-mail-nav-selected font-medium"
|
|
: isOver
|
|
? "bg-mail-nav-drop text-foreground"
|
|
: rowHoverHeld
|
|
? "bg-mail-nav-hover text-foreground"
|
|
: hasUnread
|
|
? "text-gray-900 hover:bg-mail-nav-hover"
|
|
: "text-gray-700 hover:bg-mail-nav-hover",
|
|
touchRowClassName
|
|
)}
|
|
>
|
|
<button
|
|
type="button"
|
|
onClick={() => onSelectFolder(item.id)}
|
|
title={!isExpanded ? item.label : undefined}
|
|
className={cn(
|
|
"flex h-8 min-w-0 flex-1 cursor-pointer items-center gap-4 py-0 text-left outline-none",
|
|
showCategoryMenu ? "pr-1" : "pr-3"
|
|
)}
|
|
>
|
|
<SidebarNavIconSlot showUnreadDot={hasUnread}>
|
|
{rowIcon}
|
|
</SidebarNavIconSlot>
|
|
{isExpanded && (
|
|
<div className="flex min-w-0 flex-1 items-baseline gap-4">
|
|
<span
|
|
className={cn(
|
|
"min-w-0 flex-1 truncate text-sm leading-5",
|
|
hasUnread && !isSelected && "font-semibold text-gray-900"
|
|
)}
|
|
>
|
|
{item.label}
|
|
</span>
|
|
{!showCategoryMenu && unreadCount > 0 && (
|
|
<span
|
|
className={cn(
|
|
"shrink-0 text-xs tabular-nums leading-none",
|
|
isSelected && "font-medium",
|
|
hasUnread && !isSelected && "font-semibold"
|
|
)}
|
|
>
|
|
{formatCount(unreadCount)}
|
|
</span>
|
|
)}
|
|
</div>
|
|
)}
|
|
</button>
|
|
{showCategoryMenu && (
|
|
<SidebarOverflowColumn
|
|
unread={unreadCount}
|
|
menuOpen={menuOpen || sheetOpen}
|
|
hoverGroup="catnav"
|
|
isSelected={isSelected}
|
|
hasUnread={hasUnread}
|
|
className="mr-[-7px]"
|
|
showMenuButton={!touchNav}
|
|
>
|
|
{!touchNav && (
|
|
<DropdownMenu open={menuOpen} onOpenChange={handleMenuOpenChange}>
|
|
<DropdownMenuTrigger asChild>
|
|
<button
|
|
ref={menuTriggerRef}
|
|
type="button"
|
|
className={cn(
|
|
sidebarOverflowMenuButtonClass,
|
|
isSelected && "text-gray-900"
|
|
)}
|
|
aria-label={`Options pour ${item.label}`}
|
|
onClick={(e) => {
|
|
e.stopPropagation()
|
|
}}
|
|
>
|
|
<MoreVertical className="h-4 w-4" />
|
|
</button>
|
|
</DropdownMenuTrigger>
|
|
<DropdownMenuContent align="end" className="min-w-40">
|
|
<DropdownMenuItem disabled className="text-gray-400">
|
|
Afficher
|
|
</DropdownMenuItem>
|
|
<DropdownMenuItem
|
|
onClick={() => {
|
|
onDisableNavLabel(item.id)
|
|
setMenuOpen(false)
|
|
}}
|
|
>
|
|
Désactiver le libellé
|
|
</DropdownMenuItem>
|
|
</DropdownMenuContent>
|
|
</DropdownMenu>
|
|
)}
|
|
</SidebarOverflowColumn>
|
|
)}
|
|
</div>
|
|
{touchNav && showCategoryMenu && (
|
|
<SidebarNavOptionsSheet
|
|
open={sheetOpen}
|
|
onOpenChange={setSheetOpen}
|
|
title={item.label}
|
|
>
|
|
<div className="px-4 py-3 text-sm text-muted-foreground">Afficher</div>
|
|
<SidebarNavSheetAction
|
|
onClick={() => {
|
|
onDisableNavLabel(item.id)
|
|
closeSheet()
|
|
}}
|
|
>
|
|
Désactiver le libellé
|
|
</SidebarNavSheetAction>
|
|
</SidebarNavOptionsSheet>
|
|
)}
|
|
</>
|
|
)
|
|
}
|