import { Copy, FileText, Share2, } from "lucide-react"; import React, { useCallback, useState, useRef, useEffect } from "react"; import { useI18n } from "../../application/i18n/I18nProvider"; import { type VaultNote, } from "../../domain/notes"; import { copyToClipboard } from "../keychain/utils"; import { toast } from "../ui/toast"; export interface NoteExportMenuProps { note: VaultNote | null; allNotes: VaultNote[]; onExportNote: (note: VaultNote) => void; onExportAll: () => void; className?: string; } export const NoteExportMenu: React.FC = ({ note, allNotes, onExportNote, onExportAll, className = "", }) => { const { t } = useI18n(); const [open, setOpen] = useState(false); const menuRef = useRef(null); const triggerRef = useRef(null); const closeAndRestoreFocus = useCallback(() => { setOpen(false); requestAnimationFrame(() => triggerRef.current?.focus()); }, []); useEffect(() => { if (!open) return; const handleClickOutside = (e: MouseEvent) => { if (menuRef.current && !menuRef.current.contains(e.target as Node)) { setOpen(false); } }; const handleKeyDown = (e: KeyboardEvent) => { if (e.key === "Escape") { e.preventDefault(); closeAndRestoreFocus(); return; } if (e.key === "Tab") { setOpen(false); return; } const items = Array.from( menuRef.current?.querySelectorAll('[role="menuitem"]:not(:disabled)') ?? [], ); if (!items.length) return; const current = items.indexOf(document.activeElement as HTMLButtonElement); let next = current; if (e.key === "ArrowDown") next = (current + 1 + items.length) % items.length; else if (e.key === "ArrowUp") next = (current - 1 + items.length) % items.length; else if (e.key === "Home") next = 0; else if (e.key === "End") next = items.length - 1; else return; e.preventDefault(); items[next]?.focus(); }; window.addEventListener("mousedown", handleClickOutside); window.addEventListener("keydown", handleKeyDown); requestAnimationFrame(() => { menuRef.current?.querySelector('[role="menuitem"]')?.focus(); }); return () => { window.removeEventListener("mousedown", handleClickOutside); window.removeEventListener("keydown", handleKeyDown); }; }, [closeAndRestoreFocus, open]); const handleExportSingleMarkdown = () => { if (!note) return; onExportNote(note); closeAndRestoreFocus(); }; const handleCopyMarkdown = async () => { if (!note) return; const ok = await copyToClipboard(note.content); if (ok) { toast.success(t("common.copied") || "已复制到剪贴板"); } closeAndRestoreFocus(); }; const handleExportAllZip = () => { if (!allNotes.length) return; onExportAll(); closeAndRestoreFocus(); }; return (
{open && (
{note && ( <>
{t("notes.export.currentNote")}
)}
{t("notes.export.allNotes")}
)}
); };