feat(练习): 新增过程背诵练习模块
- 实现知识领域和过程的背诵练习功能 - 矩阵布局:知识领域格子横跨5列,过程按过程组分列 - 动态输入框:根据答案长度自动调整横线数量 - 实时验证:逐字符验证,错误标红,正确后自动跳转 - 辅助信息:知识领域显示裁剪因素,过程显示主要作用 - 长按显示答案:支持触摸、鼠标和键盘(空格键) - TAB键切换:按顺序切换格子,自动跳过空单元格 - 支持输入法和批量粘贴 - 完整的无障碍支持(aria-live、tabIndex、scrollIntoView) - 进度跟踪:顶部显示答题进度条 新增文件: - src/utils/practice.ts - 工具函数 - src/hooks/useLongPress.ts - 长按 Hook - src/components/practice/ - 练习组件 - src/pages/ProcessPracticePage.tsx - 练习页面 via [HAPI](https://hapi.run) Co-Authored-By: HAPI <noreply@hapi.run>
This commit is contained in:
62
src/hooks/useLongPress.ts
Normal file
62
src/hooks/useLongPress.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
import { useCallback, useRef } from 'react'
|
||||
|
||||
interface UseLongPressOptions {
|
||||
onLongPress: (cellId: string) => void
|
||||
onLongPressEnd: () => void
|
||||
delay?: number
|
||||
}
|
||||
|
||||
/**
|
||||
* 自定义长按 Hook
|
||||
* 支持触摸、鼠标和键盘(空格键)
|
||||
*/
|
||||
export function useLongPress(
|
||||
cellId: string,
|
||||
{ onLongPress, onLongPressEnd, delay = 600 }: UseLongPressOptions
|
||||
) {
|
||||
const timerRef = useRef<number>()
|
||||
const isLongPressRef = useRef(false)
|
||||
|
||||
const start = useCallback(() => {
|
||||
isLongPressRef.current = false
|
||||
timerRef.current = window.setTimeout(() => {
|
||||
isLongPressRef.current = true
|
||||
onLongPress(cellId)
|
||||
}, delay)
|
||||
}, [cellId, onLongPress, delay])
|
||||
|
||||
const cancel = useCallback(() => {
|
||||
if (timerRef.current) {
|
||||
clearTimeout(timerRef.current)
|
||||
}
|
||||
// 只有在长按成功触发后才调用 onLongPressEnd
|
||||
if (isLongPressRef.current) {
|
||||
onLongPressEnd()
|
||||
}
|
||||
isLongPressRef.current = false
|
||||
}, [onLongPressEnd])
|
||||
|
||||
return {
|
||||
onPointerDown: start,
|
||||
onPointerUp: cancel,
|
||||
onPointerLeave: cancel,
|
||||
onPointerCancel: cancel,
|
||||
onContextMenu: (e: React.MouseEvent) => {
|
||||
e.preventDefault()
|
||||
cancel()
|
||||
},
|
||||
// 键盘支持(空格键长按)
|
||||
onKeyDown: (e: React.KeyboardEvent) => {
|
||||
if (e.key === ' ' && !e.repeat) {
|
||||
e.preventDefault()
|
||||
start()
|
||||
}
|
||||
},
|
||||
onKeyUp: (e: React.KeyboardEvent) => {
|
||||
if (e.key === ' ') {
|
||||
e.preventDefault()
|
||||
cancel()
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user