146 lines
3.8 KiB
TypeScript
146 lines
3.8 KiB
TypeScript
"use client"
|
|
|
|
import { Fragment, useMemo } from "react"
|
|
import { Icon } from "@iconify/react"
|
|
import type { FolderTreeNode } from "@/lib/sidebar-nav-maps"
|
|
import type { LabelRowItem } from "@/lib/sidebar-nav-data"
|
|
import { breadcrumbItemsForVisitKey } from "@/lib/mail-folder-display"
|
|
import { resolveMailNavIcon } from "@/lib/mail-nav-icons"
|
|
import { cn } from "@/lib/utils"
|
|
|
|
type MailFolderStackIndicatorProps = {
|
|
currentKey: string
|
|
folderTree: FolderTreeNode[]
|
|
folderIdToLabel: Record<string, string>
|
|
labelRows?: readonly LabelRowItem[]
|
|
className?: string
|
|
onNavigate?: (visitKey: string) => void
|
|
}
|
|
|
|
function MailNavIconGlyph({
|
|
visitKey,
|
|
folderTree,
|
|
labelRows,
|
|
}: {
|
|
visitKey: string
|
|
folderTree: FolderTreeNode[]
|
|
labelRows?: readonly LabelRowItem[]
|
|
}) {
|
|
const resolved = useMemo(
|
|
() => resolveMailNavIcon(visitKey, folderTree, labelRows),
|
|
[visitKey, folderTree, labelRows]
|
|
)
|
|
|
|
if (resolved.kind === "folder") {
|
|
return (
|
|
<Icon
|
|
icon={resolved.icon}
|
|
className="size-4 shrink-0"
|
|
style={{ color: resolved.color }}
|
|
aria-hidden
|
|
/>
|
|
)
|
|
}
|
|
|
|
if (resolved.kind === "iconify") {
|
|
return (
|
|
<Icon
|
|
icon={resolved.icon}
|
|
className="size-4 shrink-0 text-[#5f6368]"
|
|
aria-hidden
|
|
/>
|
|
)
|
|
}
|
|
|
|
const { Icon: LucideIcon } = resolved
|
|
return (
|
|
<LucideIcon
|
|
className="size-4 shrink-0 text-[#5f6368]"
|
|
strokeWidth={1.5}
|
|
aria-hidden
|
|
/>
|
|
)
|
|
}
|
|
|
|
export function MailFolderStackIndicator({
|
|
currentKey,
|
|
folderTree,
|
|
folderIdToLabel,
|
|
labelRows,
|
|
className,
|
|
onNavigate,
|
|
}: MailFolderStackIndicatorProps) {
|
|
const items = useMemo(
|
|
() =>
|
|
breadcrumbItemsForVisitKey(
|
|
currentKey,
|
|
folderTree,
|
|
folderIdToLabel,
|
|
labelRows
|
|
),
|
|
[currentKey, folderTree, folderIdToLabel, labelRows]
|
|
)
|
|
|
|
const ariaLabel = items.map((i) => i.label).join(" · ")
|
|
|
|
return (
|
|
<nav
|
|
aria-label={`Fil d'Ariane : ${ariaLabel}`}
|
|
className={cn(
|
|
"flex max-w-[min(360px,calc(100vw-1rem))] items-center",
|
|
"border-t border-r border-[#dadce0]/90",
|
|
"bg-white/78 px-3.5 py-2.5 text-sm font-medium leading-snug text-[#3c4043]",
|
|
"rounded-tr-2xl shadow-sm backdrop-blur-md",
|
|
className
|
|
)}
|
|
>
|
|
<span className="flex min-w-0 items-center gap-1.5">
|
|
{items.map((item, i) => {
|
|
const isCurrent = item.visitKey === currentKey
|
|
const segmentClass = cn(
|
|
"flex min-w-0 items-center gap-1.5 rounded-sm outline-none",
|
|
onNavigate &&
|
|
!isCurrent &&
|
|
"cursor-pointer hover:bg-[#f1f3f4] focus-visible:ring-2 focus-visible:ring-[#0b57d0]/40",
|
|
onNavigate && isCurrent && "cursor-default"
|
|
)
|
|
const content = (
|
|
<>
|
|
<MailNavIconGlyph
|
|
visitKey={item.visitKey}
|
|
folderTree={folderTree}
|
|
labelRows={labelRows}
|
|
/>
|
|
<span className="truncate">{item.label}</span>
|
|
</>
|
|
)
|
|
return (
|
|
<Fragment key={`${item.visitKey}-${i}`}>
|
|
{i > 0 ? (
|
|
<span
|
|
className="shrink-0 text-xs leading-none text-[#9aa0a6]"
|
|
aria-hidden
|
|
>
|
|
·
|
|
</span>
|
|
) : null}
|
|
{onNavigate ? (
|
|
<button
|
|
type="button"
|
|
className={segmentClass}
|
|
aria-current={isCurrent ? "page" : undefined}
|
|
onClick={() => onNavigate(item.visitKey)}
|
|
>
|
|
{content}
|
|
</button>
|
|
) : (
|
|
<span className={segmentClass}>{content}</span>
|
|
)}
|
|
</Fragment>
|
|
)
|
|
})}
|
|
</span>
|
|
</nav>
|
|
)
|
|
}
|