Skip to content

Conversation

@edokeh
Copy link
Contributor

@edokeh edokeh commented Jun 26, 2025

Summary by CodeRabbit

  • 新功能
    • VirtualInput 组件新增可选属性 cursor,支持用户通过点击字符或触摸拖动调整光标位置,默认值为 { movable: false }(不可调整)。
  • 样式
    • 优化光标动画样式,新增拖动状态下禁用光标闪烁效果。
  • 文档
    • 更新 VirtualInput 组件文档,补充 cursor 属性说明。
  • 测试
    • 增加光标位置触摸拖动相关测试,覆盖开启和关闭 cursor 的行为。
    • 优化测试中光标位置变更的异步处理及元素尺寸模拟。

@github-actions
Copy link
Contributor

github-actions bot commented Jun 26, 2025

Preview is ready

@coderabbitai
Copy link

coderabbitai bot commented Jun 26, 2025

📝 Walkthrough
## Walkthrough

本次更改在 VirtualInput 组件中新增了 `cursor` 属性,支持用户通过点击字符或触摸拖动来调整光标位置(默认静态不可动)。更新了组件文档、样式和示例,并完善了相关的测试用例以覆盖光标位置调整的行为。

## Changes

| 文件/路径                                     | 变更摘要                                                         |
|-----------------------------------------------|------------------------------------------------------------------|
| src/components/virtual-input/index.zh.md      | 文档新增 `cursor` 属性说明,支持 `movable``onMove` 回调,默认 `{ movable: false }`|
| 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: true }}` 属性,新增默认不可调光标示例,调整部分字体大小。 |
| src/components/virtual-input/tests/virtual-input.test.tsx | 新增触摸事件辅助函数,完善光标位置调整相关测试,统一模拟字符宽度,改进异步事件处理。 |

## Sequence Diagram(s)

```mermaid
sequenceDiagram
    participant User
    participant VirtualInput
    participant DOM

    User->>VirtualInput: 点击字符或触摸拖动(cursor.movable=true)
    VirtualInput->>VirtualInput: 计算并更新 caretPosition
    VirtualInput->>DOM: 渲染新光标位置
    User->>VirtualInput: 键盘输入或删除
    VirtualInput->>VirtualInput: 根据输入类型调整 caretPosition
    VirtualInput->>DOM: 更新显示
    VirtualInput->>User: 触发 onMove 回调(如有)

Estimated code review effort

3 (~45 minutes)

Possibly related PRs

Suggested reviewers

  • zombieJ

Poem

🐇
光标跳跃随心动,
拖拽点击皆可控。
cursor 新增两模式,
输入体验更灵动。
代码如兔欢快跑,
开发乐趣日日浓!


</details>

<!-- walkthrough_end -->
<!-- internal state start -->


<!-- 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 -->

<!-- internal state end -->
<!-- finishing_touch_checkbox_start -->

<details>
<summary>✨ Finishing Touches</summary>

- [ ] <!-- {"checkboxId": "7962f53c-55bc-4827-bfbf-6a18da830691"} --> 📝 Generate Docstrings
<details>
<summary>🧪 Generate unit tests</summary>

- [ ] <!-- {"checkboxId": "f47ac10b-58cc-4372-a567-0e02b2c3d479", "radioGroupId": "utg-output-choice-group-unknown_comment_id"} -->   Create PR with unit tests
- [ ] <!-- {"checkboxId": "07f1e7d6-8a8e-4e23-9900-8731c2c87f58", "radioGroupId": "utg-output-choice-group-unknown_comment_id"} -->   Post copyable unit tests in a comment

</details>

</details>

<!-- finishing_touch_checkbox_end -->
<!-- tips_start -->

---

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

<details>
<summary>❤️ Share</summary>

- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai)
- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai)
- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai)
- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)

</details>

