dramaling-vocab-learning/frontend/app/review-simple/components/SimpleFlipCard.tsx

180 lines
6.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

'use client'
import { useState, useRef, useEffect, useCallback } from 'react'
import { SimpleCard, CONFIDENCE_LEVELS } from '../data'
interface SimpleFlipCardProps {
card: SimpleCard
onAnswer: (confidence: number) => void
}
export function SimpleFlipCard({ card, onAnswer }: SimpleFlipCardProps) {
const [isFlipped, setIsFlipped] = useState(false)
const [selectedConfidence, setSelectedConfidence] = useState<number | null>(null)
const [cardHeight, setCardHeight] = useState<number>(400)
const frontRef = useRef<HTMLDivElement>(null)
const backRef = useRef<HTMLDivElement>(null)
// 智能高度計算 (復用原有邏輯)
useEffect(() => {
const updateCardHeight = () => {
if (backRef.current) {
const backHeight = backRef.current.scrollHeight
// 響應式最小高度設定 (復用原有響應式邏輯)
const minHeightByScreen = window.innerWidth <= 480 ? 300 :
window.innerWidth <= 768 ? 350 : 400
const finalHeight = Math.max(minHeightByScreen, backHeight)
setCardHeight(finalHeight)
}
}
const timer = setTimeout(updateCardHeight, 100)
window.addEventListener('resize', updateCardHeight)
return () => {
clearTimeout(timer)
window.removeEventListener('resize', updateCardHeight)
}
}, [card.word, card.definition, card.example])
const handleFlip = useCallback(() => {
setIsFlipped(!isFlipped)
}, [isFlipped])
const handleConfidenceSelect = useCallback((level: number) => {
setSelectedConfidence(level)
}, [])
const handleSubmit = () => {
if (selectedConfidence) {
onAnswer(selectedConfidence)
// 重置狀態為下一張卡片準備
setIsFlipped(false)
setSelectedConfidence(null)
}
}
const hasAnswered = selectedConfidence !== null
return (
<div className="max-w-md mx-auto">
{/* 高級3D翻卡容器 (復用原有設計) */}
<div
className="flip-card-container"
onClick={handleFlip}
style={{
height: `${cardHeight}px`,
minHeight: '400px',
transition: 'height 0.4s cubic-bezier(0.4, 0, 0.2, 1)'
}}
>
<div
className={`flip-card ${isFlipped ? 'flipped' : ''}`}
style={{
transform: isFlipped ? 'rotateY(180deg)' : 'rotateY(0deg)'
}}
>
{/* 正面 - 單字 (復用原有設計風格) */}
<div ref={frontRef} className="flip-card-front bg-white">
<div className="p-8 h-full">
<div className="space-y-4">
<p className="text-lg text-gray-700 mb-6 text-left">
</p>
<div className="flex-1 flex items-center justify-center mt-6">
<div className="bg-gray-50 rounded-lg p-8 w-full text-center">
<h3 className="text-4xl font-bold text-gray-900 mb-6">{card.word}</h3>
<span className="text-lg text-gray-500">{card.pronunciation}</span>
</div>
</div>
</div>
</div>
</div>
{/* 背面 - 詳細資訊 (復用原有布局) */}
<div ref={backRef} className="flip-card-back bg-white">
<div className="p-8 h-full">
<div className="space-y-4 pb-6">
{/* 定義區塊 */}
<div className="content-block">
<h3></h3>
<p>{card.definition}</p>
</div>
{/* 例句區塊 */}
<div className="content-block">
<h3></h3>
<p className="italic mb-2">"{card.example}"</p>
<p className="text-gray-600 text-sm">"{card.translation}"</p>
</div>
</div>
</div>
</div>
</div>
</div>
{/* 信心度選擇 - 復用原有的精美設計 */}
{isFlipped && (
<div className="mt-6 space-y-3">
<h3 className="text-lg font-semibold text-gray-900 mb-4 text-left">
</h3>
<div className="grid grid-cols-5 gap-3">
{CONFIDENCE_LEVELS.map(({ level, label, color }) => {
const isSelected = selectedConfidence === level
const colorClasses = {
'bg-red-500': 'bg-red-100 text-red-700 border-red-200 hover:bg-red-200',
'bg-orange-500': 'bg-orange-100 text-orange-700 border-orange-200 hover:bg-orange-200',
'bg-yellow-500': 'bg-yellow-100 text-yellow-700 border-yellow-200 hover:bg-yellow-200',
'bg-blue-500': 'bg-blue-100 text-blue-700 border-blue-200 hover:bg-blue-200',
'bg-green-500': 'bg-green-100 text-green-700 border-green-200 hover:bg-green-200'
}[color] || 'bg-gray-100 text-gray-700 border-gray-200 hover:bg-gray-200'
return (
<button
key={level}
onClick={(e) => {
e.stopPropagation()
handleConfidenceSelect(level)
}}
className={`confidence-button ${colorClasses} ${
isSelected ? 'selected' : ''
}`}
>
<div className="flex items-center justify-center h-8">
<span className="text-sm">{label}</span>
</div>
</button>
)
})}
</div>
{/* 提交按鈕 - 選擇後顯示 */}
{hasAnswered && (
<button
onClick={(e) => {
e.stopPropagation()
handleSubmit()
}}
className="w-full bg-blue-600 text-white py-3 px-6 rounded-lg font-semibold hover:bg-blue-700 transition-colors mt-4"
>
</button>
)}
</div>
)}
{/* 翻卡提示 - 只在未翻轉時顯示 */}
{!isFlipped && (
<div className="text-center text-gray-500">
<p></p>
</div>
)}
</div>
)
}