Enhance email components with dynamic accent colors for active tabs and improved styling. Updated CompactInboxCategoryTabs and EmailList to utilize new accent color logic, refined sidebar icons, and adjusted layout for better responsiveness in split view.
This commit is contained in:
parent
2eda0d4181
commit
18cb1257f4
@ -33,6 +33,7 @@ import {
|
||||
DEFAULT_INBOX_TAB,
|
||||
type MailRouteState,
|
||||
} from "@/lib/mail-url"
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
function segmentsFromPathname(pathname: string | null): string[] | undefined {
|
||||
if (!pathname?.startsWith("/mail")) return undefined
|
||||
@ -120,7 +121,12 @@ function MailAppInner() {
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
<div className="relative flex min-h-0 flex-1 gap-0 overflow-hidden bg-app-canvas pl-0 pr-0 pb-1 pt-1 sm:gap-1 sm:pl-1">
|
||||
<div
|
||||
className={cn(
|
||||
"relative flex min-h-0 flex-1 gap-0 overflow-hidden pl-0 pr-0",
|
||||
splitView ? "bg-white p-0" : "bg-app-canvas pb-1 pt-1 sm:gap-1 sm:pl-1"
|
||||
)}
|
||||
>
|
||||
{!sidebarCollapsed && touchNav && (
|
||||
<button
|
||||
type="button"
|
||||
@ -145,7 +151,12 @@ function MailAppInner() {
|
||||
folderUnreadCounts={folderUnreadCounts}
|
||||
splitView={splitView}
|
||||
/>
|
||||
<main className="flex min-h-0 flex-1 flex-col overflow-hidden rounded-none bg-white shadow-sm sm:rounded-2xl">
|
||||
<main
|
||||
className={cn(
|
||||
"flex min-h-0 flex-1 flex-col overflow-hidden bg-white",
|
||||
splitView ? "rounded-none shadow-none" : "rounded-none shadow-sm sm:rounded-2xl"
|
||||
)}
|
||||
>
|
||||
<Suspense>
|
||||
<EmailList
|
||||
selectedFolder={route.folderId}
|
||||
@ -160,7 +171,14 @@ function MailAppInner() {
|
||||
/>
|
||||
</Suspense>
|
||||
</main>
|
||||
<RightPanel />
|
||||
<div
|
||||
className={cn(
|
||||
"flex shrink-0 flex-col",
|
||||
splitView && "border-l border-gray-200"
|
||||
)}
|
||||
>
|
||||
<RightPanel />
|
||||
</div>
|
||||
</div>
|
||||
{!splitView ? (
|
||||
<MobileBottomBar
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
|
||||
import { memo, useEffect, useLayoutEffect, useRef, useState } from "react"
|
||||
import { Icon } from "@iconify/react"
|
||||
import { inboxTabActiveAccentColor } from "@/lib/inbox-category-tabs"
|
||||
import { inboxTabShowsInactiveMeta } from "@/lib/mail-url"
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
@ -84,22 +85,31 @@ export const CompactInboxCategoryTabs = memo(function CompactInboxCategoryTabs({
|
||||
}
|
||||
}, [displayedTabId, tabs])
|
||||
|
||||
const activeTab = tabs.find((t) => t.id === displayedTabId)
|
||||
const activeAccentColor = activeTab
|
||||
? inboxTabActiveAccentColor(activeTab.id, activeTab.badgeColor)
|
||||
: "#0b57d0"
|
||||
|
||||
return (
|
||||
<div ref={barRef} className="relative flex w-full min-w-0">
|
||||
<span
|
||||
aria-hidden
|
||||
className={cn(
|
||||
"pointer-events-none absolute bottom-0 left-0 z-[2] h-[3px] origin-left rounded-t-sm bg-[#0b57d0]",
|
||||
"will-change-[transform,width] transition-[transform,width] duration-300 ease-[cubic-bezier(0.2,0,0,1)] motion-reduce:transition-none",
|
||||
"pointer-events-none absolute bottom-0 left-0 z-[2] h-[3px] origin-left rounded-t-sm",
|
||||
"will-change-[transform,width] transition-[transform,width,background-color] duration-300 ease-[cubic-bezier(0.2,0,0,1)] motion-reduce:transition-none",
|
||||
indicator.ready ? "opacity-100" : "opacity-0"
|
||||
)}
|
||||
style={{
|
||||
width: indicator.width,
|
||||
transform: `translate3d(${indicator.x}px, 0, 0)`,
|
||||
backgroundColor: activeAccentColor,
|
||||
}}
|
||||
/>
|
||||
{tabs.map((tab) => {
|
||||
const isActive = displayedTabId === tab.id
|
||||
const accentColor = isActive
|
||||
? inboxTabActiveAccentColor(tab.id, tab.badgeColor)
|
||||
: undefined
|
||||
const unseen = unseenInTabById[tab.id] ?? 0
|
||||
const showMeta =
|
||||
inboxTabShowsInactiveMeta(tab.id) && !isActive && unseen > 0
|
||||
@ -135,8 +145,9 @@ export const CompactInboxCategoryTabs = memo(function CompactInboxCategoryTabs({
|
||||
className={cn(
|
||||
TAB_ICON_CLASS,
|
||||
"transition-colors duration-200 motion-reduce:transition-none",
|
||||
isActive ? "text-[#0b57d0]" : "text-[#5f6368]"
|
||||
!isActive && "text-[#5f6368]"
|
||||
)}
|
||||
style={accentColor ? { color: accentColor } : undefined}
|
||||
aria-hidden
|
||||
/>
|
||||
{showMeta && unseen > 0 ? (
|
||||
@ -147,7 +158,10 @@ export const CompactInboxCategoryTabs = memo(function CompactInboxCategoryTabs({
|
||||
) : null}
|
||||
</div>
|
||||
{isActive ? (
|
||||
<span className="shrink-0 whitespace-nowrap text-[13px] font-semibold leading-tight text-[#0b57d0]">
|
||||
<span
|
||||
className="shrink-0 whitespace-nowrap text-[13px] font-semibold leading-tight"
|
||||
style={{ color: accentColor }}
|
||||
>
|
||||
{tab.label}
|
||||
</span>
|
||||
) : null}
|
||||
|
||||
@ -129,6 +129,7 @@ import {
|
||||
} from "@/lib/mail-folder-display"
|
||||
import {
|
||||
buildInboxCategoryTabIcons,
|
||||
inboxTabActiveAccentColor,
|
||||
resolveEmailInboxCategoryTabs,
|
||||
} from "@/lib/inbox-category-tabs"
|
||||
import {
|
||||
@ -2981,6 +2982,9 @@ export function EmailList({
|
||||
>
|
||||
{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 =
|
||||
@ -2994,11 +2998,15 @@ export function EmailList({
|
||||
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 && "shadow-[inset_0_-3px_0_0_#0b57d0]",
|
||||
!isActive && "hover:bg-[#f1f3f4]"
|
||||
)}
|
||||
>
|
||||
@ -3009,8 +3017,9 @@ export function EmailList({
|
||||
icon={tab.icon}
|
||||
className={cn(
|
||||
CATEGORY_TAB_ICON_CLASS,
|
||||
isActive ? "text-[#0b57d0]" : "text-[#5f6368]"
|
||||
!isActive && "text-[#5f6368]"
|
||||
)}
|
||||
style={accentColor ? { color: accentColor } : undefined}
|
||||
aria-hidden
|
||||
/>
|
||||
{showMeta && unseen > 0 ? (
|
||||
@ -3028,8 +3037,9 @@ export function EmailList({
|
||||
className={cn(
|
||||
CATEGORY_TAB_ICON_CLASS,
|
||||
"self-center",
|
||||
isActive ? "text-[#0b57d0]" : "text-[#5f6368]"
|
||||
!isActive && "text-[#5f6368]"
|
||||
)}
|
||||
style={accentColor ? { color: accentColor } : undefined}
|
||||
aria-hidden
|
||||
/>
|
||||
<div className="flex min-w-0 w-0 flex-1 flex-col gap-px">
|
||||
@ -3042,8 +3052,9 @@ export function EmailList({
|
||||
<span
|
||||
className={cn(
|
||||
"min-w-0 flex-1 truncate text-[13px] font-semibold leading-tight",
|
||||
isActive ? "text-[#0b57d0]" : "text-[#3c4043]"
|
||||
!isActive && "text-[#3c4043]"
|
||||
)}
|
||||
style={accentColor ? { color: accentColor } : undefined}
|
||||
>
|
||||
{tab.label}
|
||||
</span>
|
||||
@ -3080,8 +3091,7 @@ export function EmailList({
|
||||
!splitView && isViewMode && openEmail
|
||||
? "relative flex min-h-0 flex-1 flex-col overflow-hidden"
|
||||
: mainScrollClass,
|
||||
"relative min-h-0 flex-1 overscroll-y-none max-sm:pb-16",
|
||||
listToolbarMode && "sm:pb-12"
|
||||
"relative min-h-0 flex-1 overscroll-y-none max-sm:pb-16"
|
||||
)}
|
||||
>
|
||||
{listToolbarMode && (
|
||||
@ -3286,7 +3296,7 @@ export function EmailList({
|
||||
!splitView &&
|
||||
"md:flex md:items-start md:gap-2 md:px-2 md:py-1.5",
|
||||
isSplitActiveRow
|
||||
? "z-[1] bg-[#e8f0fe] shadow-[inset_3px_0_0_0_#0b57d0]"
|
||||
? "z-[1] bg-[#e8f0fe] shadow-[inset_3px_0_0_0_#669df6]"
|
||||
: isSelected
|
||||
? "bg-[#e8f0fe]"
|
||||
: isRead
|
||||
@ -4527,16 +4537,14 @@ export function EmailList({
|
||||
</div>
|
||||
</div>
|
||||
{listToolbarMode ? (
|
||||
<div className="pointer-events-none absolute bottom-0 left-0 z-20 hidden max-w-full sm:block">
|
||||
<div className="pointer-events-auto w-fit max-w-full">
|
||||
<MailFolderStackIndicator
|
||||
currentKey={mailNavVisitKey(selectedFolder, inboxTab)}
|
||||
folderTree={sidebarNav.folderTree}
|
||||
folderIdToLabel={sidebarNav.folderIdToLabel}
|
||||
labelRows={sidebarNav.labelRows}
|
||||
onNavigate={handleBreadcrumbNavigate}
|
||||
/>
|
||||
</div>
|
||||
<div className="hidden w-fit max-w-full shrink-0 self-start sm:block">
|
||||
<MailFolderStackIndicator
|
||||
currentKey={mailNavVisitKey(selectedFolder, inboxTab)}
|
||||
folderTree={sidebarNav.folderTree}
|
||||
folderIdToLabel={sidebarNav.folderIdToLabel}
|
||||
labelRows={sidebarNav.labelRows}
|
||||
onNavigate={handleBreadcrumbNavigate}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
@ -7,7 +7,6 @@ import {
|
||||
ClockArrowUp,
|
||||
Send,
|
||||
FileText,
|
||||
Tag,
|
||||
ChevronDown,
|
||||
GripVertical,
|
||||
Pencil,
|
||||
@ -141,7 +140,7 @@ const mainItems = [
|
||||
{ id: "inbox", label: "Boîte de réception", icon: Inbox },
|
||||
{ id: "starred", label: "Messages suivis", icon: Star },
|
||||
{ id: "snoozed", label: "En attente", icon: Clock },
|
||||
{ id: "important", label: "Important", icon: Tag },
|
||||
{ id: "important", label: "Important", icon: "mdi:label-variant-outline" },
|
||||
{ id: "sent", label: "Messages envoyés", icon: Send },
|
||||
{ id: "drafts", label: "Brouillons", icon: FileText },
|
||||
{ id: "scheduled", label: "Planifié", icon: ClockArrowUp },
|
||||
@ -1015,12 +1014,16 @@ export function Sidebar({
|
||||
isSelected,
|
||||
unreadCount,
|
||||
}: {
|
||||
item: { id: string; label: string; icon: React.ElementType }
|
||||
item: { id: string; label: string; icon: React.ElementType | string }
|
||||
isSelected: boolean
|
||||
unreadCount: number
|
||||
}) => {
|
||||
const { isOver, dropHandlers } = useEmailDropTarget(item.id, item.label)
|
||||
const hasUnread = unreadCount > 0
|
||||
const iconClassName = cn(
|
||||
"h-5 w-5 shrink-0",
|
||||
hasUnread && !isSelected && "text-gray-900"
|
||||
)
|
||||
return (
|
||||
<button
|
||||
onClick={() => onSelectFolder(item.id)}
|
||||
@ -1038,12 +1041,11 @@ export function Sidebar({
|
||||
: "text-gray-700 hover:bg-gray-100"
|
||||
)}
|
||||
>
|
||||
<item.icon
|
||||
className={cn(
|
||||
"h-5 w-5 shrink-0",
|
||||
hasUnread && !isSelected && "text-gray-900"
|
||||
)}
|
||||
/>
|
||||
{typeof item.icon === "string" ? (
|
||||
<Icon icon={item.icon} className={iconClassName} aria-hidden />
|
||||
) : (
|
||||
<item.icon className={iconClassName} />
|
||||
)}
|
||||
{isExpanded && (
|
||||
<div className="flex min-w-0 flex-1 items-baseline gap-4">
|
||||
<span
|
||||
@ -2450,6 +2452,7 @@ export function Sidebar({
|
||||
className={cn(
|
||||
"absolute left-0 top-0 bottom-0 flex flex-col overflow-hidden bg-app-canvas transition-[width,transform] duration-200 z-40 select-none",
|
||||
isExpanded ? "w-60" : "w-[68px]",
|
||||
splitView && "border-r border-gray-200",
|
||||
!touchNav && hoverExpanded && "shadow-xl border-r border-gray-200",
|
||||
isOverlayOpen && "z-50 shadow-xl border-r border-gray-200",
|
||||
collapsed && isXs && "-translate-x-full pointer-events-none"
|
||||
|
||||
@ -1,10 +1,15 @@
|
||||
import type { Email } from "@/lib/email-data"
|
||||
import { navFolderIconColorFromBgClass } from "@/lib/label-pill-contrast"
|
||||
import {
|
||||
emailMatchesFolder,
|
||||
type MailFolderFilterCtx,
|
||||
type MailNavFolderMaps,
|
||||
} from "@/lib/mail-folder-filter"
|
||||
import { DEFAULT_INBOX_TAB, INBOX_ALL_TAB } from "@/lib/mail-url"
|
||||
import {
|
||||
DEFAULT_INBOX_TAB,
|
||||
INBOX_ALL_TAB,
|
||||
normalizeInboxTabSegment,
|
||||
} from "@/lib/mail-url"
|
||||
import {
|
||||
tabbedInboxLabelRows,
|
||||
type LabelRowItem,
|
||||
@ -17,6 +22,15 @@ export type InboxCategoryTabIcon = {
|
||||
badgeColor: string
|
||||
}
|
||||
|
||||
/** Couleur icône / libellé / soulignement de l’onglet boîte actif. */
|
||||
export function inboxTabActiveAccentColor(
|
||||
tabId: string,
|
||||
badgeColor: string
|
||||
): string {
|
||||
if (normalizeInboxTabSegment(tabId) === INBOX_ALL_TAB) return "#202124"
|
||||
return navFolderIconColorFromBgClass(badgeColor)
|
||||
}
|
||||
|
||||
/** Onglets catégorie boîte (Principale + libellés tabbed), hors « Tous les messages ». */
|
||||
export function buildInboxCategoryTabIcons(
|
||||
labelRows: readonly LabelRowItem[]
|
||||
|
||||
@ -3,7 +3,6 @@ import {
|
||||
Inbox,
|
||||
Star,
|
||||
Clock,
|
||||
Tag,
|
||||
Send,
|
||||
FileText,
|
||||
ClockArrowUp,
|
||||
@ -26,7 +25,6 @@ const SYSTEM_ICONS: Record<string, LucideIcon> = {
|
||||
inbox: Inbox,
|
||||
starred: Star,
|
||||
snoozed: Clock,
|
||||
important: Tag,
|
||||
sent: Send,
|
||||
drafts: FileText,
|
||||
scheduled: ClockArrowUp,
|
||||
@ -55,6 +53,10 @@ export function resolveMailNavIcon(
|
||||
return { kind: "lucide", Icon: Inbox }
|
||||
}
|
||||
|
||||
if (folderId === "important") {
|
||||
return { kind: "iconify", icon: "mdi:label-variant-outline" }
|
||||
}
|
||||
|
||||
const system = SYSTEM_ICONS[folderId]
|
||||
if (system) return { kind: "lucide", Icon: system }
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user