From 5b283288f036c0ce2f2ed0e74bf145fb88afb48e Mon Sep 17 00:00:00 2001 From: gunshiz Date: Mon, 28 Sep 2026 19:57:47 +0700 Subject: [PATCH] feat : improve preview for export dialog --- components/public/constellation-image.tsx | 5 ++- components/public/guide-image-export.tsx | 39 ++++++++++++++++------- components/public/structured-guide.tsx | 21 ++++++++---- components/public/team-search.tsx | 1 + components/public/weapon-image-export.tsx | 5 ++- lib/guides/image-export.test.tsx | 16 +++++++++- 6 files changed, 66 insertions(+), 21 deletions(-) diff --git a/components/public/constellation-image.tsx b/components/public/constellation-image.tsx index f22cc98..2bae3a1 100644 --- a/components/public/constellation-image.tsx +++ b/components/public/constellation-image.tsx @@ -43,7 +43,10 @@ export function ConstellationImage({ : "text-[76px]", )} > - {`${guide.character.name}’s Constellations`} +

diff --git a/components/public/guide-image-export.tsx b/components/public/guide-image-export.tsx index 604dfba..0a0613c 100644 --- a/components/public/guide-image-export.tsx +++ b/components/public/guide-image-export.tsx @@ -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(null); const [captureMode, setCaptureMode] = useState< @@ -214,6 +216,7 @@ export function GuideImageExport({ const [previewUrl, setPreviewUrl] = useState(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 ? ( @@ -302,16 +308,26 @@ export function GuideImageExport({ const exportPreview = (

ตัวอย่างภาพส่งออก

-
- {previewing ? ( - - ) : previewUrl ? ( +
+ {previewUrl ? ( // This is a temporary client-generated blob, not an optimizable remote asset. // eslint-disable-next-line @next/next/no-img-element - ตัวอย่างภาพที่จะส่งออก + Preview image + ) : previewing ? ( + ) : ( -

ไม่สามารถสร้างตัวอย่างได้

+

เกิดข้อผิดพลาดในการโชว์ Preview

)} + {previewing && previewUrl ? ( +
+ +
+ ) : null}
); @@ -444,7 +460,8 @@ export function GuideImageExport({ @@ -452,12 +469,12 @@ export function GuideImageExport({ เลือกตัวเลือกที่ต้องการก่อนส่งออกภาพ {previewPosition === "right" ? ( -
+
{additionalOptions} {exportOptions}
-
{exportPreview}
+
{exportPreview}
) : ( <> diff --git a/components/public/structured-guide.tsx b/components/public/structured-guide.tsx index 27dcc51..d963393 100644 --- a/components/public/structured-guide.tsx +++ b/components/public/structured-guide.tsx @@ -1,4 +1,5 @@ import Image from "next/image"; +import localFont from "next/font/local"; import Link from "next/link"; import { ArrowLeftIcon, ArrowUpRightIcon } from "lucide-react"; import type { ReactNode } from "react"; @@ -64,6 +65,11 @@ import { import { publicGuideSections } from "@/lib/guides/public-sections"; import { cn } from "@/lib/utils"; +const kanitSemiBold = localFont({ + src: "../../public/Kanit-SemiBold.ttf", + weight: "600", +}); + export function publicSections(guide: PublicGuide) { return publicGuideSections(guide); } @@ -252,6 +258,7 @@ function GuideOverview({ showLanguageSelector={false} localizeFileName={false} captureClassName="p-0" + modeSelection="direct" >
-

+

อาวุธที่แนะนำ

@@ -432,7 +439,7 @@ function WeaponSectionContent({ "drop-shadow-[0_4px_3px_rgba(0,0,0,0.8)]", )} /> -

+

Weapons

@@ -649,7 +656,7 @@ function ArtifactSectionContent({ const recommendedConditions = profile?.recommendedConditions ?? []; const recommendedCards = recommendedConditions.map((condition, conditionIndex) => (
- {exporting && recommendedConditions.length > 1 ?

{condition.name || }

: null} + {exporting && recommendedConditions.length > 1 ?

{condition.name || }

: null} {condition.note ?

{condition.note}

: null} {profile ? ( @@ -706,7 +713,7 @@ function ArtifactSectionContent({ return (
) : null} -
+
-

+

อาร์ติแฟกต์และสแตทที่แนะนำ

@@ -734,7 +741,7 @@ function ArtifactSectionContent({ "drop-shadow-[0_4px_3px_rgba(0,0,0,0.8)]", )} /> -

+

Artifacts & Stats

diff --git a/components/public/team-search.tsx b/components/public/team-search.tsx index f73ad1d..7b20a03 100644 --- a/components/public/team-search.tsx +++ b/components/public/team-search.tsx @@ -499,6 +499,7 @@ export function TeamSearch({ groups }: { groups: PublicGuide["teamGroups"] }) { showLanguageSelector={false} promptForLanguage previewPosition="right" + compactDialog > diff --git a/components/public/weapon-image-export.tsx b/components/public/weapon-image-export.tsx index 6625563..a55d95d 100644 --- a/components/public/weapon-image-export.tsx +++ b/components/public/weapon-image-export.tsx @@ -54,7 +54,10 @@ export function WeaponComparisonImage({

- {`${guide.character.name}’s Weapon Comparison`} +

{metric === "overallPercent" ? ( diff --git a/lib/guides/image-export.test.tsx b/lib/guides/image-export.test.tsx index 2ece489..5d1ae1b 100644 --- a/lib/guides/image-export.test.tsx +++ b/lib/guides/image-export.test.tsx @@ -28,6 +28,8 @@ describe("image export controls", () => { />, ); + expect(html).toContain("เปรียบเทียบอาวุธ Raiden"); + expect(html).toContain("Raiden’s Weapon Comparison"); expect(html).not.toContain("Condition 1"); expect(html).not.toContain("เงื่อนไข 1"); }); @@ -44,6 +46,17 @@ describe("image export controls", () => { expect(html.match(/ { + const html = renderToStaticMarkup( + + Poster + , + ); + expect(html).toContain("ส่งออกภาพ"); + expect(html).not.toContain("ตั้งค่าการส่งออกภาพ"); + expect(html.match(/ { const html = renderToStaticMarkup( @@ -55,6 +68,7 @@ describe("image export controls", () => { expect(html).toContain("ไทย"); expect(html).toContain("English"); expect(html).toContain("ตัวอย่างภาพส่งออก"); + expect(html).toContain("h-[clamp(8rem,50svh,32rem)]"); expect(html).toContain('data-slot="dialog-footer"'); expect(html).toContain("sticky bottom-0"); expect(html.match(/>ส่งออกภาพ<\/button>/gu)).toHaveLength(1); @@ -86,7 +100,7 @@ describe("image export controls", () => { const group = { rows: [], note } as unknown as PublicGuide["constellationGroups"][number]; const html = renderToStaticMarkup(createElement(ConstellationImage, { guide, group })); expect(html).toContain("Raiden’s Constellations"); - expect(html).not.toContain("กลุ่มดาวของ Raiden"); + expect(html).toContain("กลุ่มดาว Raiden"); expect(html).toContain("Team Damage"); expect(html).toContain("ดาเมจทั้งทีม"); expect(html).toContain('data-export-note="true"');