Skip to content

Conversation

gimmyhehe
Copy link
Member

@gimmyhehe gimmyhehe commented Aug 21, 2025

PR

PR Checklist

Please check if your PR fulfills the following requirements:

  • The commit message follows our Commit Message Guidelines
  • Tests for the changes have been added (for bug fixes / features)
  • Docs have been added / updated (for bug fixes / features)

PR Type

What kind of change does this PR introduce?

  • Bugfix
  • Feature
  • Code style update (formatting, local variables)
  • Refactoring (no functional changes, no api changes)
  • Build related changes
  • CI related changes
  • Documentation content changes
  • Other... Please describe:

What is the current behavior?

Issue Number: N/A

What is the new behavior?

Does this PR introduce a breaking change?

  • Yes
  • No

Other information

Summary by CodeRabbit

  • New Features
    • More robust event targeting in Grid/Table to better support Shadow DOM and web components.
  • Bug Fixes
    • Improved outside-click and blur detection to prevent unintended selections or focus loss.
    • More reliable scroll handling in Grid/Table, reducing misfires during horizontal and vertical scrolling.
  • Chores
    • Updated @opentiny/vue-grid to version 3.25.1.
    • Aligned dependencies with shared utilities and cleaned up duplicates.

Copy link

coderabbitai bot commented Aug 21, 2025

Walkthrough

Event handling updated to resolve the actual DOM target via a shared utility, replacing direct event.target usage across grid keyboard, table scroll/blur, and global mousedown handlers. The utility gained optional chaining safety. Grid package version bumped with a utils dependency adjustment.

Changes

Cohort / File(s) Summary
Event target resolution utility
packages/utils/src/event/index.ts
Safened getActualTarget: use optional chaining for e.target; preserves return logic while avoiding nullish access.
Grid keyboard handlers
packages/vue/src/grid/src/keyboard/src/methods.ts
Import and use getActualTarget for outside-click detection; replace event.target in relevant checks.
Grid table scroll and blur handlers
packages/vue/src/grid/src/table/src/methods.ts
Use getActualTarget for blur containment and scrollTop reads; update containment checks and scroll computations.
Global mousedown handling
packages/vue/src/grid/src/table/src/utils/handleGlobalMousedownEvent.ts
Compute actualTarget via getActualTarget; replace event.target in header/content containment checks.
Package version/dependencies
packages/vue/src/grid/package.json
Bump @opentiny/vue-grid to 3.25.1; add/move dependency on @opentiny/utils (workspace:~).

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant Browser as Browser Event
  participant Utils as getActualTarget()
  participant Grid as Grid Components
  participant Table as Table (Keyboard/Scroll)

  User->>Browser: Click/Scroll Event
  Browser->>Utils: Resolve actual target (event)
  Utils-->>Browser: actualTarget
  Browser->>Grid: Containment checks with actualTarget
  Browser->>Table: Blur/Scroll handling with actualTarget.scrollTop
  alt Outside element
    Table->>Table: Clear selections/copies
  else Inside element
    Table->>Table: Maintain state / compute scroll
  end

  note over Utils,Table: New: all checks use actualTarget instead of event.target
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

Suggested labels

bug

Suggested reviewers

  • zzcr

Poem

A bunny peeked at targets, soft and sly,
Through shadowed roots where clicks would hide and lie.
“Hop to the truth!” it thumped with cheer—
Actual targets now crystal-clear.
Scrolls glide smooth, selections right—
Carrot-high fives for bugs in flight! 🥕✨

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch cgm/grid-actual-target-2

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.
    • 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.
  • 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 the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

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

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • 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.

@github-actions github-actions bot added the bug Something isn't working label Aug 21, 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: 3

🧹 Nitpick comments (3)
packages/utils/src/event/index.ts (1)

57-62: Harden getActualTarget for broader Shadow DOM cases and better typing

Current logic relies on e.composed and assumes composedPath() returns an element with the right shape. Two tweaks improve resilience:

  • Prefer composedPath?.() when available (independent of e.composed), and gracefully fall back.
  • Normalize Text nodes to their parentElement; add TS types for clarity.

Apply this diff:

-export const getActualTarget = (e) => {
-  if (!e || !e.target) {
-    return null
-  }
-  return e.target?.shadowRoot && e.composed ? e.composedPath()[0] || e.target : e.target
-}
+export const getActualTarget = (e: Event & { target?: EventTarget | null }): Node | null => {
+  if (!e) return null
+  const t = (e as any).target as EventTarget | null
+  const path = typeof (e as any).composedPath === 'function' ? (e as any).composedPath() : []
+  let first = (path && path.length ? path[0] : t) as Node | null
+  // Promote Text nodes to their parent element when possible
+  if (first && first.nodeType === Node.TEXT_NODE) {
+    first = (first as any).parentElement ?? first
+  }
+  return first
+}
packages/vue/src/grid/src/table/src/utils/handleGlobalMousedownEvent.ts (1)

