返回 presentation-ai
Filter.tsx
1 "use client";
2
3 import { Loader2, SlidersHorizontal, Type, Upload } from "lucide-react";
4 import { useState } from "react";
5 import { toast } from "sonner";
6
7 import { useThemePanelState } from "@/components/presentation/edit-panel/sections/theme/theme-panel-state";
8 import { extractThemeFromPptx } from "@/lib/presentation/pptx-theme-extractor";
9 import { cn } from "@/lib/utils";
10 import { openThemeCustomizer } from "./customize-theme";
11
12 function handleCustomizeClick() {
13 openThemeCustomizer();
14 }
15
16 const MAX_FILE_SIZE = 50 * 1024 * 1024; // 50MB
17
18 export function ThemeFilter() {
19 const {
20 setShowFont,
21 showFont,
22 setOpenCreateThemeModal,
23 setImportedThemeData,
24 } = useThemePanelState();
25 const [isImporting, setIsImporting] = useState(false);
26
27 function handleFontClick() {
28 setShowFont(!useThemePanelState.getState().showFont);
29 }
30
31 function handleImportClick() {
32 const input = document.createElement("input");
33 input.type = "file";
34 input.accept = ".pptx";
35 input.onchange = async (e) => {
36 const file = (e.target as HTMLInputElement).files?.[0];
37 if (!file) return;
38
39 if (file.size > MAX_FILE_SIZE) {
40 toast.error("File is too large", {
41 description: "Maximum file size is 50MB.",
42 });
43 return;
44 }
45
46 try {
47 setIsImporting(true);
48 const themeData = await extractThemeFromPptx(file);
49 setImportedThemeData(themeData);
50 setOpenCreateThemeModal(true);
51 } catch {
52 try {
53 toast.error("Failed to extract theme", {
54 description:
55 "The file could not be read. Please ensure it is a valid .pptx file.",
56 });
57 } catch (reactDoctorCatchError) {
58 setIsImporting(false);
59 throw reactDoctorCatchError;
60 }
61 }
62 setIsImporting(false);
63 };
64 input.click();
65 }
66
67 return (
68 <div className="relative flex flex-wrap gap-2 px-4">
69 {/* Customize Button */}
70 <button
71 onClick={handleCustomizeClick}
72 className="flex min-w-0 flex-1 items-center justify-center gap-2 rounded-lg border border-border px-3 py-2 text-sm whitespace-nowrap transition-colors hover:bg-muted"
73 type="button"
74 >
75 <SlidersHorizontal className="size-4 shrink-0 text-foreground" />
76 <span className="text-foreground">Customize</span>
77 </button>
78
79 {/* Font Button */}
80 <button
81 onClick={handleFontClick}
82 className={cn(
83 "flex shrink-0 items-center justify-center gap-2 rounded-lg border border-border px-3 py-2 text-sm whitespace-nowrap transition-colors hover:bg-muted",
84 showFont && "bg-muted",
85 )}
86 type="button"
87 >
88 <Type className="size-4 shrink-0 text-foreground" />
89 <span className="text-foreground">Font</span>
90 </button>
91
92 {/* Import Button */}
93 <button
94 onClick={handleImportClick}
95 disabled={isImporting}
96 className={cn(
97 "flex shrink-0 items-center justify-center gap-2 rounded-lg border border-border px-3 py-2 text-sm whitespace-nowrap transition-colors hover:bg-muted",
98 isImporting && "cursor-not-allowed opacity-60",
99 )}
100 title="Import Theme from PPTX"
101 type="button"
102 >
103 {isImporting ? (
104 <Loader2 className="size-4 shrink-0 animate-spin text-foreground" />
105 ) : (
106 <Upload className="size-4 shrink-0 text-foreground" />
107 )}
108 <span className="text-foreground">
109 {isImporting ? "Importing..." : "Import"}
110 </span>
111 </button>
112 </div>
113 );
114 }
115
115 lines Plain Text