-
Notifications
You must be signed in to change notification settings - Fork 2.5k
feat: 默认禁用光标位置可调整,支持通过 touchmove 调整光标位置 #6897
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
📝 Walkthrough## Walkthrough
本次更改在 VirtualInput 组件中新增了 `cursor` 属性,支持用户通过点击字符或触摸拖动来调整光标位置(默认静态不可动)。更新了组件文档、样式和示例,并完善了相关的测试用例以覆盖光标位置调整的行为。
## Changes
| 文件/路径 | 变更摘要 |
|-----------------------------------------------|------------------------------------------------------------------|
| src/components/virtual-input/index.zh.md | 文档新增 `cursor` 属性说明,支持 `'movable'` 和 `'static'` 两种模式,默认 `'static'`。 |
| src/components/virtual-input/virtual-input.less | 样式中将光标动画类名改为变量,新增 `-caret-dragging` 状态以禁用闪烁动画。 |
| src/components/virtual-input/virtual-input.tsx | 新增 `cursor` 属性,实现光标点击和拖动调整,增加相关状态和事件处理逻辑。 |
| src/components/virtual-input/demos/demo1.tsx | 在示例中添加 `cursor='movable'` 属性,新增默认静态光标示例,调整部分字体大小。 |
| src/components/virtual-input/tests/virtual-input.test.tsx | 新增触摸事件辅助函数,完善光标位置调整相关测试,统一模拟字符宽度,更新测试异步处理。 |
## Sequence Diagram(s)
```mermaid
sequenceDiagram
participant User
participant VirtualInput
participant DOM
User->>VirtualInput: 点击字符或触摸拖动(cursor='movable')
VirtualInput->>VirtualInput: 计算并更新 caretPosition
VirtualInput->>DOM: 渲染新光标位置
User->>VirtualInput: 键盘输入/删除
VirtualInput->>VirtualInput: 按输入模式调整 caretPosition
VirtualInput->>DOM: 更新显示 Suggested reviewers
Poem
|
commit: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (1)
src/components/virtual-input/virtual-input.tsx (1)
198-248
: 触摸拖拽实现完善,建议提取魔法数字。实现了完整的触摸拖拽功能,包括边界处理和超时机制。建议将硬编码的阈值提取为常量。
+const TOUCH_DRAG_THRESHOLD = 20 // px +const TOUCH_DRAG_TIMEOUT = 200 // ms const handleTouchStart = (e: React.TouchEvent<HTMLDivElement>) => { // ... - if (distance < 20) { + if (distance < TOUCH_DRAG_THRESHOLD) { // ... const handleTouchMove = (e: React.TouchEvent<HTMLDivElement>) => { // ... - }, 200) + }, TOUCH_DRAG_TIMEOUT)
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
src/components/virtual-input/index.zh.md
(1 hunks)src/components/virtual-input/virtual-input.less
(2 hunks)src/components/virtual-input/virtual-input.tsx
(8 hunks)
🧰 Additional context used
🪛 GitHub Actions: Check
src/components/virtual-input/virtual-input.tsx
[warning] 181-181: React warning: An update to ForwardRef inside a test was not wrapped in act(...). See https://reactjs.org/link/wrap-tests-with-act
[warning] 55-55: React warning: An update to ForwardRef inside a test was not wrapped in act(...). See https://reactjs.org/link/wrap-tests-with-act
⏰ Context from checks skipped due to timeout of 90000ms (3)
- GitHub Check: compressed-size
- GitHub Check: build preview
- GitHub Check: size
🔇 Additional comments (8)
src/components/virtual-input/virtual-input.less (2)
72-72
: 使用动态类前缀变量保持一致性。将硬编码的类名改为使用变量形式,与文件中其他地方保持一致。
83-87
: 拖拽时禁用光标闪烁效果。新增的拖拽状态样式合理地禁用了光标动画,提升了拖拽时的用户体验。
src/components/virtual-input/index.zh.md (1)
26-26
: 文档更新准确完整。新增的
adjustableCaret
属性文档说明清晰,格式规范。src/components/virtual-input/virtual-input.tsx (5)
28-28
: 新属性定义合理。
adjustableCaret
属性默认为false
,确保了向后兼容性。Also applies to: 45-45
90-96
: 字符宽度测量的潜在问题。当前实现假设所有字符宽度相同,这对于等宽字体有效,但对于包含中英文混合或特殊字符的内容可能不准确。
请确认是否需要支持可变宽度字符的场景。如果需要,可能需要更精确的光标定位算法。
97-109
: 光标位置更新逻辑清晰。通过检查值是否与预期匹配来决定光标移动方式,处理了受控组件的边界情况。
185-196
: 点击定位实现巧妙。通过判断点击位置相对于字符中心来决定光标位置,用户体验良好。
55-55
: 测试中存在 React act() 警告。管道测试显示有状态更新未在
act()
中包装。虽然不影响功能,但建议修复测试以消除警告。请检查相关测试代码,确保状态更新都正确包装在
act()
中。Also applies to: 181-181
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #6897 +/- ##
==========================================
+ Coverage 92.80% 92.84% +0.03%
==========================================
Files 336 336
Lines 7244 7283 +39
Branches 1784 1831 +47
==========================================
+ Hits 6723 6762 +39
+ Misses 513 485 -28
- Partials 8 36 +28 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (5)
src/components/virtual-input/virtual-input.tsx (3)
65-65
: 完善注释内容当前注释不够完整,建议补充说明该 ref 的具体用途。
- }>({}) // 临时记录虚拟键盘输入,在下次更新时用于判断光标位置如何调整 + }>({}) // 临时记录虚拟键盘输入的值和操作类型,用于在值更新后智能调整光标位置
93-98
: 字符宽度测量可能不准确当前实现仅测量第一个字符的宽度,假设所有字符宽度相同。这在使用等宽字体时没问题,但对于比例字体或混合字符类型(如数字和小数点)可能导致光标定位不准确。
建议考虑更精确的测量方案,或在文档中说明此限制。
211-212
: 触摸检测阈值应考虑屏幕密度硬编码的 20px 阈值在不同屏幕密度的设备上可能表现不一致。建议根据设备像素比调整或使其可配置。
- if (distance < 20) { - // 20px 阈值可调整 + const threshold = 20 * (window.devicePixelRatio || 1) + if (distance < threshold) { + // 根据设备像素比调整阈值或者考虑将其作为组件属性暴露,允许开发者自定义。
src/components/virtual-input/tests/virtual-input.test.tsx (2)
315-315
: 使用更具体的断言建议使用更具体的断言来提高测试失败时的可读性。
- expect(caretContainer).toBeTruthy() + expect(caretContainer).not.toBeNull()Also applies to: 541-541, 621-621
492-654
: 建议补充更多测试场景当前测试覆盖较好,但建议考虑增加以下测试场景:
- 多点触控的处理
- 快速连续触摸的处理
- 组件禁用状态下的触摸行为
另外,建议在后续迭代中考虑:
- RTL(从右到左)文本的支持
- 无障碍访问的影响(屏幕阅读器用户如何使用此功能)
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
src/components/virtual-input/demos/demo1.tsx
(3 hunks)src/components/virtual-input/index.zh.md
(1 hunks)src/components/virtual-input/tests/virtual-input.test.tsx
(12 hunks)src/components/virtual-input/virtual-input.tsx
(8 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- src/components/virtual-input/index.zh.md
- src/components/virtual-input/demos/demo1.tsx
🧰 Additional context used
🪛 GitHub Check: codecov/patch
src/components/virtual-input/virtual-input.tsx
[warning] 248-249: src/components/virtual-input/virtual-input.tsx#L248-L249
Added lines #L248 - L249 were not covered by tests
⏰ Context from checks skipped due to timeout of 90000ms (1)
- GitHub Check: size
🔇 Additional comments (5)
src/components/virtual-input/virtual-input.tsx (2)
28-28
: 新增的属性定义合理新增的
allowCaretPositioning
属性定义为可选布尔类型,默认值为false
,保证了向后兼容性。Also applies to: 45-45
237-244
: 超时处理机制设计良好使用超时机制来处理
touchend
事件可能不触发的边缘情况是很好的防御性编程实践。src/components/virtual-input/tests/virtual-input.test.tsx (3)
84-103
: 测试环境模拟设置合理通过 mock
getBoundingClientRect
为所有字符设置统一宽度,确保了测试的一致性和可重复性。
492-587
: 触摸拖动测试覆盖全面测试用例设计良好,覆盖了以下场景:
- 光标左右移动
- 拖动状态的 CSS 类切换
- 边界条件处理
- 非光标附近的触摸事件处理
测试实现质量高,场景覆盖完整。
589-654
: 禁用状态测试完善正确验证了当
allowCaretPositioning
未启用时,触摸和点击都不会改变光标位置,确保了功能的正确禁用。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (1)
src/components/virtual-input/virtual-input.tsx (1)
202-250
: 优化触摸事件处理的用户体验。触摸事件处理逻辑基本正确,但建议进行以下优化:
- 考虑添加 preventDefault() 防止触摸时触发页面滚动
- 调整超时时间 500ms 可能过长,建议减少到 200-300ms
- 动态调整触摸阈值 20px 阈值可以根据设备 DPI 进行调整
const handleTouchMove = (e: React.TouchEvent<HTMLDivElement>) => { if (!touchDataRef.current || mergedProps.cursor === 'locked') return + + e.preventDefault() setIsCaretDragging(true) // ... existing logic ... touchMoveTimeoutRef.current = setTimeout(() => { setIsCaretDragging(false) touchMoveTimeoutRef.current = null - }, 500) + }, 200) }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
src/components/virtual-input/demos/demo1.tsx
(3 hunks)src/components/virtual-input/index.zh.md
(1 hunks)src/components/virtual-input/tests/virtual-input.test.tsx
(12 hunks)src/components/virtual-input/virtual-input.tsx
(8 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
- src/components/virtual-input/demos/demo1.tsx
- src/components/virtual-input/index.zh.md
- src/components/virtual-input/tests/virtual-input.test.tsx
⏰ Context from checks skipped due to timeout of 90000ms (4)
- GitHub Check: compressed-size
- GitHub Check: check
- GitHub Check: build preview
- GitHub Check: size
🔇 Additional comments (7)
src/components/virtual-input/virtual-input.tsx (7)
28-28
: 新增的 cursor 属性设计合理。属性定义清晰,类型安全,默认值设置为 'locked' 保证了向后兼容性。
Also applies to: 45-45
61-61
: 光标位置状态管理逻辑正确。状态初始化和更新逻辑合理,正确处理了键盘输入和外部值变更的场景。
Also applies to: 73-73, 100-111
66-74
: Ref 类型定义已改进,字符宽度测量逻辑合理。很好地解决了之前关于 ref 类型的建议,现在使用了具体的类型定义。字符宽度测量对于光标位置计算是必要的。
Also applies to: 93-98
188-199
: 字符点击定位逻辑实现正确。正确处理了禁用状态和锁定光标的判断,点击位置计算逻辑合理,事件传播控制得当。
145-164
: 键盘输入与光标位置的集成处理正确。输入和删除操作正确使用了光标位置进行字符串操作,临时记录机制有助于后续位置调整。
260-260
: DOM 结构更新合理支持新功能。拖拽状态样式类、触摸事件处理器、以及 ref 引用都正确集成到了 DOM 结构中。
Also applies to: 274-276, 280-280, 289-289
304-308
: 清除按钮处理逻辑简化合理。移除显式的光标位置重置是正确的,因为值变更时的 effect 会自动将光标位置设置到末尾。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (1)
src/components/virtual-input/virtual-input.tsx (1)
222-245
: 触摸移动事件处理需要优化性能。虽然逻辑正确,但频繁的状态更新可能影响性能。建议添加节流优化。
const handleTouchMove = (e: React.TouchEvent<HTMLDivElement>) => { if (!touchDataRef.current || mergedProps.cursor === 'static') return setIsCaretDragging(true) const touch = e.touches[0] const deltaX = touch.clientX - touchDataRef.current.startX const charWidth = charWidthRef.current const moveChars = Math.round(deltaX / charWidth) let newCaretPosition = touchDataRef.current.startCaretPosition + moveChars // 边界处理 newCaretPosition = Math.max(0, Math.min(newCaretPosition, value.length)) - setCaretPosition(newCaretPosition) + + // 添加节流,避免频繁更新 + if (newCaretPosition !== caretPosition) { + setCaretPosition(newCaretPosition) + } // 防止 touchend 不触发 if (touchMoveTimeoutRef.current) { clearTimeout(touchMoveTimeoutRef.current) } touchMoveTimeoutRef.current = setTimeout(() => { setIsCaretDragging(false) touchMoveTimeoutRef.current = null }, 500) }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
src/components/virtual-input/index.zh.md
(1 hunks)src/components/virtual-input/tests/virtual-input.test.tsx
(12 hunks)src/components/virtual-input/virtual-input.tsx
(8 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- src/components/virtual-input/index.zh.md
⏰ Context from checks skipped due to timeout of 90000ms (4)
- GitHub Check: size
- GitHub Check: build preview
- GitHub Check: compressed-size
- GitHub Check: check
🔇 Additional comments (21)
src/components/virtual-input/virtual-input.tsx (12)
28-28
: 光标属性定义正确。属性类型定义清晰,支持静态和可移动两种模式。
45-45
: 默认值设置合理。默认为 'static' 模式符合向后兼容的设计原则。
60-74
: 状态管理和引用定义良好。光标位置状态管理逻辑清晰,各个 ref 的用途明确,注释详细。过去的类型安全问题已得到解决。
93-98
: 字符宽度测量实现正确。使用 useEffect 在值变化时重新测量字符宽度,确保光标位置计算准确。
100-111
: 光标位置更新逻辑实现正确。根据键盘输入模式智能调整光标位置,处理了受控组件的边界情况。
189-199
: 字符点击处理逻辑完善。正确处理了禁用状态和静态光标模式的检查,点击位置判断逻辑合理。
202-220
: 触摸开始事件处理实现良好。距离阈值设置合理(20px),正确检查了禁用状态和静态模式。
247-250
: 触摸结束事件处理简洁有效。正确清理了触摸数据和拖拽状态。
260-260
: CSS 类名切换逻辑正确。动态添加拖拽状态的 CSS 类,用于控制光标闪烁动画。
274-276
: 触摸事件绑定位置合适。在内容区域绑定触摸事件,确保用户可以在整个输入区域进行操作。
279-285
: 字符渲染逻辑完善。正确设置了第一个字符的 ref 用于宽度测量,点击事件绑定恰当。
288-290
: 光标渲染逻辑正确。添加了 ref 引用,便于触摸事件中的位置计算。
src/components/virtual-input/tests/virtual-input.test.tsx (9)
68-84
: 触摸事件辅助函数实现正确。正确模拟了触摸事件的结构,支持不同类型的触摸事件和坐标位置。
87-106
: DOM 元素模拟策略合理。通过模拟 getBoundingClientRect 为 SPAN 元素提供固定宽度,与组件中的字符宽度测量逻辑配合良好。
224-224
: 测试用例正确更新。为需要光标移动功能的测试添加了
cursor='movable'
属性。
254-256
: 异步状态更新处理正确。使用
act()
包装点击操作,确保 React 状态更新完成后再进行断言。
292-292
: 测试配置一致性良好。在所有需要光标移动的测试中统一添加了
cursor='movable'
属性。
318-360
: 光标点击测试覆盖全面。测试涵盖了左右点击、键盘输入、删除操作等各种场景下的光标位置变化,使用
act()
正确处理异步更新。
421-461
: 受控组件测试更新恰当。在复杂的受控组件场景中正确使用
act()
处理点击事件,确保测试稳定性。
495-607
: 触摸拖拽测试实现全面。测试涵盖了触摸开始、移动、结束的完整流程,包括:
- 光标位置的精确计算和舍入逻辑
- 拖拽状态的 CSS 类切换
- 超时机制的验证
- 边界条件的处理
测试逻辑清晰,断言准确。
609-674
: 禁用状态测试完整。正确验证了在默认(静态)模式下,触摸移动和字符点击都不会改变光标位置,确保了向后兼容性。
} | ||
touchMoveTimeoutRef.current = setTimeout(() => { | ||
setIsCaretDragging(false) | ||
touchMoveTimeoutRef.current = null |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
这个拖拽手感非常奇怪,有时候拖得动有时候拖不动。另外如果是拖动,预期应该是选中一个范围才对。比如说:
12345,我从2左边拖到4右,这个时候再按下键盘6,预期应该是 165,而不是光标到处走。
const charWidthRef = useRef<number>(0) // 单个字符宽度 | ||
const caretRef = useRef<HTMLDivElement>(null) // 光标的 DOM | ||
const [isCaretDragging, setIsCaretDragging] = useState<boolean>(false) | ||
const touchMoveTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ref 有点太多了,React 里推荐用 state 进行状态控制,避免用 ref 来存数据。
Summary by CodeRabbit
cursor
,支持用户通过点击字符或触摸拖动调整光标位置,默认值为'static'
(不可调整)。cursor
属性说明。cursor
的行为。