<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=ant-design/ant-design-mobile&utm_content=6897):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Explain this complex logic.`
  - `Open a follow-up GitHub issue for this discussion.`
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples:
  - `@coderabbitai explain this code block.`
  -	`@coderabbitai modularize this function.`
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
  - `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.`
  - `@coderabbitai read src/utils.ts and explain its main purpose.`
  - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`
  - `@coderabbitai help me debug CodeRabbit configuration file.`

### Support

Need help? Create a ticket on our [support page](https://www.coderabbit.ai/contact-us/support) for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (Invoked using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai generate docstrings` to [generate docstrings](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai generate sequence diagram` to generate a sequence diagram of the changes in this PR.
- `@coderabbitai generate unit tests` to generate unit tests for this PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically.

### CodeRabbit Configuration File (`.coderabbit.yaml`)

- You can programmatically configure CodeRabbit by adding a `.coderabbit.yaml` file to the root of your repository.
- Please see the [configuration documentation](https://docs.coderabbit.ai/guides/configure-coderabbit) for more information.
- If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: `# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json`

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->

@coderabbitai coderabbitai bot requested a review from zombieJ June 26, 2025 03:40
@pkg-pr-new
Copy link

pkg-pr-new bot commented Jun 26, 2025

npm i https://pkg.pr.new/ant-design/ant-design-mobile/antd-mobile@6897

commit: 696b7c8

Copy link

@coderabbitai coderabbitai bot left a 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

📥 Commits

Reviewing files that changed from the base of the PR and between 5e063e8 and eda2c05.

📒 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

@coderabbitai coderabbitai bot requested a review from zombieJ June 26, 2025 12:42
@codecov
Copy link

codecov bot commented Jun 26, 2025

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 92.89%. Comparing base (5e063e8) to head (696b7c8).
Report is 7 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #6897      +/-   ##
==========================================
+ Coverage   92.80%   92.89%   +0.08%     
==========================================
  Files         336      336              
  Lines        7244     7318      +74     
  Branches     1784     1846      +62     
==========================================
+ Hits         6723     6798      +75     
+ Misses        513      484      -29     
- Partials        8       36      +28     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link

@coderabbitai coderabbitai bot left a 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: 建议补充更多测试场景

当前测试覆盖较好,但建议考虑增加以下测试场景:

  1. 多点触控的处理
  2. 快速连续触摸的处理
  3. 组件禁用状态下的触摸行为

另外,建议在后续迭代中考虑:

  • RTL(从右到左)文本的支持
  • 无障碍访问的影响(屏幕阅读器用户如何使用此功能)
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ff5d277 and 7920bf9.

📒 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 未启用时,触摸和点击都不会改变光标位置,确保了功能的正确禁用。

Copy link

@coderabbitai coderabbitai bot left a 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: 优化触摸事件处理的用户体验。

触摸事件处理逻辑基本正确,但建议进行以下优化:

  1. 考虑添加 preventDefault() 防止触摸时触发页面滚动
  2. 调整超时时间 500ms 可能过长,建议减少到 200-300ms
  3. 动态调整触摸阈值 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

📥 Commits

Reviewing files that changed from the base of the PR and between 7920bf9 and 100cb87.

📒 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 会自动将光标位置设置到末尾。

Copy link

@coderabbitai coderabbitai bot left a 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

📥 Commits

Reviewing files that changed from the base of the PR and between 100cb87 and 2da5dec.

📒 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: 禁用状态测试完整。

正确验证了在默认(静态)模式下,触摸移动和字符点击都不会改变光标位置,确保了向后兼容性。

@coderabbitai coderabbitai bot requested a review from zombieJ June 30, 2025 06:02
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (2)
src/components/virtual-input/virtual-input.tsx (2)

250-250: 触摸拖拽体验问题需要关注。

根据之前的评论,用户反映拖拽手感奇怪,有时候拖得动有时候拖不动。建议进行以下优化:

  1. 增加触摸反馈
  2. 优化拖拽阈值判断
  3. 增加视觉反馈提示用户可以拖拽

建议测试以下场景:

  • 在不同设备上的拖拽体验
  • 快速连续拖拽时的响应性
  • 边界情况下的光标位置准确性

需要验证拖拽功能在实际使用中的稳定性和用户体验。


67-75: 状态管理过于复杂,建议优化ref的使用。

根据之前的评论,这里确实存在ref过多的问题。建议考虑以下优化:

  1. charWidthRef 可以考虑使用状态管理
  2. touchMoveTimeoutRef 可以使用 useCallback 配合 clearTimeout 来管理
  3. 部分ref数据可以合并到一个对象中
-    const touchDataRef = useRef<{
-      startX: number
-      startCaretPosition: number
-    } | null>() // 记录上一次 touch 时的坐标位置
-    const charRef = useRef<HTMLElement>(null) // 第一个字符的 DOM
-    const charWidthRef = useRef<number>(0) // 单个字符宽度
-    const caretRef = useRef<HTMLDivElement>(null) // 光标的 DOM
-    const [isCaretDragging, setIsCaretDragging] = useState<boolean>(false)
-    const touchMoveTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>()
+    const charRef = useRef<HTMLElement>(null)
+    const caretRef = useRef<HTMLDivElement>(null)
+    const [charWidth, setCharWidth] = useState<number>(0)
+    const [isCaretDragging, setIsCaretDragging] = useState<boolean>(false)
+    const [touchData, setTouchData] = useState<{
+      startX: number
+      startCaretPosition: number
+    } | null>(null)
🧹 Nitpick comments (2)
src/components/virtual-input/virtual-input.tsx (2)

94-99: 字符宽度计算可能存在性能问题。

每次 value 变化都会重新计算字符宽度,但通常字符宽度是固定的。建议只在组件首次渲染或字体相关属性变化时计算。

-    useEffect(() => {
-      // 记录单个字符的宽度,用于光标移动时的计算
-      if (charRef.current) {
-        charWidthRef.current = charRef.current.getBoundingClientRect().width
-      }
-    }, [value])
+    useEffect(() => {
+      // 记录单个字符的宽度,用于光标移动时的计算
+      if (charRef.current && charWidthRef.current === 0) {
+        charWidthRef.current = charRef.current.getBoundingClientRect().width
+      }
+    }, [value])

208-226: 触摸开始事件的阈值判断需要优化。

20px的硬编码阈值可能在不同设备上表现不一致,建议考虑使用相对值或可配置的阈值。

-      if (distance < 20) {
-        // 20px 阈值可调整
+      const threshold = Math.max(20, charWidthRef.current) // 使用字符宽度或20px的较大值
+      if (distance < threshold) {
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2da5dec and 1082410.

📒 Files selected for processing (2)
  • src/components/virtual-input/index.zh.md (1 hunks)
  • src/components/virtual-input/virtual-input.tsx (7 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: compressed-size
  • GitHub Check: build preview
  • GitHub Check: size
  • GitHub Check: check
🔇 Additional comments (5)
src/components/virtual-input/virtual-input.tsx (5)

28-29: 新增属性定义合理,类型定义清晰。

cursoronCursorMove 属性的定义符合组件设计需求,类型定义也很清晰。


101-112: 光标位置更新逻辑清晰合理。

通过 keyboardDataRef 来区分输入和删除操作,相应调整光标位置的逻辑是正确的。边界处理也得当。


192-205: 字符点击事件处理逻辑正确。

通过计算点击位置相对于字符中心的偏移来决定光标位置,逻辑合理。e.stopPropagation() 的使用也是必要的。


281-283: 触摸事件绑定位置合适。

在 content 容器上绑定触摸事件可以确保整个输入区域都能响应触摸操作。


295-297: 光标渲染优化良好。

只在获得焦点时渲染光标,避免了不必要的DOM操作,同时通过ref引用便于后续的位置计算。

@dosubot dosubot bot added the size:L This PR changes 100-499 lines, ignoring generated files. label Jul 22, 2025
@coderabbitai coderabbitai bot requested a review from zombieJ July 22, 2025 03:14
Copy link

@coderabbitai coderabbitai bot left a 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

♻️ Duplicate comments (3)
src/components/virtual-input/virtual-input.tsx (3)

71-79: 建议将部分 ref 改为 state

过多使用 ref 存储数据不符合 React 最佳实践。建议将 touchDataRef 改为 state,因为它的改变会影响组件行为。

-    const touchDataRef = useRef<{
-      startX: number
-      startCaretPosition: number
-    } | null>() // 记录上一次 touch 时的坐标位置
+    const [touchData, setTouchData] = useState<{
+      startX: number
+      startCaretPosition: number
+    } | null>(null) // 记录上一次 touch 时的坐标位置

232-256: 触摸事件处理存在多个问题

  1. 缺少 preventDefault() 导致可能触发页面滚动
  2. 500ms 超时机制可能导致状态不一致
  3. 拖动行为不符合预期(应该选中范围而非移动光标)

建议修改实现:

     const handleTouchMove = (e: React.TouchEvent<HTMLDivElement>) => {
       if (!touchDataRef.current || !mergedProps.cursor?.movable) return
 
+      e.preventDefault() // 防止页面滚动
       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)
       mergedProps.cursor?.onMove?.(newCaretPosition)
-
-      // 防止 touchend 不触发
-      if (touchMoveTimeoutRef.current) {
-        clearTimeout(touchMoveTimeoutRef.current)
-      }
-      touchMoveTimeoutRef.current = setTimeout(() => {
-        setIsCaretDragging(false)
-        touchMoveTimeoutRef.current = null
-      }, 500)
     }

同时需要在组件卸载时清理定时器:

+    useEffect(() => {
+      return () => {
+        if (touchMoveTimeoutRef.current) {
+          clearTimeout(touchMoveTimeoutRef.current)
+        }
+      }
+    }, [])

258-261: touchEnd 处理需要清理定时器

当前实现未清理可能存在的定时器。

     const handleTouchEnd = () => {
       touchDataRef.current = null
       setIsCaretDragging(false)
+      if (touchMoveTimeoutRef.current) {
+        clearTimeout(touchMoveTimeoutRef.current)
+        touchMoveTimeoutRef.current = null
+      }
     }
🧹 Nitpick comments (1)
src/components/virtual-input/tests/virtual-input.test.tsx (1)

612-677: 建议补充更多测试场景

当前测试覆盖较好,但建议增加以下场景:

  1. 组件卸载时的清理逻辑
  2. 快速连续触摸的场景
  3. 空值时的光标行为

需要我帮您生成这些额外的测试用例吗?

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1082410 and 52c4a11.

📒 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 (7 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/components/virtual-input/demos/demo1.tsx
  • src/components/virtual-input/index.zh.md
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: build preview
  • GitHub Check: compressed-size
  • GitHub Check: check
  • GitHub Check: size
🔇 Additional comments (2)
src/components/virtual-input/virtual-input.tsx (1)

21-51: 类型定义和默认值设置合理

Cursor 类型定义清晰,默认禁用光标移动的设计符合 PR 目标。

src/components/virtual-input/tests/virtual-input.test.tsx (1)

498-610: 触摸移动测试覆盖全面

测试用例很好地覆盖了触摸移动的各种场景,包括:

  • 双向拖动
  • CSS 类切换
  • 超时状态重置
  • 触摸区域判断

@dosubot dosubot bot added size:XL This PR changes 500-999 lines, ignoring generated files. and removed size:L This PR changes 100-499 lines, ignoring generated files. labels Jul 23, 2025
Copy link

@coderabbitai coderabbitai bot left a 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

♻️ Duplicate comments (3)
src/components/virtual-input/virtual-input.tsx (3)

71-79: ref 使用过多,建议优化状态管理。

当前实现使用了大量 ref 来存储状态数据,这与 React 推荐的状态管理方式不符。建议将部分 ref 转换为 state。

-const touchDataRef = useRef<{
-  startX: number
-  startCaretPosition: number
-} | null>()
+const [touchData, setTouchData] = useState<{
+  startX: number
+  startCaretPosition: number
+} | null>(null)

-const [isCaretDragging, setIsCaretDragging] = useState<boolean>(false)
-const touchMoveTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>()
+const [isCaretDragging, setIsCaretDragging] = useState<boolean>(false)
+const [touchMoveTimeout, setTouchMoveTimeout] = useState<ReturnType<typeof setTimeout> | null>(null)

232-256: 触摸移动事件处理存在已知问题。

正如之前的评论所指出的,当前的超时机制可能导致状态不一致。建议直接在 touchend 事件中处理状态重置。

另外,缺少对组件卸载时定时器的清理,可能导致内存泄漏:

+useEffect(() => {
+  return () => {
+    if (touchMoveTimeoutRef.current) {
+      clearTimeout(touchMoveTimeoutRef.current)
+    }
+  }
+}, [])

254-254: 触摸交互体验问题需要解决。

如评论中提到的,当前拖拽手感不稳定,有时能拖动有时不能。更重要的是,拖动操作的预期行为应该是选中文本范围而不是移动光标位置。建议重新考虑交互设计:

  1. 拖动应该创建文本选区,而不是移动光标
  2. 单击才应该移动光标位置
  3. 考虑添加haptic feedback提升触摸体验
🧹 Nitpick comments (2)
src/components/virtual-input/virtual-input.tsx (2)

105-116: 光标位置更新逻辑存在潜在问题。

当前逻辑依赖 keyboardDataRef.current.newValuevalue 的比较来判断是否为外部更新,但这种方式在某些边缘情况下可能不够准确,比如用户输入相同的值。

建议添加一个更明确的标识来区分输入来源:

const keyboardDataRef = useRef<{
  newValue?: string
  mode?: 'input' | 'delete'
+  timestamp?: number
}>({})

212-230: 触摸开始事件处理需要改进。

当前实现使用固定的 20px 阈值来判断是否在光标附近,这个值可能需要根据不同设备的屏幕密度进行调整。

-if (distance < 20) {
+const threshold = window.devicePixelRatio * 15 // 根据设备像素比调整
+if (distance < threshold) {
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 52c4a11 and 696b7c8.

📒 Files selected for processing (1)
  • src/components/virtual-input/virtual-input.tsx (7 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: compressed-size
  • GitHub Check: build
  • GitHub Check: size
  • GitHub Check: build preview
  • GitHub Check: check
🔇 Additional comments (8)
src/components/virtual-input/virtual-input.tsx (8)

21-24: 类型定义清晰且合理。

新增的 Cursor 类型定义结构合理,movableonMove 都是可选属性,符合设计需求。


33-33: Props 接口和默认值设计合理。

cursor 属性的默认值 { movable: false } 符合 PR 目标中提到的"默认禁用光标位置可调整"的需求。

Also applies to: 50-50


98-103: 字符宽度测量逻辑合理。

在 value 变化时重新测量字符宽度是必要的,实现方式正确。


196-209: 字符点击事件处理逻辑正确。

通过计算点击位置相对于字符中心的位置来确定光标位置的逻辑合理,并正确调用了 onMove 回调。


258-266: 触摸结束事件处理已改进。

相比之前的版本,现在正确清理了所有相关的定时器和状态,确保组件状态的一致性。


274-277: 拖拽状态的 CSS 类名应用合理。

通过 isCaretDragging 状态动态添加 CSS 类来禁用光标闪烁动画是一个好的设计。


290-292: 触摸事件绑定位置合适。

将触摸事件绑定到 content 容器上确保了事件能够正确捕获,同时不会影响其他交互。


304-307: 光标渲染条件合理。

只在输入框获得焦点时渲染光标,符合用户体验预期。通过 caretRef 获取光标元素用于触摸事件处理也是必要的。

@dosubot dosubot bot added the lgtm This PR has been approved by a maintainer label Jul 23, 2025
@zombieJ zombieJ merged commit 08fe5ba into ant-design:master Jul 23, 2025
14 checks passed
This was referenced Jul 23, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

lgtm This PR has been approved by a maintainer size:XL This PR changes 500-999 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants