feat : improve export image dialog

This commit is contained in:
2026-09-20 19:02:46 +07:00 Unverified
parent 71bc247b73
commit bde2f1d09e
5 changed files with 96 additions and 175 deletions
+51 -66
View File
@@ -13,13 +13,13 @@ import {
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog";
import { ExportLanguageDialog } from "@/components/public/export-language-dialog";
import {
ExportLanguageToggle,
useExportLanguage,
type ExportLanguage,
} from "@/components/public/export-language";
import { Spinner } from "@/components/ui/spinner";
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group";
import { cn } from "@/lib/utils";
const EXPORT_WIDTH = 1200;
@@ -89,55 +89,70 @@ export function GuideImageExport({
const capturing = captureMode !== null;
const [storedLanguage, setStoredLanguage] = useExportLanguage();
const language = controlledLanguage ?? storedLanguage;
const [pendingCaptureMode, setPendingCaptureMode] = useState<
"with-notes" | "without-notes" | null
>(null);
const [selectedMode, setSelectedMode] = useState<"with-notes" | "without-notes">(
"with-notes",
);
const [modeDialogOpen, setModeDialogOpen] = useState(false);
function requestCapture(mode: "with-notes" | "without-notes") {
function requestCapture() {
setModeDialogOpen(false);
if (promptForLanguage) setPendingCaptureMode(mode);
else setCaptureMode(mode);
setCaptureMode(selectedMode);
}
function selectLanguage(nextLanguage: ExportLanguage) {
if (!pendingCaptureMode) return;
setStoredLanguage(nextLanguage);
setCaptureMode(pendingCaptureMode);
setPendingCaptureMode(null);
}
function exportButton(
mode: "with-notes" | "without-notes" | null,
label: string,
) {
const loading = capturing && (mode === null || captureMode === mode);
function exportButton(label: string) {
return (
<Button
key={mode ?? "export"}
type="button"
variant="outline"
size="sm"
className={mobileIconOnly ? "max-sm:size-7 max-sm:px-0 max-sm:has-data-[icon=inline-start]:pl-0" : undefined}
disabled={capturing || disabled}
onClick={() => {
if (mode) requestCapture(mode);
else if (allowWithoutNotes) setModeDialogOpen(true);
else requestCapture("with-notes");
}}
onClick={() => setModeDialogOpen(true)}
>
{loading ? (
{capturing ? (
<Spinner data-icon="inline-start" />
) : (
<DownloadIcon data-icon="inline-start" />
)}
<span className={mobileIconOnly ? "sr-only sm:not-sr-only" : undefined}>
{loading ? "กำลังส่งออก…" : label}
{capturing ? "กำลังส่งออก…" : label}
</span>
</Button>
);
}
const exportSettings = (
<>
{showLanguageSelector || promptForLanguage ? (
<div className="flex flex-col gap-2">
<p className="text-sm font-medium">ภาษาในภาพ</p>
<ExportLanguageToggle language={language} onChange={setStoredLanguage} />
</div>
) : null}
{allowWithoutNotes ? (
<div className="flex flex-col gap-2">
<p className="text-sm font-medium">โน้ต</p>
<ToggleGroup
aria-label="การแสดงโน้ตในภาพส่งออก"
variant="outline"
value={[selectedMode]}
onValueChange={(values) => {
const next = values[0];
if (next === "with-notes" || next === "without-notes") setSelectedMode(next);
}}
>
<ToggleGroupItem value="with-notes">รวมโน้ต</ToggleGroupItem>
<ToggleGroupItem value="without-notes">ไม่รวมโน้ต</ToggleGroupItem>
</ToggleGroup>
</div>
) : null}
<Button type="button" disabled={capturing || disabled} onClick={requestCapture}>
{capturing ? <Spinner data-icon="inline-start" /> : <DownloadIcon data-icon="inline-start" />}
{capturing ? "กำลังส่งออก…" : buttonLabel}
</Button>
</>
);
useEffect(() => {
if (!capturing) return;
@@ -204,52 +219,22 @@ export function GuideImageExport({
return (
<>
{showLanguageSelector ? (
<ExportLanguageToggle language={language} onChange={setStoredLanguage} />
) : null}
<div className="flex flex-wrap gap-2">
{allowWithoutNotes && modeSelection === "inline"
? [
exportButton("with-notes", buttonLabel),
exportButton("without-notes", "ส่งออกภาพโดยไม่รวมโน้ต"),
]
: exportButton(null, buttonLabel)}
</div>
{allowWithoutNotes && modeSelection === "dialog" ? (
{modeSelection === "inline" ? (
exportSettings
) : (
<div className="flex flex-wrap gap-2">{exportButton(buttonLabel)}</div>
)}
{modeSelection === "dialog" ? (
<Dialog open={modeDialogOpen} onOpenChange={setModeDialogOpen}>
<DialogContent>
<DialogHeader>
<DialogTitle>เลือกรูปแบบการส่งออก</DialogTitle>
<DialogDescription>เลือกว่าจะรวมโน้ตไว้ในภาพหรือไม่</DialogDescription>
<DialogTitle>ตั้งค่าการส่งออกภาพ</DialogTitle>
<DialogDescription>เลือกตัวเลือกที่ต้องการก่อนส่งออกภาพ</DialogDescription>
</DialogHeader>
<div className="flex flex-col gap-2 sm:flex-row">
<Button
type="button"
variant="secondary"
className="flex-1"
onClick={() => requestCapture("with-notes")}
>
ส่งออกพร้อมโน้ต
</Button>
<Button
type="button"
variant="secondary"
className="flex-1"
onClick={() => requestCapture("without-notes")}
>
ส่งออกโดยไม่รวมโน้ต
</Button>
</div>
{exportSettings}
</DialogContent>
</Dialog>
) : null}
<ExportLanguageDialog
open={pendingCaptureMode !== null}
onOpenChange={(open) => {
if (!open) setPendingCaptureMode(null);
}}
onSelect={selectLanguage}
/>
{capturing
? createPortal(
<div