summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorpolwex <polwex@sortug.com>2025-08-16 13:38:27 +0700
committerpolwex <polwex@sortug.com>2025-08-16 13:38:27 +0700
commit274a7bbb1f82e99cdc9876f6d0de430585282797 (patch)
tree70322887617038fccab1bae7dcaf0875ed9c42ed
parent9a1e8af707acec9bfabd4c5be9ba6595d7f14c3e (diff)
beautiful flip animation by chatgpt
-rw-r--r--src/components/tones/ToneSelectorClient.tsx119
1 files changed, 81 insertions, 38 deletions
diff --git a/src/components/tones/ToneSelectorClient.tsx b/src/components/tones/ToneSelectorClient.tsx
index 48580a4..8abbd38 100644
--- a/src/components/tones/ToneSelectorClient.tsx
+++ b/src/components/tones/ToneSelectorClient.tsx
@@ -27,7 +27,7 @@ import { Label } from "@/components/ui/label";
import { Skeleton } from "@/components/ui/skeleton"; // For loading state
import { MutationOrder, ToneQuery } from "@/lib/types/phonetics";
import { ProsodySyllable } from "@/lib/types/cards";
-import { ArrowLeft, ArrowRight, Volume2 } from "lucide-react";
+import { ArrowLeft, ArrowRight, Volume2, FlipHorizontal2 } from "lucide-react";
function getColorByTone(tone: string): string {
if (tone === "mid") return "blue";
@@ -192,43 +192,86 @@ type IProps = {
goNext: () => void;
};
function Inner({ isLoading, currentWord, goPrev, goNext }: IProps) {
- return isLoading ? (
- <Card>
- <CardHeader>
- <Skeleton className="h-12 w-3/4" />
- </CardHeader>
- <CardContent className="space-y-4">
- <Skeleton className="h-8 w-1/2" />
- <Skeleton className="h-20 w-full" />
- <Skeleton className="h-6 w-full" />
- </CardContent>
- </Card>
- ) : currentWord ? (
- <Card>
- <CardHeader>
- <CardTitle className="text-center">Current Word</CardTitle>
- </CardHeader>
- <CardContent>
- <ProminentToneDisplay word={currentWord} />
- {/* You can add more details from WordData here if needed, like definitions */}
- </CardContent>
- <CardFooter className="justify-between">
- <ArrowLeft onClick={goPrev} />
- <ArrowRight onClick={goNext} />
- </CardFooter>
- </Card>
- ) : (
- <Card>
- <CardHeader>
- <CardTitle className="text-center">No Word Found</CardTitle>
- </CardHeader>
- <CardContent>
- <p className="text-center text-gray-600">
- Could not find a Thai word matching your criteria. Try different
- selections.
- </p>
- </CardContent>
- </Card>
+ const [isFlipped, setIsFlipped] = useState(false);
+ const toggleFlip = () => setIsFlipped((f) => !f);
+
+ if (isLoading) {
+ return (
+ <Card>
+ <CardHeader>
+ <Skeleton className="h-12 w-3/4" />
+ </CardHeader>
+ <CardContent className="space-y-4">
+ <Skeleton className="h-8 w-1/2" />
+ <Skeleton className="h-20 w-full" />
+ <Skeleton className="h-6 w-full" />
+ </CardContent>
+ </Card>
+ );
+ }
+
+ if (!currentWord) {
+ return (
+ <Card>
+ <CardHeader>
+ <CardTitle className="text-center">No Word Found</CardTitle>
+ </CardHeader>
+ <CardContent>
+ <p className="text-center text-gray-600">
+ Could not find a Thai word matching your criteria. Try different
+ selections.
+ </p>
+ </CardContent>
+ </Card>
+ );
+ }
+
+ return (
+ <div className="relative w-full max-w-2xl mx-auto h-100 perspective">
+ <button
+ type="button"
+ onClick={toggleFlip}
+ className="absolute z-20 top-2 right-2 p-2 rounded-md hover:bg-slate-100 dark:hover:bg-slate-800 transition-colors"
+ title="Flip"
+ >
+ <FlipHorizontal2 size={18} />
+ </button>
+ <div
+ className={`relative w-full h-full rounded-xl shadow-xl transition-transform duration-700 ease-in-out transform-style-preserve-3d ${
+ isFlipped ? "rotate-y-180" : ""
+ }`}
+ >
+ {/* Front */}
+ <div className="absolute inset-0 bg-white dark:bg-slate-800 rounded-xl backface-hidden flex flex-col">
+ <div className="px-6 pt-6 pb-2">
+ <h3 className="text-center text-lg font-semibold">Current Word</h3>
+ </div>
+ <div className="flex-1 px-6 overflow-auto">
+ <ProminentToneDisplay word={currentWord} />
+ </div>
+ <div className="px-6 py-4 flex items-center justify-between">
+ <ArrowLeft onClick={goPrev} className="cursor-pointer" />
+ <ArrowRight onClick={goNext} className="cursor-pointer" />
+ </div>
+ </div>
+
+ {/* Back */}
+ <div className="absolute inset-0 bg-slate-50 dark:bg-slate-700 rounded-xl backface-hidden rotate-y-180 flex flex-col">
+ <div className="px-6 pt-6 pb-2">
+ <h3 className="text-center text-lg font-semibold">Details</h3>
+ </div>
+ <div className="flex-1 px-6 py-2 overflow-auto text-sm text-slate-700 dark:text-slate-100">
+ {/* Placeholder for user-provided data */}
+ <pre className="whitespace-pre-wrap break-words text-xs opacity-80">
+ {JSON.stringify(currentWord, null, 2)}
+ </pre>
+ </div>
+ <div className="px-6 py-4 flex items-center justify-center text-xs opacity-60">
+ Back of card – replace with your data
+ </div>
+ </div>
+ </div>
+ </div>
);
}