Files
ittoview/src/components/practice/ProficientInputArea.tsx
2026-05-10 15:43:34 +01:00

74 lines
2.3 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.

import { useEffect, useRef } from 'react'
import clsx from 'clsx'
import { AnimatePresence, motion } from 'framer-motion'
interface ProficientInputAreaProps {
value: string
isComposing: boolean
inputLocked: boolean
hasError: boolean
onChange: (value: string) => void
onCompositionStart: () => void
onCompositionEnd: (value: string) => void
showLockedHint?: boolean
}
export function ProficientInputArea({
value,
isComposing,
inputLocked,
hasError,
onChange,
onCompositionStart,
onCompositionEnd,
showLockedHint = true,
}: ProficientInputAreaProps) {
const inputRef = useRef<HTMLInputElement | null>(null)
useEffect(() => {
if (inputLocked) return
inputRef.current?.focus()
}, [inputLocked, value])
return (
<div className="flex w-full max-w-3xl flex-col gap-3 practice-input-area">
<input
ref={inputRef}
type="text"
value={value}
onChange={(e) => onChange(e.target.value)}
onCompositionStart={onCompositionStart}
onCompositionEnd={(e) => onCompositionEnd(e.currentTarget.value)}
disabled={inputLocked}
placeholder="输入当前分组中的一个完整条目"
className={clsx(
'w-full rounded-xl border px-4 py-3 text-base md:text-lg',
'bg-white dark:bg-gray-800/80 text-gray-900 dark:text-gray-100',
'transition-all duration-200 focus:outline-none focus:ring-2',
isComposing && 'border-gray-300 dark:border-gray-600 opacity-80',
!isComposing && !hasError && 'border-gray-300 dark:border-gray-600 focus:ring-indigo-400 focus:border-indigo-400',
!isComposing && hasError && 'border-red-400 focus:ring-red-400 focus:border-red-400',
inputLocked && 'cursor-not-allowed opacity-60'
)}
autoComplete="off"
autoCorrect="off"
autoCapitalize="off"
spellCheck="false"
/>
<AnimatePresence>
{showLockedHint && inputLocked && (
<motion.div
initial={{ opacity: 0, y: -6 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -6 }}
className="text-sm text-gray-500 dark:text-gray-400"
>
</motion.div>
)}
</AnimatePresence>
</div>
)
}