fix: mail grouping collapsible groups, no sticky header, recursive subgroups with all mails
This commit is contained in:
@@ -9,9 +9,10 @@ import { useTranslation } from 'react-i18next';
|
|||||||
import type { Mail } from '@/api/mail';
|
import type { Mail } from '@/api/mail';
|
||||||
import { decodeMimeHeader } from '@/api/mail';
|
import { decodeMimeHeader } from '@/api/mail';
|
||||||
import { EmptyState } from '@/components/ui/EmptyState';
|
import { EmptyState } from '@/components/ui/EmptyState';
|
||||||
import { Loader2, Paperclip, Star, ChevronDown } from 'lucide-react';
|
import { Loader2, Paperclip, Star, ChevronDown, ChevronRight } from 'lucide-react';
|
||||||
import { formatSmartDate } from '@/utils/date';
|
import { formatSmartDate } from '@/utils/date';
|
||||||
import type { GroupedMails } from '@/components/mail/MailGroupPanel';
|
import type { GroupedMails } from '@/components/mail/MailGroupPanel';
|
||||||
|
import type { Mail } from '@/api/mail';
|
||||||
|
|
||||||
export interface MailListProps {
|
export interface MailListProps {
|
||||||
mails: Mail[];
|
mails: Mail[];
|
||||||
@@ -30,6 +31,73 @@ function formatDate(dateStr: string): string {
|
|||||||
return formatSmartDate(dateStr) || dateStr;
|
return formatSmartDate(dateStr) || dateStr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Collapsible group section for grouped mail display
|
||||||
|
function GroupSection({
|
||||||
|
group,
|
||||||
|
level = 0,
|
||||||
|
selectedMailId,
|
||||||
|
selectedMailIds,
|
||||||
|
onSelectMail,
|
||||||
|
onToggleSelect,
|
||||||
|
renderMailItem,
|
||||||
|
}: {
|
||||||
|
group: GroupedMails;
|
||||||
|
level?: number;
|
||||||
|
selectedMailId: string | null;
|
||||||
|
selectedMailIds: Set<string>;
|
||||||
|
onSelectMail: (mail: Mail) => void;
|
||||||
|
onToggleSelect: (mailId: string) => void;
|
||||||
|
renderMailItem: (mail: Mail) => React.ReactNode;
|
||||||
|
}) {
|
||||||
|
const [collapsed, setCollapsed] = useState(false);
|
||||||
|
const hasSubGroups = group.subGroups && group.subGroups.length > 0;
|
||||||
|
// Count total mails including subGroups
|
||||||
|
const totalCount = hasSubGroups
|
||||||
|
? group.subGroups!.reduce((sum, sub) => sum + sub.mails.length, 0)
|
||||||
|
: group.mails.length;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<li className="bg-secondary-50/30">
|
||||||
|
<div
|
||||||
|
className="flex items-center gap-2 px-3 py-2 bg-secondary-100/70 cursor-pointer hover:bg-secondary-100"
|
||||||
|
style={{ paddingLeft: `${level * 12 + 12}px` }}
|
||||||
|
onClick={() => setCollapsed(!collapsed)}
|
||||||
|
>
|
||||||
|
{collapsed ? (
|
||||||
|
<ChevronRight className="w-3.5 h-3.5 text-secondary-500 flex-shrink-0" />
|
||||||
|
) : (
|
||||||
|
<ChevronDown className="w-3.5 h-3.5 text-secondary-500 flex-shrink-0" />
|
||||||
|
)}
|
||||||
|
<span className="text-xs font-semibold text-secondary-700">{group.label}</span>
|
||||||
|
<span className="text-[10px] text-secondary-400">({totalCount})</span>
|
||||||
|
</div>
|
||||||
|
{!collapsed && (
|
||||||
|
<>
|
||||||
|
<ul className="divide-y divide-secondary-100">
|
||||||
|
{group.mails.map((mail) => renderMailItem(mail))}
|
||||||
|
</ul>
|
||||||
|
{hasSubGroups && (
|
||||||
|
<ul className="border-l border-secondary-200 ml-3">
|
||||||
|
{group.subGroups!.map((sub) => (
|
||||||
|
<GroupSection
|
||||||
|
key={sub.key}
|
||||||
|
group={sub}
|
||||||
|
level={level + 1}
|
||||||
|
selectedMailId={selectedMailId}
|
||||||
|
selectedMailIds={selectedMailIds}
|
||||||
|
onSelectMail={onSelectMail}
|
||||||
|
onToggleSelect={onToggleSelect}
|
||||||
|
renderMailItem={renderMailItem}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export function MailList({
|
export function MailList({
|
||||||
mails,
|
mails,
|
||||||
groupedMails,
|
groupedMails,
|
||||||
@@ -174,32 +242,15 @@ export function MailList({
|
|||||||
>
|
>
|
||||||
{groupedMails && groupedMails.length > 0 ? (
|
{groupedMails && groupedMails.length > 0 ? (
|
||||||
groupedMails.map((group) => (
|
groupedMails.map((group) => (
|
||||||
<li key={group.key} className="bg-secondary-50/50">
|
<GroupSection
|
||||||
<div className="flex items-center gap-2 px-3 py-2 bg-secondary-100/70 sticky top-0 z-10">
|
key={group.key}
|
||||||
<ChevronDown className="w-3.5 h-3.5 text-secondary-500" />
|
group={group}
|
||||||
<span className="text-xs font-semibold text-secondary-700">{group.label}</span>
|
selectedMailId={selectedMailId}
|
||||||
<span className="text-[10px] text-secondary-400">({group.mails.length})</span>
|
selectedMailIds={selectedMailIds}
|
||||||
</div>
|
onSelectMail={onSelectMail}
|
||||||
<ul className="divide-y divide-secondary-100">
|
onToggleSelect={onToggleSelect}
|
||||||
{group.mails.map((mail) => renderMailItem(mail))}
|
renderMailItem={renderMailItem}
|
||||||
</ul>
|
/>
|
||||||
{group.subGroups && group.subGroups.length > 0 && (
|
|
||||||
<ul className="pl-3 border-l border-secondary-200">
|
|
||||||
{group.subGroups.map((sub) => (
|
|
||||||
<li key={sub.key}>
|
|
||||||
<div className="flex items-center gap-2 px-3 py-1.5 bg-secondary-50">
|
|
||||||
<ChevronDown className="w-3 h-3 text-secondary-400" />
|
|
||||||
<span className="text-[11px] font-medium text-secondary-600">{sub.label}</span>
|
|
||||||
<span className="text-[10px] text-secondary-400">({sub.mails.length})</span>
|
|
||||||
</div>
|
|
||||||
<ul className="divide-y divide-secondary-100">
|
|
||||||
{sub.mails.map((mail) => renderMailItem(mail))}
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
)}
|
|
||||||
</li>
|
|
||||||
))
|
))
|
||||||
) : (
|
) : (
|
||||||
mails.map((mail) => renderMailItem(mail))
|
mails.map((mail) => renderMailItem(mail))
|
||||||
|
|||||||
Reference in New Issue
Block a user