Skip to content

feat(useListenFocusTriggers): remove from public API#3304

Merged
talkor merged 3 commits intovibe4from
breaking-change/remove-use-listen-focus-triggers
Mar 4, 2026
Merged

feat(useListenFocusTriggers): remove from public API#3304
talkor merged 3 commits intovibe4from
breaking-change/remove-use-listen-focus-triggers

Conversation

@talkor
Copy link
Copy Markdown
Member

@talkor talkor commented Mar 2, 2026

User description

Summary

  • Remove useListenFocusTriggers from @vibe/core public exports
  • Keep the hook as an internal utility (still used by AvatarGroup and useActiveDescendantListFocus)
  • Remove docs pages (Storybook stories and MDX) for the hook
  • Add migration guide entry with before/after code examples

Breaking Changes

useListenFocusTriggers is no longer exported from @vibe/core. Users who relied on this hook should inline the logic using useEventListener:

// Before (v3)
import { useListenFocusTriggers } from "@vibe/core";
useListenFocusTriggers({ ref, onFocusByKeyboard, onFocusByMouse });

// After (v4) — inline with useEventListener
import { useEventListener } from "@vibe/core";
import { useRef, useCallback } from "react";
const isMouseDown = useRef(false);
useEventListener({ eventName: "mousedown", ref, callback: useCallback(() => { isMouseDown.current = true; }, []) });
useEventListener({ eventName: "mouseup", ref, callback: useCallback(() => { isMouseDown.current = false; }, []) });
useEventListener({
  eventName: "focus",
  ref,
  callback: useCallback(() => {
    if (isMouseDown.current) { handleMouseFocus(); } else { handleKeyboardFocus(); }
  }, [handleKeyboardFocus, handleMouseFocus])
});

Task Link

Monday.com Task

Test Plan

  • All 142 core package tests pass
  • Exports snapshot updated
  • Migration guide updated
  • No TypeScript errors introduced

🤖 Generated with Claude Code


PR Type

Breaking change, Documentation


Description

  • Remove useListenFocusTriggers from public API exports

  • Keep hook as internal utility for AvatarGroup and useActiveDescendantListFocus

  • Delete Storybook stories and MDX documentation pages

  • Add comprehensive migration guide with before/after code examples


Diagram Walkthrough

flowchart LR
  A["useListenFocusTriggers<br/>Public Export"] -->|Remove from| B["@vibe/core<br/>Public API"]
  A -->|Keep as| C["Internal Utility<br/>for AvatarGroup"]
  D["Storybook Stories<br/>& MDX Docs"] -->|Delete| E["Documentation"]
  F["Migration Guide"] -->|Add| G["VIBE4_MIGRATION_GUIDE.md<br/>with inline examples"]
Loading

File Walkthrough

Relevant files
Breaking change
index.ts
Remove useListenFocusTriggers from public exports               

packages/core/src/hooks/index.ts

  • Remove useListenFocusTriggers export from public hooks API
  • Hook remains available internally for internal components
+0/-1     
Documentation
VIBE4_MIGRATION_GUIDE.md
Add useListenFocusTriggers migration guide and formatting fixes

VIBE4_MIGRATION_GUIDE.md

  • Add new "useListenFocusTriggers — Removed" section under Hooks
  • Provide detailed before/after migration code examples using
    useEventListener
  • Include explanation of focus detection logic using isMouseDown ref
    pattern
  • Mark codemod as not available (manual migration required)
  • Fix markdown formatting: add blank lines after section headers and
    improve table alignment
+150/-61
useListenFocusTriggers.mdx
Remove useListenFocusTriggers MDX documentation                   

packages/docs/src/pages/hooks/useListenFocusTriggers/useListenFocusTriggers.mdx

  • Delete entire MDX documentation file for useListenFocusTriggers
  • Remove component metadata, import instructions, and usage guidelines
+0/-51   
useListenFocusTriggers.stories.tsx
Remove useListenFocusTriggers Storybook stories                   

packages/docs/src/pages/hooks/useListenFocusTriggers/useListenFocusTriggers.stories.tsx

  • Delete entire Storybook stories file for useListenFocusTriggers
  • Remove Overview story demonstrating keyboard vs mouse focus detection
+0/-36   

- Remove useListenFocusTriggers from @vibe/core public exports (hooks/index.ts)
- Keep the hook as an internal utility in hooks/useListenFocusTriggers/ for internal use
- Remove docs pages (stories and mdx) for the hook
- Update exports snapshot
- Add migration guide entry with before/after examples

BREAKING CHANGE: useListenFocusTriggers is no longer exported from @vibe/core.
Inline the focus detection using useEventListener with mousedown/mouseup/focus events.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@talkor talkor requested a review from a team as a code owner March 2, 2026 15:25
@qodo-free-for-open-source-projects
Copy link
Copy Markdown
Contributor

qodo-free-for-open-source-projects bot commented Mar 3, 2026

Code Review by Qodo

🐞 Bugs (1) 📘 Rule violations (1) 📎 Requirement gaps (0)

Grey Divider


Action required

