Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 36 additions & 2 deletions src/pages/Typing/components/Setting/AdvancedSetting.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
import styles from './index.module.css'
import { isIgnoreCaseAtom, isShowAnswerOnHoverAtom, isShowPrevAndNextWordAtom, isTextSelectableAtom, randomConfigAtom } from '@/store'
import {
errorHandleConfigAtom,
isIgnoreCaseAtom,
isShowAnswerOnHoverAtom,
isShowPrevAndNextWordAtom,
isTextSelectableAtom,
randomConfigAtom,
} from '@/store'
import { Switch } from '@headlessui/react'
import * as ScrollArea from '@radix-ui/react-scroll-area'
import { useAtom } from 'jotai'
Expand All @@ -11,6 +18,7 @@ export default function AdvancedSetting() {
const [isIgnoreCase, setIsIgnoreCase] = useAtom(isIgnoreCaseAtom)
const [isTextSelectable, setIsTextSelectable] = useAtom(isTextSelectableAtom)
const [isShowAnswerOnHover, setIsShowAnswerOnHover] = useAtom(isShowAnswerOnHoverAtom)
const [errorHandleConfig, setErrorHandleConfig] = useAtom(errorHandleConfigAtom)

const onToggleRandom = useCallback(
(checked: boolean) => {
Expand Down Expand Up @@ -42,17 +50,41 @@ export default function AdvancedSetting() {
},
[setIsTextSelectable],
)

const onToggleShowAnswerOnHover = useCallback(
(checked: boolean) => {
setIsShowAnswerOnHover(checked)
},
[setIsShowAnswerOnHover],
)

const onToggleErrorHandleType = useCallback(
(checked: boolean) => {
setErrorHandleConfig((prev) => ({
...prev,
type: checked ? 'continue' : 'clear',
}))
},
[setErrorHandleConfig],
)

return (
<ScrollArea.Root className="flex-1 select-none overflow-y-auto ">
<ScrollArea.Viewport className="h-full w-full px-3">
<div className={styles.tabContent}>
<div className={styles.section}>
<span className={styles.sectionLabel}>错误处理方式</span>
<span className={styles.sectionDescription}>选择在输入错误时的处理方式</span>
<div className={styles.switchBlock}>
<Switch checked={errorHandleConfig.type === 'continue'} onChange={onToggleErrorHandleType} className="switch-root">
<span aria-hidden="true" className="switch-thumb" />
</Switch>
<span className="text-right text-xs font-normal leading-tight text-gray-600">
{errorHandleConfig.type === 'continue' ? '错误后继续输入(可删除修改)' : '错误后清空重新输入(传统方式)'}
</span>
</div>
</div>

<div className={styles.section}>
<span className={styles.sectionLabel}>章节乱序</span>
<span className={styles.sectionDescription}>开启后,每次练习章节中单词会随机排序。下一章节生效</span>
Expand All @@ -79,7 +111,9 @@ export default function AdvancedSetting() {
</div>
<div className={styles.section}>
<span className={styles.sectionLabel}>是否忽略大小写</span>
<span className={styles.sectionDescription}>开启后,输入时不区分大小写,如输入“hello”和“Hello”都会被认为是正确的</span>
<span className={styles.sectionDescription}>
开启后,输入时不区分大小写,如输入&quot;hello&quot;和&quot;Hello&quot;都会被认为是正确的
</span>
<div className={styles.switchBlock}>
<Switch checked={isIgnoreCase} onChange={onToggleIgnoreCase} className="switch-root">
<span aria-hidden="true" className="switch-thumb" />
Expand Down
1 change: 1 addition & 0 deletions src/pages/Typing/components/Setting/ViewSetting.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ export default function ViewSetting() {
</div>
</div>
</div>

<button className="my-btn-primary ml-4 disabled:bg-gray-300" type="button" onClick={onResetFontSize} title="重置字体设置">
重置字体设置
</button>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export type WordAddAction = {
export type WordDeleteAction = {
type: 'delete'
length: number
event?: FormEvent<HTMLTextAreaElement> | KeyboardEvent
}

// composition api is not ready yet
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ export default function KeyEventHandler({ updateInput }: { updateInput: (updateO
return
}

// 处理删除键
if (char === 'Backspace') {
updateInput({ type: 'delete', length: 1, event: e })
return
}

if (isLegal(char) && !e.altKey && !e.ctrlKey && !e.metaKey) {
updateInput({ type: 'add', value: char, event: e })
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { WordUpdateAction } from '../InputHandler'
import { TypingContext } from '@/pages/Typing/store'
import type { FormEvent } from 'react'
import type { FormEvent, KeyboardEvent } from 'react'
import { useCallback, useContext, useEffect, useRef } from 'react'

export default function TextAreaHandler({ updateInput }: { updateInput: (updateObj: WordUpdateAction) => void }) {
Expand Down Expand Up @@ -29,6 +29,14 @@ export default function TextAreaHandler({ updateInput }: { updateInput: (updateO
}
}

// 添加键盘事件处理,特别是处理删除键
const onKeyDown = (e: KeyboardEvent<HTMLTextAreaElement>) => {
if (e.key === 'Backspace') {
e.preventDefault()
updateInput({ type: 'delete', length: 1, event: e })
}
}

const onBlur = useCallback(() => {
if (!textareaRef.current) return

Expand All @@ -44,6 +52,7 @@ export default function TextAreaHandler({ updateInput }: { updateInput: (updateO
autoFocus
spellCheck="false"
onInput={onInput}
onKeyDown={onKeyDown}
onBlur={onBlur}
onCompositionStart={() => {
alert('您正在使用输入法,请关闭输入法。')
Expand Down
28 changes: 23 additions & 5 deletions src/pages/Typing/components/WordPanel/components/Word/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { TypingContext, TypingStateActionType } from '@/pages/Typing/store'
import {
currentChapterAtom,
currentDictInfoAtom,
errorHandleConfigAtom,
isIgnoreCaseAtom,
isShowAnswerOnHoverAtom,
isTextSelectableAtom,
Expand Down Expand Up @@ -51,6 +52,7 @@ export default function WordComponent({ word, onFinish }: { word: Word; onFinish

const [showTipAlert, setShowTipAlert] = useState(false)
const wordPronunciationIconRef = useRef<WordPronunciationIconRef>(null)
const errorHandleConfig = useAtomValue(errorHandleConfigAtom)

useEffect(() => {
// run only when word changes
Expand All @@ -75,7 +77,7 @@ export default function WordComponent({ word, onFinish }: { word: Word; onFinish
(updateAction: WordUpdateAction) => {
switch (updateAction.type) {
case 'add':
if (wordState.hasWrong) return
if (errorHandleConfig.type === 'clear' && wordState.hasWrong) return

if (updateAction.value === ' ') {
updateAction.event.preventDefault()
Expand All @@ -89,11 +91,22 @@ export default function WordComponent({ word, onFinish }: { word: Word; onFinish
}
break

case 'delete':
setWordState((state) => {
const newInputWord = state.inputWord.slice(0, -1)
state.inputWord = newInputWord

if (newInputWord.length < state.letterStates.length) {
state.letterStates[newInputWord.length] = 'normal'
}
})
break

default:
console.warn('unknown update type', updateAction)
}
},
[wordState.hasWrong, setWordState],
[wordState.hasWrong, errorHandleConfig.type, setWordState],
)

const handleHoverWord = useCallback((checked: boolean) => {
Expand Down Expand Up @@ -173,7 +186,7 @@ export default function WordComponent({ word, onFinish }: { word: Word; onFinish
* 目前不影响生产环境,猜测是因为开发环境下 react 会两次调用 useEffect 从而展示了这个 warning
* 但这终究是一个 bug,需要修复
*/
if (wordState.hasWrong || inputLength === 0 || wordState.displayWord.length === 0) {
if (inputLength === 0 || wordState.displayWord.length === 0) {
return
}

Expand Down Expand Up @@ -212,10 +225,15 @@ export default function WordComponent({ word, onFinish }: { word: Word; onFinish
playBeepSound()
setWordState((state) => {
state.letterStates[inputLength - 1] = 'wrong'
state.hasWrong = true

// 根据错误处理配置决定行为
if (errorHandleConfig.type === 'clear') {
state.hasWrong = true
state.letterTimeArray = []
}

state.hasMadeInputWrong = true
state.wrongCount += 1
state.letterTimeArray = []

if (state.letterMistake[inputLength - 1]) {
state.letterMistake[inputLength - 1].push(inputChar)
Expand Down
5 changes: 5 additions & 0 deletions src/store/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { idDictionaryMap } from '@/resources/dictionary'
import { correctSoundResources, keySoundResources, wrongSoundResources } from '@/resources/soundResource'
import type {
Dictionary,
ErrorHandleType,
InfoPanelState,
LoopWordTimesOption,
PhoneticType,
Expand Down Expand Up @@ -112,3 +113,7 @@ export const dismissStartCardDateAtom = atomWithStorage<Date | null>(DISMISS_STA

// for dev test
// dismissStartCardDateAtom = atom<Date | null>(new Date())

export const errorHandleConfigAtom = atomForConfig('errorHandleConfig', {
type: 'clear' as ErrorHandleType, // 'continue' 或 'clear',初始默认为 clear
})
7 changes: 7 additions & 0 deletions src/typings/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,10 @@ export type WordDictationType = 'hideAll' | 'hideVowel' | 'hideConsonant' | 'ran
* 如果是通过点击 resultScreen 中的默写本章按钮打开的,则关闭默写模式
*/
export type WordDictationOpenBy = 'user' | 'auto'

/**
* 错误处理方式
* continue: 错误后继续输入,允许修改
* clear: 错误后清空重新输入(传统方式)
*/
export type ErrorHandleType = 'continue' | 'clear'
Loading