// // Copyright (c) 2025-2026 rustmailer.com (https://rustmailer.com) // // This file is part of the Bichon Email Archiving Project // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . import { useRef } from 'react' import { X, Trash2, Upload } from 'lucide-react' import { cn } from '@/lib/utils' import { Badge } from '@/components/ui/badge' import { Button } from '@/components/ui/button' import { Separator } from '@/components/ui/separator' import { Tooltip, TooltipTrigger, TooltipContent, } from '@/components/ui/tooltip' import { useSearchContext } from './context' import { useTranslation } from 'react-i18next' type MailBulkActionsProps = { children?: React.ReactNode } export function MailBulkActions({ children }: MailBulkActionsProps) { const { selected, setSelected, setOpen, setToDelete, setCurrentEnvelope } = useSearchContext() const toolbarRef = useRef(null) const { t } = useTranslation() const selectedCount = Array.from(selected.values()) .reduce((sum, set) => sum + set.size, 0) const handleClearSelection = () => { setSelected(new Map()) } const handleDelete = () => { setToDelete(new Map()) selected.forEach((ids, accountId) => { setToDelete(prev => { const next = new Map(prev) next.set(accountId, new Set(ids)) return next }) }) setOpen('delete') } const handleRestore = () => { setCurrentEnvelope(undefined); setOpen('restore') } const handleKeyDown = (e: React.KeyboardEvent) => { const buttons = toolbarRef.current?.querySelectorAll('button') if (!buttons || buttons.length === 0) return const currentIndex = Array.from(buttons).findIndex( btn => btn === document.activeElement ) switch (e.key) { case 'ArrowRight': { e.preventDefault() const next = (currentIndex + 1) % buttons.length buttons[next]?.focus() break } case 'ArrowLeft': { e.preventDefault() const prev = currentIndex === 0 ? buttons.length - 1 : currentIndex - 1 buttons[prev]?.focus() break } case 'Home': e.preventDefault() buttons[0]?.focus() break case 'End': e.preventDefault() buttons[buttons.length - 1]?.focus() break case 'Escape': { const target = e.target as HTMLElement const active = document.activeElement as HTMLElement const isFromDropdown = target.closest('[data-slot="dropdown-menu-trigger"]') || active.closest('[data-slot="dropdown-menu-trigger"]') || target.closest('[data-slot="dropdown-menu-content"]') || active.closest('[data-slot="dropdown-menu-content"]') if (!isFromDropdown) { e.preventDefault() handleClearSelection() } break } } } if (selectedCount === 0) return null return ( <>
{/* Clear Selection */} {/* Selected Count */}
{selectedCount} {' '} {t('search.bulkActions.selected', { count: selectedCount })}
{t('search.bulkActions.restoreDesc')} {/* Delete */} {t('search.bulkActions.deleteDesc')} {children}
) }