diff options
author | polwex <polwex@sortug.com> | 2025-08-16 13:38:27 +0700 |
---|---|---|
committer | polwex <polwex@sortug.com> | 2025-08-16 13:38:27 +0700 |
commit | 274a7bbb1f82e99cdc9876f6d0de430585282797 (patch) | |
tree | 70322887617038fccab1bae7dcaf0875ed9c42ed | |
parent | 9a1e8af707acec9bfabd4c5be9ba6595d7f14c3e (diff) |
beautiful flip animation by chatgpt
-rw-r--r-- | src/components/tones/ToneSelectorClient.tsx | 119 |
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> ); } |