Skip to content

fix: popover opened after click twice#115

Merged
gene9831 merged 2 commits intoopentiny:release/v0.2.xfrom
gene9831:fix/ios-safari-hover
Jun 16, 2025
Merged

fix: popover opened after click twice#115
gene9831 merged 2 commits intoopentiny:release/v0.2.xfrom
gene9831:fix/ios-safari-hover

Conversation

@gene9831
Copy link
Copy Markdown
Collaborator

@gene9831 gene9831 commented Jun 16, 2025

TODO 后面移动端要移除当hover才显示的可交互元素
临时方案:

  • Changed event listeners from @click to @pointerup in DropdownMenu and PillButtonWrapper components for improved responsiveness.
  • Added max-height style to suggestion popover for better layout control.

在 iOS 移动端,:hover 的确会干扰首次点击事件,导致用户需要点击两次才能触发 click —— 第一次触发 :hover,第二次才触发事件。这是因为:

iOS Safari 模拟了 :hover 行为,即使是在触屏设备上。

事件类型 支持的输入方式 特点
click 鼠标、部分触摸 有延迟(移动端通常有 300ms 延迟)
touchend 触摸 仅适用于触摸设备
pointerup 鼠标、触摸、笔输入 ✅ 推荐,统一事件模型,无需区分设备类型

Summary by CodeRabbit

  • New Features

    • Popover containers now have a maximum height limited to the viewport for improved display on various devices.
  • Style

    • Updated popover styling to enhance usability with dynamic viewport units.
  • Refactor

    • Changed dropdown menu, suggestion pill, and popover triggers to respond to pointer release actions instead of clicks for more consistent user interaction across devices.

- Changed event listeners from `@click` to `@pointerup` in DropdownMenu and PillButtonWrapper components for improved responsiveness.
- Added `max-height` style to suggestion popover for better layout control.
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 16, 2025

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

The event handlers for user interactions in dropdown menus, suggestion pills, and popovers were updated from click to pointerup events, altering how these components respond to user input. Additionally, a maximum height constraint using 100dvh was added to the popover container for improved viewport handling.

Changes

File(s) Change Summary
dropdown-menu/index.vue, suggestion-pills/components/PillButtonWrapper.vue, suggestion-popover/index.vue Changed event handlers from click to pointerup for trigger interactions.
suggestion-popover/index.vue Added CSS rule to set max-height of popover container to 100dvh.

Poem

A gentle tap, a pointer’s lift,
Now menus bloom and popovers shift.
No more clicks, just up we go—
The interface responds in flow.
With heights contained, the view is neat,
A rabbit’s work is quite a feat!
🐇✨

✨ Finishing Touches
🧪 Generate Unit Tests
  • Create PR with Unit Tests
  • Commit Unit Tests in branch fix/ios-safari-hover
  • Post Copyable Unit Tests in Comment

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.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • 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 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 for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai auto-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 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 for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@gene9831
Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 16, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown

@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: 2

🧹 Nitpick comments (2)
packages/components/src/suggestion-popover/index.vue (1)

283-284: Redundant height + max-height – consider making height flexible

height: v-bind('toCssUnit(props.popoverHeight)') forces a fixed box even when the content is shorter, defeating the purpose of the new max-height: 100dvh;.
If the goal is “cap, but don’t stretch”, drop the fixed height or move it to min-height instead.

packages/components/src/suggestion-pills/components/PillButtonWrapper.vue (1)

23-37: Pointer event choice is fine; tiny typing nit

PointerEvent extends MouseEvent, so passing it to the emit('click', ev, item) signature is valid.
No action required, but consider renaming the emitted event ('pointerup' vs 'click') for semantic clarity.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between fd65365 and 4d44cf4.

📒 Files selected for processing (3)
  • packages/components/src/dropdown-menu/index.vue (1 hunks)
  • packages/components/src/suggestion-pills/components/PillButtonWrapper.vue (2 hunks)
  • packages/components/src/suggestion-popover/index.vue (2 hunks)