1. Docs use Dropdown.sizes.MEDIUM 📘 Rule violation ✓ Correctness
Description
The migration guide example uses the static property Dropdown.sizes.MEDIUM as a prop value, which
the checklist disallows in favor of string literals. This can encourage consumers to depend on
brittle static property APIs.
Code

VIBE4_MIGRATION_GUIDE.md[774]

+<Dropdown placeholder="Select..." options={options} size={Dropdown.sizes.MEDIUM} searchable />;
Evidence
PR Compliance ID 8 forbids using static properties on components for prop values. The updated
migration guide line uses size={Dropdown.sizes.MEDIUM}, which is exactly the prohibited pattern.

CLAUDE.md
VIBE4_MIGRATION_GUIDE.md[774-774]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The migration guide includes an example using a static property (`Dropdown.sizes.MEDIUM`) for a prop value, which is disallowed by the compliance checklist.

## Issue Context
This is documentation, but it still sets the recommended consumer usage pattern. Prefer a string literal (or the documented non-static type/value) for `size`.

## Fix Focus Areas
- VIBE4_MIGRATION_GUIDE.md[774-774]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

2. Stuck isMouseDown flag 🐞 Bug ✓ Correctness
Description
The migration guide’s inline replacement can leave isMouseDown.current stuck true when mouseup
happens outside the element, causing later keyboard-focus to be misclassified as mouse-focus. This
can break keyboard-only behaviors (e.g., not showing keyboard tooltips) and also affects current
internal callers because the internal hook uses the same pattern.
Code

VIBE4_MIGRATION_GUIDE.md[R318-343]

+const isMouseDown = useRef(false);
+useEventListener({
+  eventName: "mousedown",
+  ref,
+  callback: useCallback(() => {
+    isMouseDown.current = true;
+  }, [])
+});
+useEventListener({
+  eventName: "mouseup",
+  ref,
+  callback: useCallback(() => {
+    isMouseDown.current = false;
+  }, [])
+});
+useEventListener({
+  eventName: "focus",
+  ref,
+  callback: useCallback(() => {
+    if (isMouseDown.current) {
+      handleMouseFocus();
+    } else {
+      handleKeyboardFocus();
+    }
+  }, [handleKeyboardFocus, handleMouseFocus])
+});
Evidence
The newly added migration snippet sets/clears isMouseDown only via mousedown/mouseup listeners
on the same ref, so mouseup will not run if the pointer is released outside the element; the
subsequent focus handler will then continue to treat focus as mouse-triggered. The internal
useListenFocusTriggers utility (still used by AvatarGroup and useActiveDescendantListFocus)
implements the same approach, so the same misclassification can occur in those features today.

VIBE4_MIGRATION_GUIDE.md[318-343]
packages/core/src/hooks/useListenFocusTriggers/index.ts[13-33]
packages/core/src/hooks/useListenFocusTriggers/index.ts[34-50]
packages/core/src/components/AvatarGroup/AvatarGroupCounterTooltipHelper.tsx[43-47]
packages/core/src/hooks/useActiveDescendantListFocus/useActiveDescendantListFocusHooks.ts[205-235]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The migration guide’s suggested inline replacement (and the internal `useListenFocusTriggers`) can misclassify keyboard focus as mouse focus if `mouseup` is not received on the target element (e.g., mousedown on element → drag outside → mouseup).

## Issue Context
- New docs encourage users to copy this logic.
- The internal hook still powers `AvatarGroup` and `useActiveDescendantListFocus`, so the edge case can impact current behavior too.

## Fix Focus Areas
- VIBE4_MIGRATION_GUIDE.md[318-343]
- packages/core/src/hooks/useListenFocusTriggers/index.ts[13-50]

## Suggested direction
- Reset the flag in the `focus` handler after deciding which callback to invoke (so it is only &quot;armed&quot; for the next focus).
- Optionally also clear on `pointerup`/`mouseup` at the `document` level (or add `mouseleave`/`pointercancel`) if you want full robustness.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

ⓘ The new review experience is currently in Beta. Learn more

Grey Divider

Qodo Logo

size={Dropdown.sizes.MEDIUM}
searchable
/>
<Dropdown placeholder="Select..." options={options} size={Dropdown.sizes.MEDIUM} searchable />;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Action required

1. Docs use dropdown.sizes.medium 📘 Rule violation ✓ Correctness

The migration guide example uses the static property Dropdown.sizes.MEDIUM as a prop value, which
the checklist disallows in favor of string literals. This can encourage consumers to depend on
brittle static property APIs.
Agent Prompt
## Issue description
The migration guide includes an example using a static property (`Dropdown.sizes.MEDIUM`) for a prop value, which is disallowed by the compliance checklist.

## Issue Context
This is documentation, but it still sets the recommended consumer usage pattern. Prefer a string literal (or the documented non-static type/value) for `size`.

## Fix Focus Areas
- VIBE4_MIGRATION_GUIDE.md[774-774]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@talkor talkor merged commit e870562 into vibe4 Mar 4, 2026
12 checks passed
@talkor talkor deleted the breaking-change/remove-use-listen-focus-triggers branch March 4, 2026 12:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants