feat : improve preview for export dialog

This commit is contained in:
2026-09-28 19:57:47 +07:00 Unverified
parent 63341e892b
commit 5b283288f0
6 changed files with 66 additions and 21 deletions
+28 -11
View File
@@ -180,6 +180,7 @@ export function GuideImageExport({
modeSelection = "dialog",
additionalOptions,
previewPosition = "top",
compactDialog = false,
}: {
children: ReactNode;
fileName: string;
@@ -195,9 +196,10 @@ export function GuideImageExport({
showLanguageSelector?: boolean;
promptForLanguage?: boolean;
localizeFileName?: boolean;
modeSelection?: "dialog" | "inline";
modeSelection?: "dialog" | "inline" | "direct";
additionalOptions?: ReactNode;
previewPosition?: "top" | "right";
compactDialog?: boolean;
}) {
const captureRef = useRef<HTMLDivElement>(null);
const [captureMode, setCaptureMode] = useState<
@@ -214,6 +216,7 @@ export function GuideImageExport({
const [previewUrl, setPreviewUrl] = useState<string | null>(null);
const [previewing, setPreviewing] = useState(false);
const previewEnabled = modeSelection === "inline" || modeDialogOpen;
const squarePreview = height !== undefined && width === height;
function requestCapture() {
setModeDialogOpen(false);
@@ -228,7 +231,10 @@ export function GuideImageExport({
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={() => setModeDialogOpen(true)}
onClick={() => {
if (modeSelection === "direct") setCaptureMode("with-notes");
else setModeDialogOpen(true);
}}
>
{capturing ? (
<Spinner data-icon="inline-start" />
@@ -302,16 +308,26 @@ export function GuideImageExport({
const exportPreview = (
<div className="flex flex-col gap-2">
<p className="text-sm font-medium">ตัวอย่างภาพส่งออก</p>
<div className="flex min-h-32 items-center justify-center overflow-hidden rounded-lg border bg-muted/30 p-2">
{previewing ? (
<Spinner className="size-6" />
) : previewUrl ? (
<div
className={cn(
"relative flex h-[clamp(8rem,50svh,32rem)] items-center justify-center overflow-hidden rounded-lg border bg-muted/30 p-2",
modeSelection === "dialog" && squarePreview && "lg:h-80",
)}
>
{previewUrl ? (
// This is a temporary client-generated blob, not an optimizable remote asset.
// eslint-disable-next-line @next/next/no-img-element
<img src={previewUrl} alt="ตัวอย่างภาพที่จะส่งออก" className="max-h-[50svh] max-w-full object-contain" />
<img src={previewUrl} alt="Preview image" className="size-full object-contain" />
) : previewing ? (
<Spinner className="size-6" />
) : (
<p className="text-sm text-muted-foreground">ไม่สามารถสร้างตัวอย่างได้</p>
<p className="text-sm text-muted-foreground">เกิดข้อผิดพลาดในการโชว์ Preview</p>
)}
{previewing && previewUrl ? (
<div className="absolute inset-0 flex items-center justify-center bg-background/50">
<Spinner className="size-6" />
</div>
) : null}
</div>
</div>
);
@@ -444,7 +460,8 @@ export function GuideImageExport({
<DialogContent
className={cn(
"max-h-[90svh] overflow-y-auto sm:max-w-3xl",
previewPosition === "right" && "sm:w-fit sm:max-w-[calc(100%-2rem)]",
previewPosition === "right" && "lg:max-w-[calc(100%-2rem)]",
previewPosition === "right" && (squarePreview || compactDialog ? "lg:w-156" : "lg:w-236"),
)}
>
<DialogHeader>
@@ -452,12 +469,12 @@ export function GuideImageExport({
<DialogDescription>เลือกตัวเลือกที่ต้องการก่อนส่งออกภาพ</DialogDescription>
</DialogHeader>
{previewPosition === "right" ? (
<div className="grid min-h-0 gap-4 lg:grid-cols-[16rem_fit-content(42rem)]">
<div className="grid min-h-0 gap-4 lg:grid-cols-[16rem_minmax(0,1fr)]">
<div className="flex min-w-0 flex-col gap-4">
{additionalOptions}
{exportOptions}
</div>
<div className="min-w-0">{exportPreview}</div>
<div className="w-full min-w-0">{exportPreview}</div>
</div>
) : (
<>