mirror of
https://github.com/anomalyco/opentui.git
synced 2026-04-25 04:55:58 +03:00
[PR #646] [CLOSED] Renderable-scoped keyboard focus #684
Labels
No labels
bug
core
documentation
feature
good first issue
help wanted
pull-request
question
react
solid
tmux
windows
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
starred/opentui#684
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
📋 Pull Request Information
Original PR: https://github.com/anomalyco/opentui/pull/646
Author: @cevr
Created: 2/7/2026
Status: ❌ Closed
Base:
main← Head:feat/focus-scope📝 Commits (6)
9a1ff63feat(core): add renderable-scoped keyboard focus behavior01f374ffeat(frameworks): support scope-aware keyboard hooks57f2f63refactor(frameworks): simplify scope keyboard hooks and isolate react testsa69082ffix(core): dispatch order for global preventDefault vs scope isolationa2c4205fix(core): isolate paste events in trap-focus scopes and simplify onParentAddedef80af6refactor(core): remove default tab cycling from trapFocus, rename focus utilities📊 Changes
12 files changed (+1758 additions, -118 deletions)
View changed files
📝
packages/core/src/Renderable.ts(+186 -38)📝
packages/core/src/lib/KeyHandler.ts(+134 -34)➕
packages/core/src/lib/focus-traversal.test.ts(+157 -0)➕
packages/core/src/lib/focus-traversal.ts(+78 -0)📝
packages/core/src/lib/index.ts(+1 -0)📝
packages/core/src/renderer.ts(+7 -9)➕
packages/core/src/tests/renderable.scope-focus.test.ts(+650 -0)📝
packages/core/src/types.ts(+2 -3)📝
packages/react/src/hooks/use-keyboard.ts(+13 -6)➕
packages/react/tests/renderable.scope-focus.test.tsx(+66 -0)📝
packages/solid/src/elements/hooks.ts(+17 -28)➕
packages/solid/tests/renderable.scope-focus.test.tsx(+447 -0)📄 Description
Closes #638
Problem / Intent
Keyboard events need to be isolatable to a renderable subtree — modals, dialogs, and panels that trap focus within their boundaries, with automatic focus save/restore on scope teardown.
Approach
Scope behavior lives directly on
Renderablevia two new options:trapFocus— isolates keyboard events to the subtree. Key, release, and paste events bubble up from the focused renderable and stop at the scope boundary viastopPropagation. Ctrl+C is the one exception — it always passes through.autoFocus— focuses the first focusable descendant (or self) on mount.No default keybindings are imposed. Focus cycling utilities (
nextFocusable,prevFocusable) are exported for consumers to wire to whatever keys make sense for their app.Event dispatch in
InternalKeyHandleruses a dual-path strategy: trap-focus scopes dispatch to the renderable first (so the scope can stop propagation before globals see it), while regular focused renderables dispatch to globals first (sopreventDefaultworks for things like Input shortcuts).Framework hooks (
useKeyboardin both React and Solid) accept an optionalrefto discover the nearest keyboard scope via parent-walking, falling back to global when none exists.Focus save/restore is handled per-scope: activating a scope snapshots the current focus, deactivating restores it if the target is still alive and focusable.
🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.