refactor(mail): 统一右键菜单按钮样式

- 将邮件页右键菜单中的原生 `button` 替换为 `Button` 组件。
- 抽取通用按钮样式,统一打开、刷新、移动、标签等菜单项表现。
- 保持删除等危险操作的视觉强调一致。
This commit is contained in:
LanQin_
2026-06-25 16:17:56 +08:00
parent 98e7190512
commit 2558aa96ed
+24 -23
View File
@@ -1822,6 +1822,7 @@ function SidebarContextMenu({ state, canOrganize, pending, onClose, onOpen, onRe
const item = state.item const item = state.item
const customFolder = item.type === "folder" && item.custom const customFolder = item.type === "folder" && item.custom
const position = contextMenuPosition(state.x, state.y) const position = contextMenuPosition(state.x, state.y)
const itemClass = "h-auto w-full justify-start rounded px-2 py-1.5 text-left font-normal"
return ( return (
<div <div
className="fixed z-[110] w-48 rounded-md border bg-popover p-1 text-sm text-popover-foreground shadow-md" className="fixed z-[110] w-48 rounded-md border bg-popover p-1 text-sm text-popover-foreground shadow-md"
@@ -1830,36 +1831,36 @@ function SidebarContextMenu({ state, canOrganize, pending, onClose, onOpen, onRe
onContextMenu={(event) => event.preventDefault()} onContextMenu={(event) => event.preventDefault()}
role="menu" role="menu"
> >
<button type="button" className="flex w-full items-center gap-2 rounded px-2 py-1.5 text-left hover:bg-accent" onClick={() => onOpen(item)}> <Button type="button" variant="ghost" className={itemClass} onClick={() => onOpen(item)}>
<Inbox className="h-4 w-4" /> <Inbox className="h-4 w-4" />
</button> </Button>
<button type="button" className="flex w-full items-center gap-2 rounded px-2 py-1.5 text-left hover:bg-accent" onClick={onRefresh}> <Button type="button" variant="ghost" className={itemClass} onClick={onRefresh}>
<RefreshCcw className="h-4 w-4" /> <RefreshCcw className="h-4 w-4" />
</button> </Button>
{canOrganize && ( {canOrganize && (
<button type="button" className="flex w-full items-center gap-2 rounded px-2 py-1.5 text-left hover:bg-accent" onClick={onCreateFolder}> <Button type="button" variant="ghost" className={itemClass} onClick={onCreateFolder}>
<Plus className="h-4 w-4" /> <Plus className="h-4 w-4" />
</button> </Button>
)} )}
{canOrganize && customFolder && ( {canOrganize && customFolder && (
<> <>
<div className="my-1 h-px bg-border" /> <div className="my-1 h-px bg-border" />
<button type="button" className="flex w-full items-center gap-2 rounded px-2 py-1.5 text-left hover:bg-accent disabled:cursor-not-allowed disabled:opacity-50" disabled={pending} onClick={() => onMove(item, "top")}> <Button type="button" variant="ghost" className={itemClass} disabled={pending} onClick={() => onMove(item, "top")}>
<ArrowLeft className="h-4 w-4 rotate-90" /> <ArrowLeft className="h-4 w-4 rotate-90" />
</button> </Button>
<button type="button" className="flex w-full items-center gap-2 rounded px-2 py-1.5 text-left hover:bg-accent disabled:cursor-not-allowed disabled:opacity-50" disabled={pending} onClick={() => onMove(item, "up")}> <Button type="button" variant="ghost" className={itemClass} disabled={pending} onClick={() => onMove(item, "up")}>
<ChevronDown className="h-4 w-4 rotate-180" /> <ChevronDown className="h-4 w-4 rotate-180" />
</button> </Button>
<button type="button" className="flex w-full items-center gap-2 rounded px-2 py-1.5 text-left hover:bg-accent disabled:cursor-not-allowed disabled:opacity-50" disabled={pending} onClick={() => onMove(item, "down")}> <Button type="button" variant="ghost" className={itemClass} disabled={pending} onClick={() => onMove(item, "down")}>
<ChevronDown className="h-4 w-4" /> <ChevronDown className="h-4 w-4" />
</button> </Button>
<button type="button" className="flex w-full items-center gap-2 rounded px-2 py-1.5 text-left hover:bg-accent disabled:cursor-not-allowed disabled:opacity-50" disabled={pending} onClick={() => onMove(item, "bottom")}> <Button type="button" variant="ghost" className={itemClass} disabled={pending} onClick={() => onMove(item, "bottom")}>
<ArrowLeft className="h-4 w-4 -rotate-90" /> <ArrowLeft className="h-4 w-4 -rotate-90" />
</button> </Button>
<div className="my-1 h-px bg-border" /> <div className="my-1 h-px bg-border" />
<button type="button" className="flex w-full items-center gap-2 rounded px-2 py-1.5 text-left text-destructive hover:bg-destructive/10 hover:text-destructive" onClick={() => onDelete(item)}> <Button type="button" variant="ghost" className={cn(itemClass, "text-destructive hover:bg-destructive/10 hover:text-destructive")} onClick={() => onDelete(item)}>
<Trash2 className="h-4 w-4" /> <Trash2 className="h-4 w-4" />
</button> </Button>
</> </>
)} )}
</div> </div>
@@ -1889,14 +1890,14 @@ function MessageContextMenu({ state, labels, folders, canSend, canOrganize, canM
const { message } = state const { message } = state
const position = contextMenuPosition(state.x, state.y) const position = contextMenuPosition(state.x, state.y)
const draft = message.folder === "Drafts" const draft = message.folder === "Drafts"
const itemClass = "flex w-full items-center gap-2 rounded-sm px-3 py-2 text-left text-sm outline-none transition-colors hover:bg-accent focus:bg-accent disabled:pointer-events-none disabled:opacity-50" const itemClass = "h-auto w-full justify-start rounded-sm px-3 py-2 text-left font-normal"
function item(label: string, icon: React.ReactNode, action: Parameters<typeof onAction>[0], destructive = false) { function item(label: string, icon: React.ReactNode, action: Parameters<typeof onAction>[0], destructive = false) {
return ( return (
<button type="button" className={cn(itemClass, destructive && "text-destructive hover:text-destructive")} onClick={() => onAction(action, message)}> <Button type="button" variant="ghost" className={cn(itemClass, destructive && "text-destructive hover:text-destructive")} onClick={() => onAction(action, message)}>
{icon} {icon}
<span>{label}</span> <span>{label}</span>
</button> </Button>
) )
} }
function toggleLabel(label: MailLabel) { function toggleLabel(label: MailLabel) {
@@ -1941,11 +1942,11 @@ function MessageContextMenu({ state, labels, folders, canSend, canOrganize, canM
<div className="px-3 py-1.5 text-xs font-medium text-muted-foreground"></div> <div className="px-3 py-1.5 text-xs font-medium text-muted-foreground"></div>
<div className="max-h-44 overflow-y-auto"> <div className="max-h-44 overflow-y-auto">
{movableFolders.map((folder) => ( {movableFolders.map((folder) => (
<button key={folder.id} type="button" className={itemClass} onClick={() => moveToFolder(folder.name)}> <Button key={folder.id} type="button" variant="ghost" className={itemClass} onClick={() => moveToFolder(folder.name)}>
{folderIcons[folder.role] || <Inbox className="h-4 w-4" />} {folderIcons[folder.role] || <Inbox className="h-4 w-4" />}
<span className="min-w-0 flex-1 truncate text-left">{folderLabels[folder.name] || folder.name}</span> <span className="min-w-0 flex-1 truncate text-left">{folderLabels[folder.name] || folder.name}</span>
{folder.totalCount > 0 && <span className="text-xs text-muted-foreground">{folder.totalCount}</span>} {folder.totalCount > 0 && <span className="text-xs text-muted-foreground">{folder.totalCount}</span>}
</button> </Button>
))} ))}
</div> </div>
</> </>
@@ -1959,11 +1960,11 @@ function MessageContextMenu({ state, labels, folders, canSend, canOrganize, canM
const active = (message.labels || []).some((item) => item.id === label.id) const active = (message.labels || []).some((item) => item.id === label.id)
const colors = generateLabelColor(label.name) const colors = generateLabelColor(label.name)
return ( return (
<button key={label.id} type="button" className={itemClass} disabled={labelPending} onClick={() => toggleLabel(label)}> <Button key={label.id} type="button" variant="ghost" className={itemClass} disabled={labelPending} onClick={() => toggleLabel(label)}>
<Check className={cn("h-4 w-4", active ? "opacity-100" : "opacity-0")} /> <Check className={cn("h-4 w-4", active ? "opacity-100" : "opacity-0")} />
<span className="h-2 w-2 shrink-0 rounded-full" style={{ backgroundColor: colors.backgroundColor }} /> <span className="h-2 w-2 shrink-0 rounded-full" style={{ backgroundColor: colors.backgroundColor }} />
<span className="min-w-0 flex-1 truncate text-left">{active ? `移除 ${label.name}` : label.name}</span> <span className="min-w-0 flex-1 truncate text-left">{active ? `移除 ${label.name}` : label.name}</span>
</button> </Button>
) )
})} })}
</div> </div>