🧰 Additional context used
🧠 Learnings (1)
packages/components/src/suggestion-popover/index.vue (1)
Learnt from: gene9831
PR: opentiny/tiny-robot#59
File: packages/components/src/suggestion-popover/index.vue:131-133
Timestamp: 2025-05-27T03:35:11.008Z
Learning: In the SuggestionPopover component (packages/components/src/suggestion-popover/index.vue), the click handler can be bound unconditionally because the `show` computed property has a custom setter that prevents state mutations when `props.trigger === 'manual'`. This design centralizes trigger mode logic in the computed property rather than requiring conditional checks in event handlers.

Comment on lines +215 to 216
@pointerup="handleToggleShow"
>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue

pointerup opening event conflicts with click-based outside-close logic & drops keyboard accessibility

  1. The popover now opens on pointerup, but onClickOutside (L150) still listens for the default click event.
    – On many touch devices a synthetic click fires ~300 ms after pointerup; the outside-click handler will therefore close the popover right after it opens → visible flicker / impossible to interact.
  2. Replacing click also removes the implicit keyboard fallback (Enter/Space generate a click but not a pointer event).
 // keep the trigger semantics but listen to the same event type + add KB fallback
-if (props.trigger === 'click') {
-  onClickOutside(popoverRef, handleOutside, /* default => click */)
+if (props.trigger === 'click') {
+  onClickOutside(
+    popoverRef,
+    handleOutside,
+    { event: 'pointerup' }  // stay in the same event stream
+  )
 }
 ...
 <div
   ref="popoverTriggerRef"
-  @pointerup="handleToggleShow"
+  @pointerup="handleToggleShow"
+  @keyup.enter.space="handleToggleShow"
 >

Failing to align the two streams is a breaking UX bug on iOS/Android; please patch before merge.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@pointerup="handleToggleShow"
>
// keep the trigger semantics but listen to the same event type + add KB fallback
if (props.trigger === 'click') {
onClickOutside(
popoverRef,
handleOutside,
{ event: 'pointerup' } // stay in the same event stream
)
}
// …
<template>
<div
ref="popoverTriggerRef"
@pointerup="handleToggleShow"
@keyup.enter.space="handleToggleShow"
>
<!-- popover trigger content -->
</div>
</template>
🤖 Prompt for AI Agents
In packages/components/src/suggestion-popover/index.vue around lines 215-216,
the popover opens on the pointerup event but the outside-close logic listens for
click events, causing a conflict and flicker on touch devices. To fix this,
align the outside-close event listener to also use pointerup instead of click,
ensuring consistent behavior across devices and preserving keyboard
accessibility by handling keyboard events separately or ensuring they still
trigger the close logic appropriately.

Comment on lines +64 to 65
@pointerup="handleToggleShow"
>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue

Same pointer/click mismatch as above – dropdown closes immediately after opening

@pointerup opens the menu, but onClickOutside (L34) still reacts to click.
Touch devices will emit the delayed click → menu closes at once.

Apply the same fix as for the popover:

-onClickOutside(dropdownMenuRef, handleOutside)
+onClickOutside(dropdownMenuRef, handleOutside, { event: 'pointerup' })
 ...
-@pointerup="handleToggleShow"
+@pointerup="handleToggleShow"
+@keyup.enter.space="handleToggleShow"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@pointerup="handleToggleShow"
>
@pointerup="handleToggleShow"
@keyup.enter.space="handleToggleShow"
>
🤖 Prompt for AI Agents
In packages/components/src/dropdown-menu/index.vue around lines 64 to 65, the
dropdown menu uses @pointerup to open but listens for click events on outside
clicks, causing the menu to close immediately on touch devices due to delayed
click events. To fix this, change the outside click listener on line 34 to
listen for pointerup events instead of click, matching the open event and
preventing immediate closure on touch devices.

@gene9831 gene9831 changed the title refactor: update event handling in dropdown and suggestion components fix: popover opened after click twice Jun 16, 2025
…uggestion components

- Introduced a `manual` trigger option for DropdownMenu and SuggestionPopover to allow external control of visibility.
- Implemented `@click-outside` event to close the dropdown and popover when clicking outside.
- Updated documentation to include new properties and event details for both components.
@gene9831 gene9831 merged commit 46c5762 into opentiny:release/v0.2.x Jun 16, 2025
@gene9831 gene9831 deleted the fix/ios-safari-hover branch June 17, 2025 12:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant