| 1 | import { useEffect, useRef } from "react"; |
| 2 | import { FileDown, FileImage, FileJson, FileSearch, FileText } from "lucide-react"; |
| 3 | import { t } from "../lib/i18n"; |
| 4 | import type { TopicbarSessionActionsProps } from "./TopicbarSessionActions"; |
| 5 | |
| 6 | export function TopicbarExportMenu({ initialFocus, exportSession, onClose }: { |
| 7 | initialFocus: "first" | "last"; |
| 8 | exportSession: TopicbarSessionActionsProps["exportSession"]; |
| 9 | onClose: (restoreFocus: boolean) => void; |
| 10 | }) { |
| 11 | const menuRef = useRef<HTMLDivElement>(null); |
| 12 | useEffect(() => { |
| 13 | const items = menuRef.current?.querySelectorAll<HTMLButtonElement>("button"); |
| 14 | if (items) (initialFocus === "last" ? items[items.length - 1] : items[0])?.focus(); |
| 15 | }, [initialFocus]); |
| 16 | |
| 17 | return ( |
| 18 | <div ref={menuRef} className="topicbar__export-menu" role="menu" aria-label={t("topicBar.export")} |
| 19 | onKeyDown={(event) => { |
| 20 | if (event.key === "Escape") { |
| 21 | event.preventDefault(); |
| 22 | event.stopPropagation(); |
| 23 | onClose(true); |
| 24 | return; |
| 25 | } |
| 26 | if (!["ArrowDown", "ArrowUp", "Home", "End"].includes(event.key)) return; |
| 27 | event.preventDefault(); |
| 28 | event.stopPropagation(); |
| 29 | const items = Array.from(event.currentTarget.querySelectorAll<HTMLButtonElement>("button")); |
| 30 | const current = items.indexOf(document.activeElement as HTMLButtonElement); |
| 31 | const next = event.key === "Home" ? 0 : event.key === "End" ? items.length - 1 |
| 32 | : (current + (event.key === "ArrowDown" ? 1 : -1) + items.length) % items.length; |
| 33 | items[next]?.focus(); |
| 34 | }} |
| 35 | > |
| 36 | {([ |
| 37 | ["markdown", FileText, t("topicBar.exportMarkdown")], |
| 38 | ["json", FileJson, t("topicBar.exportJson")], |
| 39 | ["pdf", FileDown, t("topicBar.exportPdf")], |
| 40 | ["image", FileImage, t("topicBar.exportImage")], |
| 41 | ["diagnostic", FileSearch, t("topicBar.exportDiagnostic")], |
| 42 | ] as const).map(([format, Icon, label]) => ( |
| 43 | <button key={format} tabIndex={-1} type="button" role="menuitem" onClick={() => { |
| 44 | onClose(true); |
| 45 | exportSession(format); |
| 46 | }}> |
| 47 | <Icon size={13} /><span>{label}</span> |
| 48 | </button> |
| 49 | ))} |
| 50 | </div> |
| 51 | ); |
| 52 | } |
| 53 |