1345 lines
48 KiB
TypeScript
1345 lines
48 KiB
TypeScript
"use client"
|
||
|
||
import { Icon } from "@iconify/react"
|
||
import {
|
||
Archive,
|
||
ArrowLeft,
|
||
CalendarX2,
|
||
ChevronDown,
|
||
ChevronLeft,
|
||
ChevronRight,
|
||
CheckSquare,
|
||
Clock,
|
||
FolderInput,
|
||
ListTodo,
|
||
Mail,
|
||
MailOpen,
|
||
Menu,
|
||
MoreVertical,
|
||
Paperclip,
|
||
RefreshCw,
|
||
Search,
|
||
Send,
|
||
ShieldAlert,
|
||
SquareArrowOutUpRight,
|
||
Tag,
|
||
Trash2,
|
||
User as UserIcon,
|
||
VolumeX,
|
||
X,
|
||
} from "lucide-react"
|
||
import { Button } from "@/components/ui/button"
|
||
import { Checkbox } from "@/components/ui/checkbox"
|
||
import {
|
||
DropdownMenu,
|
||
DropdownMenuContent,
|
||
DropdownMenuItem,
|
||
DropdownMenuSeparator,
|
||
DropdownMenuSub,
|
||
DropdownMenuSubContent,
|
||
DropdownMenuSubTrigger,
|
||
DropdownMenuTrigger,
|
||
} from "@/components/ui/dropdown-menu"
|
||
import {
|
||
Tooltip,
|
||
TooltipContent,
|
||
TooltipProvider,
|
||
TooltipTrigger,
|
||
} from "@/components/ui/tooltip"
|
||
import { CompactInboxCategoryTabs } from "@/components/gmail/compact-inbox-category-tabs"
|
||
import { EmailLabelPickerBlock } from "@/components/gmail/email-label-picker-block"
|
||
import type { CatalogLabelPresence } from "@/components/gmail/email-label-picker-block"
|
||
import { MailSearchBar } from "@/components/gmail/mail-search-bar"
|
||
import {
|
||
MoveToDropdownItems,
|
||
} from "@/components/gmail/email-list/move-to-menu-items"
|
||
import type { MailMoveTargets } from "@/components/gmail/move-to-menu-items"
|
||
import { cn } from "@/lib/utils"
|
||
import type { Email } from "@/lib/email-data"
|
||
import {
|
||
MAIL_INBOX_CATEGORY_TAB_CONTENT_DARK_CLASS,
|
||
MAIL_MENU_SURFACE_CLASS,
|
||
} from "@/lib/mail-chrome-classes"
|
||
import {
|
||
DATE_RANGE_OPTIONS,
|
||
type SearchParams,
|
||
} from "@/lib/mail-search/search-params"
|
||
import { inboxTabActiveAccentColor } from "@/lib/inbox-category-tabs"
|
||
import { inboxTabShowsInactiveMeta } from "@/lib/mail-url"
|
||
import {
|
||
CATEGORY_TAB_ICON_CLASS,
|
||
inboxTabBadgeCountClass,
|
||
inboxTabBadgeDotClass,
|
||
REFRESH_SPIN_CLASS,
|
||
} from "@/components/gmail/email-list/email-list-helpers"
|
||
import { LIST_PAGE_SIZE } from "@/components/gmail/email-list/email-list-helpers"
|
||
|
||
export type EmailListToolbarProps = {
|
||
isViewMode: boolean
|
||
splitView: boolean
|
||
listToolbarMode: boolean
|
||
compactInboxTabs: boolean
|
||
isSearchMode: boolean
|
||
selectedFolder: string
|
||
mobileFolderLabel: string
|
||
displayListEmails: Email[]
|
||
mobileUnreadCount: number
|
||
mobileSelectionMode: boolean
|
||
setMobileSelectionMode: (v: boolean | ((p: boolean) => boolean)) => void
|
||
setSelectedEmails: (v: string[] | ((p: string[]) => string[])) => void
|
||
mobileXsMoreMenuOpen: boolean
|
||
setMobileXsMoreMenuOpen: (v: boolean) => void
|
||
showBulkToolbar: boolean
|
||
bulkSelectMenuOpen: boolean
|
||
setBulkSelectMenuOpen: (v: boolean) => void
|
||
selectAllChecked: boolean | "indeterminate"
|
||
handleSelectAllChange: (checked: boolean | "indeterminate") => void
|
||
selectMenuAll: () => void
|
||
selectMenuNone: () => void
|
||
selectMenuRead: () => void
|
||
selectMenuUnread: () => void
|
||
selectMenuStarred: () => void
|
||
selectMenuUnstarred: () => void
|
||
bulkArchive: () => void
|
||
bulkDelete: () => void
|
||
bulkSpam: () => void
|
||
hasUnreadInSelection: boolean
|
||
bulkMarkRead: () => void
|
||
bulkMarkUnread: () => void
|
||
moveTargets: MailMoveTargets
|
||
bulkMoveTo: (targetId: string) => void
|
||
labelPickerQuery: string
|
||
setLabelPickerQuery: (q: string) => void
|
||
catalogLabels: string[]
|
||
resolveLabelVisual: (label: string) => ReturnType<typeof import("@/lib/label-picker-visual").resolveLabelPickerVisual>
|
||
bulkTargetIds: string[]
|
||
getCatalogLabelPresence: (ids: string[], catalogLabel: string) => CatalogLabelPresence
|
||
toggleLabelOnEmails: (ids: string[], label: string) => void
|
||
addLabelToEmails: (ids: string[], label: string) => void
|
||
isRefreshing: boolean
|
||
handleManualRefresh: () => void
|
||
markAllInViewAsRead: () => void
|
||
openMobileXsMoveSheet: () => void
|
||
openMobileXsLabelSheet: () => void
|
||
listPage: number
|
||
totalPages: number
|
||
openMailIndex: number
|
||
goListPrevPage: () => void
|
||
goListNextPage: () => void
|
||
goToPrev: () => void
|
||
goToNext: () => void
|
||
goBack: () => void
|
||
openEmail: Email | null
|
||
viewModeIsRead: boolean
|
||
singleArchive: () => void
|
||
singleDelete: () => void
|
||
singleNotSpam: () => void
|
||
singleSpam: () => void
|
||
singleToggleRead: () => void
|
||
singleMoveTo: (targetId: string) => void
|
||
onToggleSidebar?: () => void
|
||
inboxTabBarItems: Array<{ id: string; label: string; icon: string; badgeColor: string }>
|
||
activeInboxTabId: string
|
||
unseenInTabById: Record<string, number>
|
||
tabUnseenSenderLineById: Record<string, string>
|
||
handleCategoryInboxTabClick: (tabId: string) => void
|
||
searchParams: SearchParams | null
|
||
searchAccount: { email: string }
|
||
allEmails: Email[]
|
||
setSearchFilter: (patch: Partial<SearchParams>) => void
|
||
toggleSearchFilter: (key: keyof SearchParams, value: string) => void
|
||
setAdvancedOpen: (open: boolean) => void
|
||
searchRouter: { push: (url: string) => void }
|
||
buildSearchUrl: (params: SearchParams) => string
|
||
variant?: "list" | "reading-pane"
|
||
part?: "mobile" | "list" | "all"
|
||
}
|
||
|
||
export function EmailListToolbar(props: EmailListToolbarProps) {
|
||
const {
|
||
isViewMode,
|
||
splitView,
|
||
listToolbarMode,
|
||
compactInboxTabs,
|
||
isSearchMode,
|
||
selectedFolder,
|
||
mobileFolderLabel,
|
||
displayListEmails,
|
||
mobileUnreadCount,
|
||
mobileSelectionMode,
|
||
setMobileSelectionMode,
|
||
setSelectedEmails,
|
||
mobileXsMoreMenuOpen,
|
||
setMobileXsMoreMenuOpen,
|
||
showBulkToolbar,
|
||
bulkSelectMenuOpen,
|
||
setBulkSelectMenuOpen,
|
||
selectAllChecked,
|
||
handleSelectAllChange,
|
||
selectMenuAll,
|
||
selectMenuNone,
|
||
selectMenuRead,
|
||
selectMenuUnread,
|
||
selectMenuStarred,
|
||
selectMenuUnstarred,
|
||
bulkArchive,
|
||
bulkDelete,
|
||
bulkSpam,
|
||
hasUnreadInSelection,
|
||
bulkMarkRead,
|
||
bulkMarkUnread,
|
||
moveTargets,
|
||
bulkMoveTo,
|
||
labelPickerQuery,
|
||
setLabelPickerQuery,
|
||
catalogLabels,
|
||
resolveLabelVisual,
|
||
bulkTargetIds,
|
||
getCatalogLabelPresence,
|
||
toggleLabelOnEmails,
|
||
addLabelToEmails,
|
||
isRefreshing,
|
||
handleManualRefresh,
|
||
markAllInViewAsRead,
|
||
openMobileXsMoveSheet,
|
||
openMobileXsLabelSheet,
|
||
listPage,
|
||
totalPages,
|
||
openMailIndex,
|
||
goListPrevPage,
|
||
goListNextPage,
|
||
goToPrev,
|
||
goToNext,
|
||
goBack,
|
||
openEmail,
|
||
viewModeIsRead,
|
||
singleArchive,
|
||
singleDelete,
|
||
singleNotSpam,
|
||
singleSpam,
|
||
singleToggleRead,
|
||
singleMoveTo,
|
||
onToggleSidebar,
|
||
inboxTabBarItems,
|
||
activeInboxTabId,
|
||
unseenInTabById,
|
||
tabUnseenSenderLineById,
|
||
handleCategoryInboxTabClick,
|
||
searchParams,
|
||
searchAccount,
|
||
allEmails,
|
||
setSearchFilter,
|
||
toggleSearchFilter,
|
||
setAdvancedOpen,
|
||
searchRouter,
|
||
buildSearchUrl,
|
||
variant = "list",
|
||
part = "all",
|
||
} = props
|
||
|
||
const dropdownSurfaceClass = MAIL_MENU_SURFACE_CLASS
|
||
|
||
const openMailToolbar = (showBack: boolean) => (
|
||
<TooltipProvider delayDuration={400}>
|
||
{showBack ? (
|
||
<Tooltip>
|
||
<TooltipTrigger asChild>
|
||
<Button
|
||
type="button"
|
||
variant="ghost"
|
||
size="icon"
|
||
className="h-9 w-9 shrink-0 text-[#444746] hover:bg-[#f1f3f4]"
|
||
aria-label="Retour à la boîte de réception"
|
||
onClick={goBack}
|
||
>
|
||
<ArrowLeft className="h-[18px] w-[18px]" strokeWidth={1.5} />
|
||
</Button>
|
||
</TooltipTrigger>
|
||
<TooltipContent side="bottom" className="text-xs">
|
||
Retour à la boîte de réception
|
||
</TooltipContent>
|
||
</Tooltip>
|
||
) : null}
|
||
|
||
<div className="flex min-w-0 flex-wrap items-center gap-0.5 pl-1">
|
||
{openEmail?.spam === true ? (
|
||
<>
|
||
<div className="flex min-w-0 shrink-0 flex-wrap items-center gap-0.5">
|
||
<Button
|
||
type="button"
|
||
variant="ghost"
|
||
size="sm"
|
||
className="h-9 shrink-0 px-2.5 text-sm font-medium text-[#444746] hover:bg-[#f1f3f4]"
|
||
onClick={singleDelete}
|
||
>
|
||
Supprimer définitivement
|
||
</Button>
|
||
<Button
|
||
type="button"
|
||
variant="ghost"
|
||
size="sm"
|
||
className="h-9 shrink-0 px-2.5 text-sm font-medium text-[#444746] hover:bg-[#f1f3f4]"
|
||
onClick={singleNotSpam}
|
||
>
|
||
Non-spam
|
||
</Button>
|
||
</div>
|
||
|
||
<span className="mx-1 h-6 w-px shrink-0 bg-[#dadce0]" aria-hidden />
|
||
|
||
<div className="flex shrink-0 items-center gap-0.5">
|
||
<Tooltip>
|
||
<TooltipTrigger asChild>
|
||
<Button
|
||
type="button"
|
||
variant="ghost"
|
||
size="icon"
|
||
className="h-9 w-9 shrink-0 text-[#444746] hover:bg-[#f1f3f4]"
|
||
aria-label="Archiver"
|
||
onClick={singleArchive}
|
||
>
|
||
<Archive className="h-[18px] w-[18px]" strokeWidth={1.5} />
|
||
</Button>
|
||
</TooltipTrigger>
|
||
<TooltipContent side="bottom" className="text-xs">
|
||
Archiver
|
||
</TooltipContent>
|
||
</Tooltip>
|
||
</div>
|
||
|
||
<span className="mx-1 h-6 w-px shrink-0 bg-[#dadce0]" aria-hidden />
|
||
|
||
<div className="flex min-w-0 shrink-0 items-center gap-0.5">
|
||
<Tooltip>
|
||
<TooltipTrigger asChild>
|
||
<Button
|
||
type="button"
|
||
variant="ghost"
|
||
size="icon"
|
||
className="h-9 w-9 shrink-0 text-[#444746] hover:bg-[#f1f3f4]"
|
||
aria-label={
|
||
viewModeIsRead ? "Marquer comme non lu" : "Marquer comme lu"
|
||
}
|
||
onClick={singleToggleRead}
|
||
>
|
||
{viewModeIsRead ? (
|
||
<Mail className="h-[18px] w-[18px]" strokeWidth={1.5} />
|
||
) : (
|
||
<MailOpen className="h-[18px] w-[18px]" strokeWidth={1.5} />
|
||
)}
|
||
</Button>
|
||
</TooltipTrigger>
|
||
<TooltipContent side="bottom" className="text-xs">
|
||
{viewModeIsRead ? "Marquer comme non lu" : "Marquer comme lu"}
|
||
</TooltipContent>
|
||
</Tooltip>
|
||
|
||
<DropdownMenu>
|
||
<DropdownMenuTrigger asChild>
|
||
<Button
|
||
type="button"
|
||
variant="ghost"
|
||
size="sm"
|
||
className="h-9 w-9 shrink-0 gap-1 px-0 text-[#444746] hover:bg-[#f1f3f4] lg:h-9 lg:w-auto lg:px-2"
|
||
aria-label="Déplacer vers"
|
||
>
|
||
<FolderInput
|
||
className="h-[18px] w-[18px] shrink-0"
|
||
strokeWidth={1.5}
|
||
/>
|
||
<span className="hidden max-w-32 truncate lg:inline">
|
||
Déplacer vers
|
||
</span>
|
||
<ChevronDown className="hidden h-3.5 w-3.5 shrink-0 opacity-70 lg:block" />
|
||
</Button>
|
||
</DropdownMenuTrigger>
|
||
<DropdownMenuContent
|
||
align="start"
|
||
className={cn(dropdownSurfaceClass, "max-h-80 overflow-y-auto")}
|
||
>
|
||
<MoveToDropdownItems targets={moveTargets} onMoveTo={singleMoveTo} />
|
||
</DropdownMenuContent>
|
||
</DropdownMenu>
|
||
</div>
|
||
</>
|
||
) : (
|
||
<>
|
||
<div className="flex shrink-0 items-center gap-0.5">
|
||
<Tooltip>
|
||
<TooltipTrigger asChild>
|
||
<Button
|
||
type="button"
|
||
variant="ghost"
|
||
size="icon"
|
||
className="h-9 w-9 shrink-0 text-[#444746] hover:bg-[#f1f3f4]"
|
||
aria-label="Archiver"
|
||
onClick={singleArchive}
|
||
>
|
||
<Archive className="h-[18px] w-[18px]" strokeWidth={1.5} />
|
||
</Button>
|
||
</TooltipTrigger>
|
||
<TooltipContent side="bottom" className="text-xs">
|
||
Archiver
|
||
</TooltipContent>
|
||
</Tooltip>
|
||
<Tooltip>
|
||
<TooltipTrigger asChild>
|
||
<Button
|
||
type="button"
|
||
variant="ghost"
|
||
size="icon"
|
||
className="h-9 w-9 shrink-0 text-[#444746] hover:bg-[#f1f3f4]"
|
||
aria-label="Signaler comme spam"
|
||
onClick={singleSpam}
|
||
>
|
||
<ShieldAlert className="h-[18px] w-[18px]" strokeWidth={1.5} />
|
||
</Button>
|
||
</TooltipTrigger>
|
||
<TooltipContent side="bottom" className="text-xs">
|
||
Signaler comme spam
|
||
</TooltipContent>
|
||
</Tooltip>
|
||
<Tooltip>
|
||
<TooltipTrigger asChild>
|
||
<Button
|
||
type="button"
|
||
variant="ghost"
|
||
size="icon"
|
||
className="h-9 w-9 shrink-0 text-[#444746] hover:bg-[#f1f3f4]"
|
||
aria-label="Supprimer"
|
||
onClick={singleDelete}
|
||
>
|
||
<Trash2 className="h-[18px] w-[18px]" strokeWidth={1.5} />
|
||
</Button>
|
||
</TooltipTrigger>
|
||
<TooltipContent side="bottom" className="text-xs">
|
||
Supprimer
|
||
</TooltipContent>
|
||
</Tooltip>
|
||
</div>
|
||
|
||
<span className="mx-1 h-6 w-px shrink-0 bg-[#dadce0]" aria-hidden />
|
||
|
||
<div className="flex min-w-0 shrink-0 items-center gap-0.5">
|
||
<Tooltip>
|
||
<TooltipTrigger asChild>
|
||
<Button
|
||
type="button"
|
||
variant="ghost"
|
||
size="icon"
|
||
className="h-9 w-9 shrink-0 text-[#444746] hover:bg-[#f1f3f4]"
|
||
aria-label={
|
||
viewModeIsRead ? "Marquer comme non lu" : "Marquer comme lu"
|
||
}
|
||
onClick={singleToggleRead}
|
||
>
|
||
{viewModeIsRead ? (
|
||
<Mail className="h-[18px] w-[18px]" strokeWidth={1.5} />
|
||
) : (
|
||
<MailOpen className="h-[18px] w-[18px]" strokeWidth={1.5} />
|
||
)}
|
||
</Button>
|
||
</TooltipTrigger>
|
||
<TooltipContent side="bottom" className="text-xs">
|
||
{viewModeIsRead ? "Marquer comme non lu" : "Marquer comme lu"}
|
||
</TooltipContent>
|
||
</Tooltip>
|
||
|
||
<DropdownMenu>
|
||
<DropdownMenuTrigger asChild>
|
||
<Button
|
||
type="button"
|
||
variant="ghost"
|
||
size="sm"
|
||
className="h-9 w-9 shrink-0 gap-1 px-0 text-[#444746] hover:bg-[#f1f3f4] lg:h-9 lg:w-auto lg:px-2"
|
||
aria-label="Déplacer vers"
|
||
>
|
||
<FolderInput
|
||
className="h-[18px] w-[18px] shrink-0"
|
||
strokeWidth={1.5}
|
||
/>
|
||
<span className="hidden max-w-32 truncate lg:inline">
|
||
Déplacer vers
|
||
</span>
|
||
<ChevronDown className="hidden h-3.5 w-3.5 shrink-0 opacity-70 lg:block" />
|
||
</Button>
|
||
</DropdownMenuTrigger>
|
||
<DropdownMenuContent
|
||
align="start"
|
||
className={cn(dropdownSurfaceClass, "max-h-80 overflow-y-auto")}
|
||
>
|
||
<MoveToDropdownItems targets={moveTargets} onMoveTo={singleMoveTo} />
|
||
</DropdownMenuContent>
|
||
</DropdownMenu>
|
||
</div>
|
||
</>
|
||
)}
|
||
</div>
|
||
</TooltipProvider>
|
||
)
|
||
|
||
const mailPaginationControls = (mode: "list" | "view") => (
|
||
<div
|
||
className={cn(
|
||
"flex shrink-0 items-center gap-2 whitespace-nowrap text-sm text-gray-600",
|
||
mode === "list" && "max-sm:hidden sm:flex"
|
||
)}
|
||
>
|
||
{displayListEmails.length === 0 ? (
|
||
<span>Aucun résultat</span>
|
||
) : mode === "view" ? (
|
||
<span className="hidden sm:inline">
|
||
{openMailIndex >= 0 ? openMailIndex + 1 : "–"} sur {displayListEmails.length}
|
||
</span>
|
||
) : (
|
||
<span>
|
||
{(listPage - 1) * LIST_PAGE_SIZE + 1}–
|
||
{Math.min(listPage * LIST_PAGE_SIZE, displayListEmails.length)} sur{" "}
|
||
{displayListEmails.length}
|
||
{totalPages > 1 ? ` · p. ${listPage}/${totalPages}` : null}
|
||
</span>
|
||
)}
|
||
<Tooltip>
|
||
<TooltipTrigger asChild>
|
||
<Button
|
||
variant="ghost"
|
||
size="icon"
|
||
className={cn(
|
||
"h-9 w-9",
|
||
mode === "view" && openMailIndex > 0
|
||
? "text-gray-600"
|
||
: mode === "list" && listPage > 1
|
||
? "text-gray-600"
|
||
: "text-gray-400"
|
||
)}
|
||
disabled={mode === "view" ? openMailIndex <= 0 : listPage <= 1}
|
||
onClick={mode === "view" ? goToPrev : goListPrevPage}
|
||
aria-label={mode === "view" ? "Plus récent" : "Page précédente"}
|
||
>
|
||
<ChevronLeft className="h-4 w-4" />
|
||
</Button>
|
||
</TooltipTrigger>
|
||
<TooltipContent side="bottom" className="text-xs">
|
||
{mode === "view" ? "Plus récent" : "Page précédente"}
|
||
</TooltipContent>
|
||
</Tooltip>
|
||
<Tooltip>
|
||
<TooltipTrigger asChild>
|
||
<Button
|
||
variant="ghost"
|
||
size="icon"
|
||
className={cn(
|
||
"h-9 w-9",
|
||
mode === "view" && openMailIndex < displayListEmails.length - 1
|
||
? "text-gray-600"
|
||
: mode === "list" && listPage < totalPages
|
||
? "text-gray-600"
|
||
: "text-gray-400"
|
||
)}
|
||
disabled={
|
||
mode === "view"
|
||
? openMailIndex >= displayListEmails.length - 1
|
||
: listPage >= totalPages
|
||
}
|
||
onClick={mode === "view" ? goToNext : goListNextPage}
|
||
aria-label={mode === "view" ? "Plus ancien" : "Page suivante"}
|
||
>
|
||
<ChevronRight className="h-4 w-4" />
|
||
</Button>
|
||
</TooltipTrigger>
|
||
<TooltipContent side="bottom" className="text-xs">
|
||
{mode === "view" ? "Plus ancien" : "Page suivante"}
|
||
</TooltipContent>
|
||
</Tooltip>
|
||
</div>
|
||
)
|
||
|
||
if (variant === "reading-pane") {
|
||
return (
|
||
<div className="relative z-20 flex shrink-0 min-h-12 items-start gap-2 border-b border-gray-200 py-1.5 pl-2 pr-4">
|
||
{openMailToolbar(false)}
|
||
<div className="flex-1" />
|
||
{mailPaginationControls("view")}
|
||
</div>
|
||
)
|
||
}
|
||
|
||
return (
|
||
<>
|
||
{part !== "list" && !isViewMode && (
|
||
<div className="relative z-20 flex shrink-0 items-center gap-2 border-b border-border bg-mail-surface px-4 py-2.5 sm:hidden">
|
||
<div className="min-w-0 flex-1">
|
||
<h1 className="truncate text-base font-semibold text-[#1f1f1f] leading-tight">
|
||
{mobileFolderLabel}
|
||
</h1>
|
||
<p className="text-xs text-[#5f6368] leading-snug">
|
||
{displayListEmails.length} message{displayListEmails.length !== 1 ? "s" : ""}
|
||
{mobileUnreadCount > 0 && ` · ${mobileUnreadCount} non lu${mobileUnreadCount !== 1 ? "s" : ""}`}
|
||
</p>
|
||
</div>
|
||
<Button
|
||
type="button"
|
||
variant="ghost"
|
||
size={mobileSelectionMode ? "icon" : "sm"}
|
||
className={cn(
|
||
"shrink-0 text-[#444746]",
|
||
mobileSelectionMode
|
||
? "size-9 rounded-full border border-gray-200 bg-white/80 shadow-md backdrop-blur hover:bg-white"
|
||
: "h-9 min-h-9 gap-1.5 rounded-full border border-gray-200 bg-white/80 px-3 text-xs font-medium shadow-md backdrop-blur hover:bg-white"
|
||
)}
|
||
onClick={() => {
|
||
setMobileSelectionMode((p) => !p)
|
||
if (mobileSelectionMode) setSelectedEmails([])
|
||
}}
|
||
aria-label={mobileSelectionMode ? "Annuler la sélection" : "Sélection"}
|
||
>
|
||
{mobileSelectionMode ? (
|
||
<X className="size-[18px]" strokeWidth={1.5} />
|
||
) : (
|
||
<>
|
||
<CheckSquare className="size-4" strokeWidth={1.5} />
|
||
<span>Sélection</span>
|
||
</>
|
||
)}
|
||
</Button>
|
||
<DropdownMenu
|
||
open={mobileXsMoreMenuOpen}
|
||
onOpenChange={setMobileXsMoreMenuOpen}
|
||
>
|
||
<DropdownMenuTrigger asChild>
|
||
<Button
|
||
type="button"
|
||
variant="ghost"
|
||
size="icon"
|
||
className="size-9 shrink-0 rounded-full border border-gray-200 bg-white/80 text-[#444746] shadow-md backdrop-blur hover:bg-white"
|
||
aria-label="Plus d'actions"
|
||
>
|
||
<MoreVertical className="size-[18px]" strokeWidth={1.5} />
|
||
</Button>
|
||
</DropdownMenuTrigger>
|
||
<DropdownMenuContent
|
||
align="end"
|
||
sideOffset={4}
|
||
className={cn(dropdownSurfaceClass, "min-w-[260px]")}
|
||
>
|
||
{showBulkToolbar ? (
|
||
<>
|
||
<DropdownMenuItem onSelect={bulkArchive}>
|
||
<Archive className="size-[18px] text-[#5f6368]" strokeWidth={1.5} />
|
||
Archiver
|
||
</DropdownMenuItem>
|
||
<DropdownMenuItem onSelect={bulkDelete}>
|
||
<Trash2 className="size-[18px] text-[#5f6368]" strokeWidth={1.5} />
|
||
Supprimer
|
||
</DropdownMenuItem>
|
||
<DropdownMenuItem onSelect={bulkSpam}>
|
||
<ShieldAlert className="size-[18px] text-[#5f6368]" strokeWidth={1.5} />
|
||
Signaler comme spam
|
||
</DropdownMenuItem>
|
||
<DropdownMenuItem
|
||
onSelect={hasUnreadInSelection ? bulkMarkRead : bulkMarkUnread}
|
||
>
|
||
{hasUnreadInSelection ? (
|
||
<>
|
||
<MailOpen className="size-[18px] text-[#5f6368]" strokeWidth={1.5} />
|
||
Marquer comme lu
|
||
</>
|
||
) : (
|
||
<>
|
||
<Mail className="size-[18px] text-[#5f6368]" strokeWidth={1.5} />
|
||
Marquer comme non lu
|
||
</>
|
||
)}
|
||
</DropdownMenuItem>
|
||
<DropdownMenuSeparator />
|
||
<DropdownMenuItem
|
||
onSelect={(e) => {
|
||
e.preventDefault()
|
||
openMobileXsMoveSheet()
|
||
}}
|
||
>
|
||
<FolderInput className="size-[18px] text-[#5f6368]" strokeWidth={1.5} />
|
||
<span className="flex-1">Déplacer vers</span>
|
||
<ChevronRight className="ml-auto size-4 text-[#5f6368]" strokeWidth={1.5} />
|
||
</DropdownMenuItem>
|
||
<DropdownMenuItem
|
||
onSelect={(e) => {
|
||
e.preventDefault()
|
||
openMobileXsLabelSheet()
|
||
}}
|
||
>
|
||
<Tag className="size-[18px] text-[#5f6368]" strokeWidth={1.5} />
|
||
<span className="flex-1">Ajouter le libellé</span>
|
||
<ChevronRight className="ml-auto size-4 text-[#5f6368]" strokeWidth={1.5} />
|
||
</DropdownMenuItem>
|
||
<DropdownMenuItem>
|
||
<VolumeX className="size-[18px] text-[#5f6368]" strokeWidth={1.5} />
|
||
Ignorer la conversation
|
||
</DropdownMenuItem>
|
||
</>
|
||
) : (
|
||
<>
|
||
<DropdownMenuItem onSelect={markAllInViewAsRead}>
|
||
<MailOpen className="size-[18px] text-[#5f6368]" strokeWidth={1.5} />
|
||
Tout marquer comme lu
|
||
</DropdownMenuItem>
|
||
<DropdownMenuSeparator />
|
||
<div
|
||
className="px-3 py-2 text-sm leading-snug text-[#5f6368] select-none"
|
||
role="note"
|
||
>
|
||
Sélectionnez des messages pour plus d'actions
|
||
</div>
|
||
</>
|
||
)}
|
||
</DropdownMenuContent>
|
||
</DropdownMenu>
|
||
</div>
|
||
)}
|
||
{part !== "mobile" && (
|
||
<>
|
||
{splitView ? (
|
||
<div className="flex max-sm:hidden shrink-0 items-center gap-2 border-b border-border bg-mail-surface px-2 py-2">
|
||
{onToggleSidebar ? (
|
||
<Button
|
||
type="button"
|
||
variant="ghost"
|
||
size="icon"
|
||
className="size-9 shrink-0 text-[#444746] hover:bg-[#f1f3f4]"
|
||
aria-label="Ouvrir le menu"
|
||
onClick={onToggleSidebar}
|
||
>
|
||
<Menu className="size-5" strokeWidth={1.5} />
|
||
</Button>
|
||
) : null}
|
||
<MailSearchBar compact className="min-w-0 flex-1" />
|
||
</div>
|
||
) : null}
|
||
{/* Toolbar — relative: scroll lives in sibling below */}
|
||
<div
|
||
className={cn(
|
||
"relative z-20 flex shrink-0 min-h-12 gap-2 border-b border-border bg-mail-surface py-1.5 pl-2 pr-4",
|
||
splitView ? "rounded-none" : "sm:rounded-t-2xl",
|
||
isViewMode ? "items-start" : "items-center",
|
||
(isViewMode ? !listToolbarMode : true) && "max-sm:hidden"
|
||
)}
|
||
>
|
||
|
||
{!splitView && isViewMode ? (
|
||
openMailToolbar(true)
|
||
) : (
|
||
/* ── LIST MODE TOOLBAR (original) ── */
|
||
<>
|
||
<DropdownMenu
|
||
open={bulkSelectMenuOpen}
|
||
onOpenChange={setBulkSelectMenuOpen}
|
||
>
|
||
<div
|
||
className={cn(
|
||
"flex items-center overflow-hidden rounded-md border pr-0 transition-[background-color,box-shadow,border-color]",
|
||
bulkSelectMenuOpen
|
||
? "border-[#dadce0] bg-[#f1f3f4] shadow-sm"
|
||
: "border-transparent"
|
||
)}
|
||
>
|
||
<div className="flex h-9 shrink-0 items-center pl-1 pr-0.5 md:pl-0">
|
||
<Checkbox
|
||
checked={selectAllChecked}
|
||
onCheckedChange={handleSelectAllChange}
|
||
className="size-4 min-h-4 min-w-4 shrink-0 rounded-[2.5px] border-[1.5px] border-[#c2c2c2] bg-transparent shadow-none dark:bg-transparent focus-visible:ring-[#c2c2c2]/30 data-[state=checked]:border-[#1a73e8] data-[state=checked]:bg-[#1a73e8] data-[state=checked]:text-white data-[state=indeterminate]:border-[#1a73e8] data-[state=indeterminate]:bg-[#1a73e8] data-[state=indeterminate]:text-white"
|
||
/>
|
||
</div>
|
||
<DropdownMenuTrigger asChild>
|
||
<Button
|
||
type="button"
|
||
variant="ghost"
|
||
size="icon"
|
||
className={cn(
|
||
"h-9 w-7 shrink-0 rounded-none p-0 text-[#5f6368]",
|
||
bulkSelectMenuOpen
|
||
? "border-l border-[#dadce0] hover:bg-[#e8eaed]"
|
||
: "hover:bg-[#f1f3f4]"
|
||
)}
|
||
aria-label="Options de sélection"
|
||
>
|
||
<ChevronDown className="h-4 w-4" />
|
||
</Button>
|
||
</DropdownMenuTrigger>
|
||
</div>
|
||
<DropdownMenuContent
|
||
align="start"
|
||
sideOffset={4}
|
||
className={cn(dropdownSurfaceClass, "min-w-[180px]")}
|
||
>
|
||
<DropdownMenuItem onSelect={selectMenuAll}>Tous</DropdownMenuItem>
|
||
<DropdownMenuItem onSelect={selectMenuNone}>Aucun</DropdownMenuItem>
|
||
<DropdownMenuItem onSelect={selectMenuRead}>Lus</DropdownMenuItem>
|
||
<DropdownMenuItem onSelect={selectMenuUnread}>Non lus</DropdownMenuItem>
|
||
<DropdownMenuItem onSelect={selectMenuStarred}>Suivis</DropdownMenuItem>
|
||
<DropdownMenuItem onSelect={selectMenuUnstarred}>
|
||
Non suivis
|
||
</DropdownMenuItem>
|
||
</DropdownMenuContent>
|
||
</DropdownMenu>
|
||
|
||
{showBulkToolbar ? (
|
||
<TooltipProvider delayDuration={400}>
|
||
<div className="flex min-w-0 items-center gap-0.5 pl-1">
|
||
<div className="flex shrink-0 items-center gap-0.5">
|
||
<Tooltip>
|
||
<TooltipTrigger asChild>
|
||
<Button
|
||
type="button"
|
||
variant="ghost"
|
||
size="icon"
|
||
className="h-9 w-9 shrink-0 text-[#444746] hover:bg-[#f1f3f4]"
|
||
aria-label="Archiver"
|
||
onClick={bulkArchive}
|
||
>
|
||
<Archive className="h-[18px] w-[18px]" strokeWidth={1.5} />
|
||
</Button>
|
||
</TooltipTrigger>
|
||
<TooltipContent side="bottom" className="text-xs">
|
||
Archiver
|
||
</TooltipContent>
|
||
</Tooltip>
|
||
<Tooltip>
|
||
<TooltipTrigger asChild>
|
||
<Button
|
||
type="button"
|
||
variant="ghost"
|
||
size="icon"
|
||
className="h-9 w-9 shrink-0 text-[#444746] hover:bg-[#f1f3f4]"
|
||
aria-label="Signaler comme spam"
|
||
onClick={bulkSpam}
|
||
>
|
||
<ShieldAlert className="h-[18px] w-[18px]" strokeWidth={1.5} />
|
||
</Button>
|
||
</TooltipTrigger>
|
||
<TooltipContent side="bottom" className="text-xs">
|
||
Signaler comme spam
|
||
</TooltipContent>
|
||
</Tooltip>
|
||
<Tooltip>
|
||
<TooltipTrigger asChild>
|
||
<Button
|
||
type="button"
|
||
variant="ghost"
|
||
size="icon"
|
||
className="h-9 w-9 shrink-0 text-[#444746] hover:bg-[#f1f3f4]"
|
||
aria-label="Supprimer"
|
||
onClick={bulkDelete}
|
||
>
|
||
<Trash2 className="h-[18px] w-[18px]" strokeWidth={1.5} />
|
||
</Button>
|
||
</TooltipTrigger>
|
||
<TooltipContent side="bottom" className="text-xs">
|
||
Supprimer
|
||
</TooltipContent>
|
||
</Tooltip>
|
||
</div>
|
||
|
||
<span
|
||
className="mx-1 h-6 w-px shrink-0 bg-[#dadce0]"
|
||
aria-hidden
|
||
/>
|
||
|
||
<div className="flex min-w-0 shrink-0 items-center gap-0.5">
|
||
<Tooltip>
|
||
<TooltipTrigger asChild>
|
||
<Button
|
||
type="button"
|
||
variant="ghost"
|
||
size="icon"
|
||
className="h-9 w-9 shrink-0 text-[#444746] hover:bg-[#f1f3f4]"
|
||
aria-label={
|
||
hasUnreadInSelection
|
||
? "Marquer comme lu"
|
||
: "Marquer comme non lu"
|
||
}
|
||
onClick={() =>
|
||
hasUnreadInSelection ? bulkMarkRead() : bulkMarkUnread()
|
||
}
|
||
>
|
||
{hasUnreadInSelection ? (
|
||
<MailOpen className="h-[18px] w-[18px]" strokeWidth={1.5} />
|
||
) : (
|
||
<Mail className="h-[18px] w-[18px]" strokeWidth={1.5} />
|
||
)}
|
||
</Button>
|
||
</TooltipTrigger>
|
||
<TooltipContent side="bottom" className="text-xs">
|
||
{hasUnreadInSelection
|
||
? "Marquer comme lu"
|
||
: "Marquer comme non lu"}
|
||
</TooltipContent>
|
||
</Tooltip>
|
||
|
||
<DropdownMenu>
|
||
<DropdownMenuTrigger asChild>
|
||
<Button
|
||
type="button"
|
||
variant="ghost"
|
||
size="sm"
|
||
className="h-9 w-9 shrink-0 gap-1 px-0 text-[#444746] hover:bg-[#f1f3f4] lg:h-9 lg:w-auto lg:px-2"
|
||
aria-label="Déplacer vers"
|
||
>
|
||
<FolderInput className="h-[18px] w-[18px] shrink-0" strokeWidth={1.5} />
|
||
<span className="hidden max-w-32 truncate lg:inline">
|
||
Déplacer vers
|
||
</span>
|
||
<ChevronDown className="hidden h-3.5 w-3.5 shrink-0 opacity-70 lg:block" />
|
||
</Button>
|
||
</DropdownMenuTrigger>
|
||
<DropdownMenuContent
|
||
align="start"
|
||
className={cn(dropdownSurfaceClass, "max-h-80 overflow-y-auto")}
|
||
>
|
||
<MoveToDropdownItems targets={moveTargets} onMoveTo={bulkMoveTo} />
|
||
</DropdownMenuContent>
|
||
</DropdownMenu>
|
||
</div>
|
||
|
||
<span
|
||
className="mx-1 h-6 w-px shrink-0 bg-[#dadce0]"
|
||
aria-hidden
|
||
/>
|
||
|
||
<DropdownMenu
|
||
onOpenChange={(open) => {
|
||
if (!open) setLabelPickerQuery("")
|
||
}}
|
||
>
|
||
<DropdownMenuTrigger asChild>
|
||
<Button
|
||
type="button"
|
||
variant="ghost"
|
||
size="icon"
|
||
className="h-9 w-9 shrink-0 text-[#444746] hover:bg-[#f1f3f4]"
|
||
aria-label="Plus d'actions"
|
||
>
|
||
<MoreVertical className="h-[18px] w-[18px]" strokeWidth={1.5} />
|
||
</Button>
|
||
</DropdownMenuTrigger>
|
||
<DropdownMenuContent
|
||
align="start"
|
||
className={cn(
|
||
dropdownSurfaceClass,
|
||
/* Sous-menus Radix restent dans ce nœud : overflow-auto les clippe */
|
||
"overflow-visible"
|
||
)}
|
||
>
|
||
<DropdownMenuItem>
|
||
<Clock className="size-[18px] text-[#5f6368]" strokeWidth={1.5} />
|
||
Mettre en attente
|
||
</DropdownMenuItem>
|
||
<DropdownMenuItem>
|
||
<ListTodo className="size-[18px] text-[#5f6368]" strokeWidth={1.5} />
|
||
Ajouter à Tasks
|
||
</DropdownMenuItem>
|
||
<DropdownMenuSeparator />
|
||
<DropdownMenuSub>
|
||
<DropdownMenuSubTrigger className="[&>svg:last-child]:text-[#5f6368]">
|
||
<Tag className="size-[18px] text-[#5f6368]" strokeWidth={1.5} />
|
||
Ajouter le libellé
|
||
</DropdownMenuSubTrigger>
|
||
<DropdownMenuSubContent
|
||
className={cn(
|
||
dropdownSurfaceClass,
|
||
"z-[100] flex max-h-72 min-w-[260px] flex-col overflow-hidden p-0 py-0"
|
||
)}
|
||
>
|
||
<EmailLabelPickerBlock
|
||
query={labelPickerQuery}
|
||
onQueryChange={setLabelPickerQuery}
|
||
catalogLabels={catalogLabels}
|
||
resolveLabelVisual={resolveLabelVisual}
|
||
Item={DropdownMenuItem}
|
||
getLabelPresence={(lab) =>
|
||
getCatalogLabelPresence(bulkTargetIds, lab)
|
||
}
|
||
onToggleCatalogLabel={(lab) =>
|
||
toggleLabelOnEmails(bulkTargetIds, lab)
|
||
}
|
||
onCreateLabel={(lab) => {
|
||
addLabelToEmails(bulkTargetIds, lab)
|
||
setLabelPickerQuery("")
|
||
}}
|
||
/>
|
||
</DropdownMenuSubContent>
|
||
</DropdownMenuSub>
|
||
<DropdownMenuItem>
|
||
<VolumeX className="size-[18px] text-[#5f6368]" strokeWidth={1.5} />
|
||
Ignorer la conversation
|
||
</DropdownMenuItem>
|
||
<DropdownMenuSeparator />
|
||
<DropdownMenuItem>
|
||
<SquareArrowOutUpRight
|
||
className="size-[18px] text-[#5f6368]"
|
||
strokeWidth={1.5}
|
||
/>
|
||
Ouvrir dans une nouvelle fenêtre
|
||
</DropdownMenuItem>
|
||
</DropdownMenuContent>
|
||
</DropdownMenu>
|
||
</div>
|
||
</TooltipProvider>
|
||
) : (
|
||
<>
|
||
<Button
|
||
type="button"
|
||
variant="ghost"
|
||
size="icon"
|
||
className="hidden h-9 w-9 text-gray-600 sm:inline-flex"
|
||
aria-label="Rafraîchir"
|
||
aria-busy={isRefreshing}
|
||
disabled={isRefreshing}
|
||
onClick={() => void handleManualRefresh()}
|
||
>
|
||
<RefreshCw
|
||
className={cn(
|
||
"h-4 w-4",
|
||
isRefreshing && REFRESH_SPIN_CLASS
|
||
)}
|
||
/>
|
||
</Button>
|
||
<DropdownMenu>
|
||
<DropdownMenuTrigger asChild>
|
||
<Button
|
||
type="button"
|
||
variant="ghost"
|
||
size="icon"
|
||
className="h-9 w-9 shrink-0 text-[#444746] hover:bg-[#f1f3f4]"
|
||
aria-label="Plus d'actions"
|
||
>
|
||
<MoreVertical
|
||
className="h-[18px] w-[18px]"
|
||
strokeWidth={1.5}
|
||
/>
|
||
</Button>
|
||
</DropdownMenuTrigger>
|
||
<DropdownMenuContent
|
||
align="start"
|
||
sideOffset={4}
|
||
className={cn(dropdownSurfaceClass, "min-w-[260px]")}
|
||
>
|
||
<DropdownMenuItem onSelect={markAllInViewAsRead}>
|
||
<MailOpen
|
||
className="size-[18px] text-[#5f6368]"
|
||
strokeWidth={1.5}
|
||
/>
|
||
Tout marquer comme lu
|
||
</DropdownMenuItem>
|
||
<DropdownMenuSeparator />
|
||
<div
|
||
className="px-3 py-2 text-sm leading-snug text-[#5f6368] select-none"
|
||
role="note"
|
||
>
|
||
Sélectionnez des messages pour afficher plus d'actions
|
||
</div>
|
||
</DropdownMenuContent>
|
||
</DropdownMenu>
|
||
</>
|
||
)}
|
||
</>
|
||
)}
|
||
|
||
<div className="flex-1" />
|
||
|
||
{listToolbarMode ? mailPaginationControls("list") : null}
|
||
{!splitView && !listToolbarMode ? mailPaginationControls("view") : null}
|
||
</div>
|
||
|
||
{selectedFolder === "inbox" && (
|
||
<div className="relative z-10 w-full shrink-0 bg-mail-surface after:pointer-events-none after:absolute after:inset-x-0 after:bottom-0 after:z-0 after:h-px after:bg-border">
|
||
{listToolbarMode &&
|
||
(compactInboxTabs ? (
|
||
<CompactInboxCategoryTabs
|
||
tabs={inboxTabBarItems}
|
||
activeTabId={activeInboxTabId}
|
||
unseenInTabById={unseenInTabById}
|
||
onTabClick={handleCategoryInboxTabClick}
|
||
/>
|
||
) : (
|
||
<div
|
||
className="grid w-full min-w-0 max-w-[1260px]"
|
||
style={{
|
||
gridTemplateColumns: `repeat(${inboxTabBarItems.length}, minmax(0, 1fr))`,
|
||
}}
|
||
>
|
||
{inboxTabBarItems.map((tab) => {
|
||
const isActive = activeInboxTabId === tab.id
|
||
const accentColor = isActive
|
||
? inboxTabActiveAccentColor(tab.id, tab.badgeColor)
|
||
: undefined
|
||
const unseen = unseenInTabById[tab.id] ?? 0
|
||
const senderLine = tabUnseenSenderLineById[tab.id] ?? ""
|
||
const showMeta =
|
||
inboxTabShowsInactiveMeta(tab.id) && !isActive && unseen > 0
|
||
const showSenderLine = showMeta && Boolean(senderLine)
|
||
const isExpandedTabMeta = showSenderLine
|
||
return (
|
||
<button
|
||
key={tab.id}
|
||
type="button"
|
||
aria-label={tab.label}
|
||
aria-current={isActive ? "true" : undefined}
|
||
onClick={() => handleCategoryInboxTabClick(tab.id)}
|
||
style={
|
||
accentColor
|
||
? { boxShadow: `inset 0 -3px 0 0 ${accentColor}` }
|
||
: undefined
|
||
}
|
||
className={cn(
|
||
"relative z-[1] flex cursor-pointer transition-colors",
|
||
"min-w-0 w-full overflow-hidden max-sm:min-h-10 max-sm:items-center max-sm:justify-center",
|
||
"sm:min-h-14 sm:items-center sm:py-2 sm:text-left",
|
||
!isActive && "hover:bg-[#f1f3f4]"
|
||
)}
|
||
>
|
||
<>
|
||
<div className="flex h-10 w-full items-center justify-center sm:hidden">
|
||
<div className="relative inline-flex shrink-0">
|
||
<Icon
|
||
icon={tab.icon}
|
||
className={cn(
|
||
CATEGORY_TAB_ICON_CLASS,
|
||
MAIL_INBOX_CATEGORY_TAB_CONTENT_DARK_CLASS,
|
||
!isActive && "text-[#5f6368]"
|
||
)}
|
||
style={accentColor ? { color: accentColor } : undefined}
|
||
aria-hidden
|
||
/>
|
||
{showMeta && unseen > 0 ? (
|
||
<span
|
||
className={inboxTabBadgeDotClass(tab.badgeColor)}
|
||
aria-hidden
|
||
/>
|
||
) : null}
|
||
</div>
|
||
</div>
|
||
|
||
<div className="hidden min-w-0 flex-1 items-center gap-2 mx-2 sm:mx-3 sm:flex">
|
||
<Icon
|
||
icon={tab.icon}
|
||
className={cn(
|
||
CATEGORY_TAB_ICON_CLASS,
|
||
"self-center",
|
||
MAIL_INBOX_CATEGORY_TAB_CONTENT_DARK_CLASS,
|
||
!isActive && "text-[#5f6368]"
|
||
)}
|
||
style={accentColor ? { color: accentColor } : undefined}
|
||
aria-hidden
|
||
/>
|
||
<div className="flex min-w-0 w-0 flex-1 flex-col gap-px">
|
||
<div
|
||
className={cn(
|
||
"flex min-w-0 items-center gap-1.5",
|
||
isExpandedTabMeta && "min-h-5"
|
||
)}
|
||
>
|
||
<span
|
||
className={cn(
|
||
"min-w-0 flex-1 truncate text-[13px] font-semibold leading-tight",
|
||
MAIL_INBOX_CATEGORY_TAB_CONTENT_DARK_CLASS,
|
||
!isActive && "text-[#3c4043]"
|
||
)}
|
||
style={accentColor ? { color: accentColor } : undefined}
|
||
>
|
||
{tab.label}
|
||
</span>
|
||
{showMeta && unseen > 0 ? (
|
||
<span className={inboxTabBadgeCountClass(tab.badgeColor)}>
|
||
{unseen}
|
||
<span className="hidden md:inline">
|
||
{" "}
|
||
{unseen === 1 ? "nouveau" : "nouveaux"}
|
||
</span>
|
||
</span>
|
||
) : null}
|
||
</div>
|
||
{isExpandedTabMeta ? (
|
||
<span
|
||
className={cn(
|
||
"block min-h-4 min-w-0 truncate text-[11px] leading-snug text-[#5f6368]",
|
||
MAIL_INBOX_CATEGORY_TAB_CONTENT_DARK_CLASS
|
||
)}
|
||
>
|
||
{senderLine}
|
||
</span>
|
||
) : null}
|
||
</div>
|
||
</div>
|
||
</>
|
||
</button>
|
||
)
|
||
})}
|
||
</div>
|
||
))}
|
||
</div>
|
||
)}
|
||
|
||
{isSearchMode && searchParams && listToolbarMode && (
|
||
<div className="flex w-full shrink-0 items-center gap-1.5 overflow-x-auto border-b border-[#dadce0] bg-mail-surface px-3 py-1.5 text-xs dark:border-gray-700">
|
||
{/* De dropdown */}
|
||
<DropdownMenu>
|
||
<DropdownMenuTrigger asChild>
|
||
<button
|
||
type="button"
|
||
className={cn(
|
||
"flex shrink-0 items-center gap-1 rounded-full border px-2.5 py-1 transition-colors",
|
||
searchParams.from
|
||
? "border-blue-200 bg-blue-50 text-blue-700 dark:border-blue-700 dark:bg-blue-900/30 dark:text-blue-300"
|
||
: "border-[#dadce0] text-[#5f6368] hover:bg-[#f1f3f4] dark:border-gray-600 dark:text-gray-400"
|
||
)}
|
||
>
|
||
<UserIcon className="size-3" strokeWidth={2} />
|
||
De{searchParams.from ? ` : ${searchParams.from}` : ""}
|
||
<ChevronDown className="size-3" />
|
||
</button>
|
||
</DropdownMenuTrigger>
|
||
<DropdownMenuContent align="start" className={MAIL_MENU_SURFACE_CLASS}>
|
||
<DropdownMenuItem onSelect={() => setSearchFilter({ from: "" })}>
|
||
N'importe qui
|
||
</DropdownMenuItem>
|
||
<DropdownMenuItem onSelect={() => setSearchFilter({ from: searchAccount.email })}>
|
||
De moi ({searchAccount.email})
|
||
</DropdownMenuItem>
|
||
<DropdownMenuSeparator />
|
||
{Array.from(new Set(allEmails.map((e) => e.senderEmail).filter(Boolean))).slice(0, 8).map((addr) => (
|
||
<DropdownMenuItem key={addr} onSelect={() => setSearchFilter({ from: addr! })}>
|
||
{addr}
|
||
</DropdownMenuItem>
|
||
))}
|
||
</DropdownMenuContent>
|
||
</DropdownMenu>
|
||
|
||
{/* Date dropdown */}
|
||
<DropdownMenu>
|
||
<DropdownMenuTrigger asChild>
|
||
<button
|
||
type="button"
|
||
className={cn(
|
||
"flex shrink-0 items-center gap-1 rounded-full border px-2.5 py-1 transition-colors",
|
||
searchParams.within
|
||
? "border-blue-200 bg-blue-50 text-blue-700 dark:border-blue-700 dark:bg-blue-900/30 dark:text-blue-300"
|
||
: "border-[#dadce0] text-[#5f6368] hover:bg-[#f1f3f4] dark:border-gray-600 dark:text-gray-400"
|
||
)}
|
||
>
|
||
<Clock className="size-3" strokeWidth={2} />
|
||
{searchParams.within
|
||
? DATE_RANGE_OPTIONS.find((o) => o.value === searchParams.within)?.label ?? searchParams.within
|
||
: "Indifférente"}
|
||
<ChevronDown className="size-3" />
|
||
</button>
|
||
</DropdownMenuTrigger>
|
||
<DropdownMenuContent align="start" className={MAIL_MENU_SURFACE_CLASS}>
|
||
<DropdownMenuItem onSelect={() => setSearchFilter({ within: "" })}>
|
||
Indifférente
|
||
</DropdownMenuItem>
|
||
<DropdownMenuSeparator />
|
||
{DATE_RANGE_OPTIONS.map((opt) => (
|
||
<DropdownMenuItem key={opt.value} onSelect={() => setSearchFilter({ within: opt.value })}>
|
||
{opt.label}
|
||
</DropdownMenuItem>
|
||
))}
|
||
</DropdownMenuContent>
|
||
</DropdownMenu>
|
||
|
||
{/* Contient une pièce jointe */}
|
||
<button
|
||
type="button"
|
||
onClick={() => toggleSearchFilter("has", "attachment")}
|
||
className={cn(
|
||
"flex shrink-0 items-center gap-1 rounded-full border px-2.5 py-1 transition-colors",
|
||
searchParams.has.includes("attachment")
|
||
? "border-blue-200 bg-blue-50 text-blue-700 dark:border-blue-700 dark:bg-blue-900/30 dark:text-blue-300"
|
||
: "border-[#dadce0] text-[#5f6368] hover:bg-[#f1f3f4] dark:border-gray-600 dark:text-gray-400"
|
||
)}
|
||
>
|
||
<Paperclip className="size-3" strokeWidth={2} />
|
||
Pièces jointes
|
||
</button>
|
||
|
||
{/* Exclure les mises à jour d'agenda */}
|
||
<button
|
||
type="button"
|
||
onClick={() => toggleSearchFilter("excludeChats", "true")}
|
||
className={cn(
|
||
"flex shrink-0 items-center gap-1 rounded-full border px-2.5 py-1 transition-colors",
|
||
searchParams.excludeChats
|
||
? "border-blue-200 bg-blue-50 text-blue-700 dark:border-blue-700 dark:bg-blue-900/30 dark:text-blue-300"
|
||
: "border-[#dadce0] text-[#5f6368] hover:bg-[#f1f3f4] dark:border-gray-600 dark:text-gray-400"
|
||
)}
|
||
>
|
||
<CalendarX2 className="size-3" strokeWidth={2} />
|
||
Exclure les mises à jour d'agenda
|
||
</button>
|
||
|
||
{/* À dropdown */}
|
||
<DropdownMenu>
|
||
<DropdownMenuTrigger asChild>
|
||
<button
|
||
type="button"
|
||
className={cn(
|
||
"flex shrink-0 items-center gap-1 rounded-full border px-2.5 py-1 transition-colors",
|
||
searchParams.to
|
||
? "border-blue-200 bg-blue-50 text-blue-700 dark:border-blue-700 dark:bg-blue-900/30 dark:text-blue-300"
|
||
: "border-[#dadce0] text-[#5f6368] hover:bg-[#f1f3f4] dark:border-gray-600 dark:text-gray-400"
|
||
)}
|
||
>
|
||
<Send className="size-3" strokeWidth={2} />
|
||
À{searchParams.to ? ` : ${searchParams.to}` : ""}
|
||
<ChevronDown className="size-3" />
|
||
</button>
|
||
</DropdownMenuTrigger>
|
||
<DropdownMenuContent align="start" className={MAIL_MENU_SURFACE_CLASS}>
|
||
<DropdownMenuItem onSelect={() => setSearchFilter({ to: "" })}>
|
||
N'importe qui
|
||
</DropdownMenuItem>
|
||
<DropdownMenuItem onSelect={() => setSearchFilter({ to: searchAccount.email })}>
|
||
À moi ({searchAccount.email})
|
||
</DropdownMenuItem>
|
||
</DropdownMenuContent>
|
||
</DropdownMenu>
|
||
|
||
{/* Non lu */}
|
||
<button
|
||
type="button"
|
||
onClick={() => {
|
||
if (!searchParams) return
|
||
const next = { ...searchParams }
|
||
if (next.q.includes("is:unread")) {
|
||
next.q = next.q.replace(/\s*is:unread\s*/g, "").trim()
|
||
} else {
|
||
next.q = (next.q + " is:unread").trim()
|
||
}
|
||
searchRouter.push(buildSearchUrl(next))
|
||
}}
|
||
className={cn(
|
||
"flex shrink-0 items-center gap-1 rounded-full border px-2.5 py-1 transition-colors",
|
||
searchParams.q.includes("is:unread")
|
||
? "border-blue-200 bg-blue-50 text-blue-700 dark:border-blue-700 dark:bg-blue-900/30 dark:text-blue-300"
|
||
: "border-[#dadce0] text-[#5f6368] hover:bg-[#f1f3f4] dark:border-gray-600 dark:text-gray-400"
|
||
)}
|
||
>
|
||
<MailOpen className="size-3" strokeWidth={2} />
|
||
Non lu
|
||
</button>
|
||
|
||
{/* Recherche avancée */}
|
||
<button
|
||
type="button"
|
||
onClick={() => setAdvancedOpen(true)}
|
||
className="ml-auto shrink-0 px-2 py-1 text-xs font-medium text-[#1a73e8] hover:text-[#1765cc] dark:text-blue-400"
|
||
>
|
||
Recherche avancée
|
||
</button>
|
||
</div>
|
||
)}
|
||
</>
|
||
)}
|
||
</>
|
||
)
|
||
}
|