"use client" import { useMemo, type MouseEvent } from "react" import { cn } from "@/lib/utils" import { labelPillTextClassForBgHex, labelPillTextClassForTailwindBgUtility, } from "@/lib/label-pill-contrast" import type { FolderTreeNode, LabelRowItem } from "@/lib/sidebar-nav-data" /** Libellés système masqués en pastilles liste (Gmail : pastilles = libellés perso / dossiers). */ export const MAIL_LABEL_STRIP_EXCLUDE = new Set([ "inbox", "sent", "drafts", "spam", "starred", "snoozed", "trash", "important", "scheduled", ]) export function buildLabelTextToNavColorClass( tree: FolderTreeNode[], labelRows: LabelRowItem[] ): Map { const m = new Map() const put = (text: string, color: string | undefined) => { if (!color) return m.set(text.toLowerCase(), color) } const walk = (nodes: FolderTreeNode[]) => { for (const n of nodes) { put(n.label, n.color) if (n.children?.length) walk(n.children) } } walk(tree) for (const r of labelRows) put(r.label, r.color) return m } export function mailLabelShouldShowInListStrip( lab: string, emailLabelToSidebarFolderId: Record, getNavItemPrefs: (id: string) => { messages: string }, labelRows?: readonly LabelRowItem[] ): boolean { if (MAIL_LABEL_STRIP_EXCLUDE.has(lab.toLowerCase())) return false const fid = emailLabelToSidebarFolderId[lab] let row: LabelRowItem | undefined if (fid) row = labelRows?.find((r) => r.id === fid) if (!row && labelRows?.length) { row = labelRows.find((r) => r.label.toLowerCase() === lab.toLowerCase()) } if (row) { if (row.enabled === false) return false if (row.showInMessageList === false) return false } if (fid) return getNavItemPrefs(fid).messages !== "hide" return true } function findNodeLabel(tree: FolderTreeNode[], folderId: string): string | null { for (const n of tree) { if (n.id === folderId) return n.label if (n.children?.length) { const hit = findNodeLabel(n.children, folderId) if (hit) return hit } } return null } function collectFolderLabelsSet(tree: FolderTreeNode[]): Set { const s = new Set() const walk = (nodes: FolderTreeNode[]) => { for (const n of nodes) { s.add(n.label.toLowerCase()) if (n.children?.length) walk(n.children) } } walk(tree) return s } const PILL_BASE = "shrink-0 whitespace-nowrap rounded font-semibold leading-snug opacity-[0.96] transition-[filter,opacity] duration-150 hover:opacity-100 hover:brightness-[0.98]" function MailLabelPill({ label, displayText, bgClass, size, onClick, onRemoveClick, }: { label: string displayText: string bgClass: string | undefined size: "list" | "header" onClick: (e: MouseEvent) => void onRemoveClick?: (e: MouseEvent) => void }) { const sizeClass = size === "list" ? "px-1 py-px text-[11px]" : "inline-flex items-center gap-0.5 px-1.5 py-0.5 text-xs" return ( ) } type MailLabelPillStripProps = { labels: string[] | undefined labelBgByText: Map emailLabelToSidebarFolderId: Record getNavItemPrefs: (id: string) => { messages: string } onLabelNavigate: (label: string) => void variant: "list" | "header" /** En-tête message : filtre additionnel (ex. libellés système avec libellé lisible). */ showLabel?: (label: string) => boolean /** En-tête : nom affiché (Boîte de réception, etc.). */ resolveDisplayName?: (label: string) => string /** En-tête : pastille spam avec action. */ spamChip?: { onNotSpam: () => void } /** En-tête : bouton × sur chaque pastille. */ showRemoveOnPills?: boolean /** Dossier actuel (id) — son label est masqué en list, mais pas ses sous-dossiers. */ currentFolderId?: string /** Libellés navigation (couleurs / enabled / showInMessageList). */ labelRows?: readonly LabelRowItem[] /** Arbre dossiers (tri pastilles liste, masquage libellé du dossier courant). */ folderTree?: FolderTreeNode[] } export function MailLabelPillStrip({ labels, labelBgByText, emailLabelToSidebarFolderId, getNavItemPrefs, onLabelNavigate, variant, showLabel, resolveDisplayName, spamChip, showRemoveOnPills, currentFolderId, folderTree, labelRows, }: MailLabelPillStripProps) { const currentFolderLabel = useMemo(() => { if (!currentFolderId || !folderTree) return null return findNodeLabel(folderTree, currentFolderId) }, [currentFolderId, folderTree]) const folderLabelsLower = useMemo(() => { if (!folderTree) return new Set() return collectFolderLabelsSet(folderTree) }, [folderTree]) const shown = useMemo(() => { const filtered = (labels ?? []).filter((lab) => { if (variant === "list") { if ( !mailLabelShouldShowInListStrip( lab, emailLabelToSidebarFolderId, getNavItemPrefs, labelRows ) ) { return false } if (currentFolderLabel && lab.toLowerCase() === currentFolderLabel.toLowerCase()) { return false } return true } if (!mailLabelShouldShowInListStrip(lab, emailLabelToSidebarFolderId, getNavItemPrefs, labelRows)) { return false } if (lab === "spam" && spamChip) return false if (showLabel && !showLabel(lab)) return false return true }) if (variant === "list" && folderTree) { filtered.sort((a, b) => { const aIsFolder = folderLabelsLower.has(a.toLowerCase()) const bIsFolder = folderLabelsLower.has(b.toLowerCase()) if (aIsFolder && !bIsFolder) return -1 if (!aIsFolder && bIsFolder) return 1 return 0 }) } return filtered }, [labels, variant, emailLabelToSidebarFolderId, getNavItemPrefs, labelRows, currentFolderLabel, folderTree, folderLabelsLower, spamChip, showLabel]) const hasSpam = variant === "header" && spamChip if (shown.length === 0 && !hasSpam) return null const wrapClass = variant === "list" ? "flex shrink-0 items-center gap-1" : "contents" return ( {hasSpam ? ( ) : null} {shown.map((lab) => { const bg = labelBgByText.get(lab.toLowerCase()) const displayText = resolveDisplayName?.(lab) ?? lab return ( { e.stopPropagation() onLabelNavigate(lab) }} onRemoveClick={ showRemoveOnPills ? (e) => { e.stopPropagation() } : undefined } /> ) })} ) }