Files
mail-client/src/components/ClassicRibbonUI.tsx
T
KanjiG 20bfd2cf6b Initial commit: Outlook-competitor desktop mail client
Tauri v2 (Rust backend, React/TypeScript frontend). IMAP/POP3/SMTP core,
Google OAuth (mail/calendar/contacts sync), CalDAV, local calendar with
recurrence, rules engine, contacts, tasks, unified inbox, context menus,
drag & drop, snooze, templates, and more.
2026-07-28 20:56:05 +02:00

171 lines
4.6 KiB
TypeScript

import clsx from 'clsx'
import { Popover } from './Popover'
export function ClassicTabBar({
tabs,
active,
onChange,
rightSlot,
}: {
tabs: { id: string; label: string }[]
active: string
onChange: (id: string) => void
rightSlot?: React.ReactNode
}) {
return (
<div className="flex items-center gap-0.5 bg-[#1e5aa8] px-2 pt-1.5 dark:bg-zinc-900">
{tabs.map((t) => (
<button
key={t.id}
onClick={() => onChange(t.id)}
className={clsx(
'rounded-t-sm px-3 py-1.5 text-[13px] transition',
active === t.id
? 'bg-[#f3f2f1] font-semibold text-[#1e5aa8] dark:bg-zinc-800 dark:text-blue-300'
: 'text-white/90 hover:bg-white/10',
)}
>
{t.label}
</button>
))}
{rightSlot && <div className="ml-auto flex items-center pb-1.5">{rightSlot}</div>}
</div>
)
}
export function ClassicRibbonGroup({
caption,
children,
onLauncher,
}: {
caption: string
children: React.ReactNode
onLauncher?: () => void
}) {
return (
<div className="flex h-[92px] shrink-0 flex-col justify-between border-r border-[#d1d1d1] px-1.5 last:border-r-0 dark:border-zinc-700">
<div className="flex flex-1 items-start gap-0.5 pt-1">{children}</div>
<div className="relative flex items-center justify-center pb-0.5">
<span className="text-[10.5px] text-[#605e5c] dark:text-zinc-400">{caption}</span>
{onLauncher && (
<button
onClick={onLauncher}
title={`${caption}: weitere Optionen`}
className="absolute right-0 text-[#605e5c] hover:text-[#1e5aa8] dark:text-zinc-400 dark:hover:text-blue-300"
>
<svg width="8" height="8" viewBox="0 0 9 9">
<path d="M0 9L9 0M9 0H3M9 0V6" stroke="currentColor" strokeWidth="1.2" fill="none" />
</svg>
</button>
)}
</div>
</div>
)
}
export function ClassicButton({
icon: Icon,
label,
onClick,
disabled,
active,
size = 'large',
title,
}: {
icon: React.ComponentType<{ size?: number }>
label: string
onClick?: () => void
disabled?: boolean
active?: boolean
size?: 'large' | 'small'
title?: string
}) {
if (size === 'small') {
return (
<button
onClick={onClick}
disabled={disabled}
title={title ?? label}
className={clsx(
'flex items-center gap-1.5 rounded-sm px-1.5 py-0.5 text-[11.5px] text-[#1f1f1f] transition disabled:cursor-not-allowed disabled:opacity-40 dark:text-zinc-200',
active ? 'bg-[#cce4f7] dark:bg-blue-500/20' : 'hover:bg-[#e6f2fb] dark:hover:bg-zinc-700/60',
)}
>
<Icon size={14} />
<span className="whitespace-nowrap">{label}</span>
</button>
)
}
return (
<button
onClick={onClick}
disabled={disabled}
title={title ?? label}
className={clsx(
'flex w-[64px] shrink-0 flex-col items-center gap-1 rounded-sm px-1 py-1 text-center text-[11px] leading-tight text-[#1f1f1f] transition disabled:cursor-not-allowed disabled:opacity-40 dark:text-zinc-200',
active ? 'bg-[#cce4f7] dark:bg-blue-500/20' : 'hover:bg-[#e6f2fb] dark:hover:bg-zinc-700/60',
)}
>
<Icon size={24} />
<span>{label}</span>
</button>
)
}
export function ClassicDropdown({
button,
children,
open,
onOpenChange,
align = 'left',
widthClass = 'w-56',
}: {
button: React.ReactNode
children: React.ReactNode
open: boolean
onOpenChange: (v: boolean) => void
align?: 'left' | 'right'
widthClass?: string
}) {
return (
<Popover
open={open}
onOpenChange={onOpenChange}
trigger={button}
align={align === 'right' ? 'end' : 'start'}
panelClassName={clsx(
'animate-panel-in overflow-hidden rounded-md border border-zinc-300 bg-white py-1 shadow-xl dark:border-zinc-700 dark:bg-zinc-900',
widthClass,
)}
>
{children}
</Popover>
)
}
export function ClassicMenuItem({
children,
onClick,
disabled,
}: {
children: React.ReactNode
onClick?: () => void
disabled?: boolean
}) {
return (
<button
onClick={onClick}
disabled={disabled}
className="flex w-full items-center gap-2 px-3 py-1.5 text-left text-[12.5px] text-zinc-700 transition hover:bg-[#e6f2fb] disabled:cursor-not-allowed disabled:opacity-40 dark:text-zinc-200 dark:hover:bg-zinc-800"
>
{children}
</button>
)
}
export function ClassicRibbonBody({ children }: { children: React.ReactNode }) {
return (
<div className="flex h-[100px] items-stretch overflow-x-auto bg-[#f3f2f1] px-1.5 dark:bg-zinc-800/60">{children}</div>
)
}