82-89: Condition reads well; optional micro-optimization to cache header element

Logic is correct and Node.contains(null) is safe (returns false). If you want to shave a lookup and improve readability, cache the header element and boolean.

Apply this diff:

-  const actualTarget = getActualTarget(event)
+  const actualTarget = getActualTarget(event)
+  const headerEl = _vm.$refs.tableHeader?.$el
+  const inHeader = headerEl ? headerEl.contains(actualTarget) : false
@@
-    (_vm.$refs.tableHeader && _vm.$refs.tableHeader.$el.contains(actualTarget)) ||
+    inHeader ||
     (tableContent && !tableContent.contains(actualTarget))
packages/vue/src/grid/src/table/src/methods.ts (1)

1747-1747: Minor: avoid recomputing actualTarget and aid TS inference

This is minor, but caching the node improves clarity and prevents recomputation.

Apply this diff:

-    if (scrollLoad && $el.contains(getActualTarget(event))) {
+    const t = getActualTarget(event) as Node | null
+    if (scrollLoad && $el.contains(t)) {
📜 Review details

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

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 33dad14 and 47a6bdf.

📒 Files selected for processing (5)
  • packages/utils/src/event/index.ts (1 hunks)
  • packages/vue/src/grid/package.json (3 hunks)
  • packages/vue/src/grid/src/keyboard/src/methods.ts (2 hunks)
  • packages/vue/src/grid/src/table/src/methods.ts (5 hunks)
  • packages/vue/src/grid/src/table/src/utils/handleGlobalMousedownEvent.ts (2 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (3)
packages/vue/src/grid/src/table/src/utils/handleGlobalMousedownEvent.ts (1)
packages/utils/src/event/index.ts (1)
  • getActualTarget (57-62)
packages/vue/src/grid/src/keyboard/src/methods.ts (1)
packages/utils/src/event/index.ts (1)
  • getActualTarget (57-62)
packages/vue/src/grid/src/table/src/methods.ts (1)
packages/utils/src/event/index.ts (1)
  • getActualTarget (57-62)
🔇 Additional comments (6)
packages/vue/src/grid/src/table/src/utils/handleGlobalMousedownEvent.ts (1)

25-25: Good call: centralizing target resolution via getActualTarget

Importing getActualTarget keeps Shadow DOM behavior consistent across handlers. No issues.

packages/vue/src/grid/src/keyboard/src/methods.ts (2)

25-25: Importing getActualTarget is aligned with the Shadow DOM fix

This keeps keyboard interactions consistent with the new target resolution utility.


670-677: No lingering direct event.target checks found
Verified with ripgrep across packages/vue/src/grid/src/keyboard—no matches for event.target remain.

packages/vue/src/grid/package.json (1)

4-4: Confirmed: getActualTarget is properly re-exported

The getActualTarget function is exported from packages/utils/src/index.ts (line 155), ensuring that imports will resolve correctly outside the workspace.

No further action required.

packages/vue/src/grid/src/table/src/methods.ts (2)

26-26: Importing getActualTarget here is consistent with the rest of the PR

Keeps table methods aligned with the shared target-resolution behavior.


1216-1217: Blur-inside check updated — LGTM

args?.cell.contains(getActualTarget(event)) correctly preserves edit state when clicking within the active cell, even in Shadow DOM.

@@ -23,7 +23,7 @@
*
*/
import { getColumnList, assemColumn, repairFixed } from '@opentiny/vue-renderless/grid/utils'
import { toDecimal } from '@opentiny/utils'
import { toDecimal, getActualTarget } from '@opentiny/utils'
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Repository-wide consistency check for event.target → getActualTarget migration

To ensure there are no remaining shadow-root-sensitive call sites in the grid/table code using raw event.target, run:

Also applies to: 1216-1217, 1624-1625, 1648-1649, 1747-1747


🏁 Script executed:

#!/bin/bash
# 1) Direct event.target usage in grid table code
rg -nP -C2 --type=ts '\bevent\.target\b' packages/vue/src/grid/src/table

# 2) Places reading scrollTop from event target
rg -nP -C2 --type=ts '\b(event|e)\.target\.scrollTop\b' packages/vue/src/grid

# 3) Containment checks that still pass event.target directly
rg -nP -C2 --type=ts 'contains\(\s*event\.target\s*\)' packages/vue/src/grid

Length of output: 707


Migrate remaining event.target usage in triggerCellClickEvent.ts

A raw event.target reference was detected in the grid/table utils and needs to be updated to use getActualTarget so that shadow-root elements are handled correctly.

• packages/vue/src/grid/src/table/src/utils/triggerCellClickEvent.ts:26

- const target = event.target
+ const target = getActualTarget(event)

Please replace all direct event.target references in this file with getActualTarget(event), and ensure any downstream logic (e.g. containment checks or property reads) operates on the resolved target. Once updated, rerun the repository-wide grep from the original comment to confirm no raw event.target calls remain.

📝 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
import { toDecimal, getActualTarget } from '@opentiny/utils'
// Replace this:
- const target = event.target
// With:
const target = getActualTarget(event)
🤖 Prompt for AI Agents
In packages/vue/src/grid/src/table/src/utils/triggerCellClickEvent.ts around
line 26, the file still uses raw event.target which breaks with shadow DOM;
replace every direct event.target access with const target =
getActualTarget(event) (import getActualTarget if not already) and update all
downstream checks and property reads to use that resolved target (e.g.,
containment checks, attribute/property reads, parent traversal). After changes,
run the repository-wide grep from the original comment to confirm no remaining
raw event.target usages.

Comment on lines +1624 to 1625
let scrollTop = getActualTarget(event).scrollTop

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Guard against non-scrollable targets to avoid NaN pagination math

getActualTarget(event) can return nodes without scrollTop (e.g., a Text or non-scrollable element via composedPath[0]). If scrollTop is undefined, Math.ceil(scrollTop / rowHeight) becomes NaN, and pageCurrentChange(NaN) may fire.

Apply this defensive fallback to the table body wrapper:

-        let scrollTop = getActualTarget(event).scrollTop
+        const t = getActualTarget(event) as any
+        const bodyEl = this.$refs.tableBody?.$el as any
+        let scrollTop = (typeof t?.scrollTop === 'number' ? t.scrollTop : bodyEl?.scrollTop) ?? 0
📝 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
let scrollTop = getActualTarget(event).scrollTop
const t = getActualTarget(event) as any
const bodyEl = this.$refs.tableBody?.$el as any
let scrollTop = (typeof t?.scrollTop === 'number' ? t.scrollTop : bodyEl?.scrollTop) ?? 0
🤖 Prompt for AI Agents
In packages/vue/src/grid/src/table/src/methods.ts around lines 1624-1625,
getActualTarget(event).scrollTop can be undefined for non-scrollable nodes which
causes NaN in pagination math; to fix, first capture the actual target into a
variable, then compute scrollTop with a defensive fallback (e.g., if target is
null or its scrollTop is not a number, use 0) and use that numeric value for the
subsequent Math.ceil/rowHeight calculation; apply the same defensive check when
reading scrollTop from the table body wrapper.

Comment on lines +1648 to 1649
let { scrollTop } = getActualTarget(event)
let toVisibleIndex = Math.ceil(scrollTop / rowHeight)
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Same robustness issue in virtual Y scroll calculation

Destructuring from getActualTarget(event) will throw if it returns null, and even when not null, scrollTop may be undefined. Mirror the fallback used above.

Apply this diff:

-    let { scrollTop } = getActualTarget(event)
+    const t = getActualTarget(event) as any
+    const bodyEl = this.$refs.tableBody?.$el as any
+    const scrollTop = (typeof t?.scrollTop === 'number' ? t.scrollTop : bodyEl?.scrollTop) ?? 0
📝 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
let { scrollTop } = getActualTarget(event)
let toVisibleIndex = Math.ceil(scrollTop / rowHeight)
const t = getActualTarget(event) as any
const bodyEl = this.$refs.tableBody?.$el as any
const scrollTop = (typeof t?.scrollTop === 'number' ? t.scrollTop : bodyEl?.scrollTop) ?? 0
let toVisibleIndex = Math.ceil(scrollTop / rowHeight)
🤖 Prompt for AI Agents
In packages/vue/src/grid/src/table/src/methods.ts around lines 1648-1649, avoid
destructuring directly from getActualTarget(event) because it can return null
and scrollTop can be undefined; instead retrieve the target with a safe fallback
(e.g., const target = getActualTarget(event) || {}) and read scrollTop using a
nullish/defined fallback (e.g., const scrollTop = target.scrollTop ?? 0) before
computing toVisibleIndex = Math.ceil(scrollTop / rowHeight) so it mirrors the
robustness used earlier.

@zzcr zzcr closed this Aug 26, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants