返回 DeepSeek-Reasonix
TopicbarExportMenu.tsx
根目录 / desktop / frontend / src / components / TopicbarExportMenu.tsx
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
53 lines Plain Text