diff --git a/src/components/PairCards.css b/src/components/PairCards.css index d5b82dd..265f636 100644 --- a/src/components/PairCards.css +++ b/src/components/PairCards.css @@ -71,12 +71,31 @@ cursor: pointer; padding: 0; flex-shrink: 0; - transition: background 0.15s; + transition: background 0.15s, color 0.15s; + color: #7A6E55; -webkit-user-select: none; user-select: none; } .pair-icon-btn:hover { background: #E0DAC8; } -.pair-icon-btn.active { background: #E0DAC8; } +.pair-icon-btn.active { background: #C4A85A22; color: #B07840; } + +/* Hold-to-translate button */ +.pair-hold-wrap { + width: 38px; + height: 38px; + flex-shrink: 0; + cursor: pointer; + -webkit-user-select: none; + user-select: none; + border-radius: 10px; + overflow: visible; +} + +/* Ring fill animation — 2 seconds */ +@keyframes holdRing { + from { stroke-dashoffset: 100.53; } + to { stroke-dashoffset: 0; } +} /* ── Image area ── */ .pair-image-wrap { @@ -278,6 +297,11 @@ background: #5C3D22; color: #F5EDE0; } +.pair-btn-locked { + background: #E0DDD5; + color: #B0A898; + cursor: not-allowed; +} .pair-btn-yes { background: #3D7055; color: #fff; diff --git a/src/components/PairSentenceCard.jsx b/src/components/PairSentenceCard.jsx index 97263fc..efef815 100644 --- a/src/components/PairSentenceCard.jsx +++ b/src/components/PairSentenceCard.jsx @@ -93,11 +93,16 @@ function toPlainText(sentence) { const LANG_LABELS = { sv: 'Svenska', en: 'English', de: 'Deutsch' } const LANG_TTS = { sv: 'sv-SE', en: 'en-US', de: 'de-DE' } +// Circumference of r=16 circle ≈ 100.53 +const RING_C = 2 * Math.PI * 16 + export default function PairSentenceCard({ card, onComplete }) { const [done, setDone] = useState(false) const [activeChip, setActiveChip] = useState(null) const [showTranslation, setShowTranslation] = useState(false) - const holdTimer = useRef(null) + const [holding, setHolding] = useState(false) + const [unlocked, setUnlocked] = useState(false) + const holdCompleted = useRef(false) const lang = card.lang || 'de' const native = lang === 'de' ? 'en' : 'de' @@ -117,6 +122,7 @@ export default function PairSentenceCard({ card, onComplete }) { } function handleConfirm() { + if (!unlocked) return setDone(true) setActiveChip(null) triggerConfetti() @@ -130,14 +136,21 @@ export default function PairSentenceCard({ card, onComplete }) { utt.lang = LANG_TTS[lang] || 'de-DE' utt.rate = 0.9 window.speechSynthesis.speak(utt) + setUnlocked(true) } - function startTranslation() { - holdTimer.current = setTimeout(() => setShowTranslation(true), 150) + function startHold() { + holdCompleted.current = false + setHolding(true) + setShowTranslation(true) } - function endTranslation() { - clearTimeout(holdTimer.current) - setShowTranslation(false) + function endHold() { + setHolding(false) + if (!holdCompleted.current) setShowTranslation(false) + } + function onHoldComplete() { + holdCompleted.current = true + setUnlocked(true) } return ( @@ -159,17 +172,7 @@ export default function PairSentenceCard({ card, onComplete }) { {isObject && } - {/* Header below image */} -
- {LANG_LABELS[lang] || lang} - - - +{card.meta?.points ?? 2} Punkte - -
-
- -
e.stopPropagation()}> +
e.stopPropagation()} style={{ paddingTop: 18 }}> {/* Sentence + action buttons */}

Satz

@@ -184,7 +187,7 @@ export default function PairSentenceCard({ card, onComplete }) { color: '#7A7060', transition: 'opacity 0.18s', margin: 0, - marginTop: showTranslation ? 0 : '-1.7em', /* overlay effect */ + marginTop: showTranslation ? 0 : '-1.7em', pointerEvents: 'none', }}> {resolveSentence(hint, card.placeholders, null, null)} @@ -193,30 +196,52 @@ export default function PairSentenceCard({ card, onComplete }) {
- {/* TTS */} - +
)}
@@ -235,9 +260,9 @@ export default function PairSentenceCard({ card, onComplete }) {
diff --git a/src/components/PairWordCard.jsx b/src/components/PairWordCard.jsx index c0eabb0..2cc386d 100644 --- a/src/components/PairWordCard.jsx +++ b/src/components/PairWordCard.jsx @@ -139,17 +139,7 @@ export default function PairWordCard({ card, onComplete }) { {isObject && }
- {/* Header below image */} -
- {lang === 'sv' ? 'Svenska' : lang === 'en' ? 'English' : 'Deutsch'} - - - +{card.meta?.points ?? 3} Punkte - -
-
- -
e.stopPropagation()}> +
e.stopPropagation()} style={{ paddingTop: 18 }}>

Frage

{resolveSentence(sentence, card.placeholders, handleChipClick, activeChip?.id)} diff --git a/src/components/PairYesNoCard.jsx b/src/components/PairYesNoCard.jsx index 7c6a5d1..29a4773 100644 --- a/src/components/PairYesNoCard.jsx +++ b/src/components/PairYesNoCard.jsx @@ -124,17 +124,7 @@ export default function PairYesNoCard({ card, onComplete }) { {isObject && }

- {/* Header below image */} -
- {lang === 'sv' ? 'Svenska' : lang === 'en' ? 'English' : 'Deutsch'} - - - +{card.meta?.points ?? 2} Punkte - -
-
- -
e.stopPropagation()}> +
e.stopPropagation()} style={{ paddingTop: 18 }}>

Frage

{resolveSentence(sentence, card.placeholders, handleChipClick, activeChip?.id)}