Skip to content

feat(focus): preventDefault for a consumed key, behind a config flag - #64

Open
chiefcll wants to merge 1 commit into
mainfrom
focus-prevent-default
Open

chiefcll wants to merge 1 commit into
mainfrom
focus-prevent-default

Conversation

@chiefcll

Copy link
Copy Markdown
Contributor

Why

On a host with no browser between the remote and the app, the host decides what an unhandled press does: on Apple TV a Menu press nothing handled returns to the Home screen, which review expects at the root screen and nowhere else. The host learns which presses the app consumed from preventDefault(), and until now only a handler calling it itself could say so, in every Back handler of every app. Phase 4 of the NativeScript integration runs the public demo app unchanged on the Apple TV, so the answer had to come from the framework.

What changed

  • Config.preventDefaultOnHandledKeys, off by default. With it on, the focus manager calls preventDefault() on every key event the app consumed: a handler returned true, or the focus manager dropped the press itself, a press the global throttle dropped or a repeat useHold suppressed. Key-ups too. An event without a preventDefault method is left alone; KeyEventLike declares it optional.
  • A second useFocusManager argument that cannot listen is ignored. 1.6.3 reads that argument as the event target (feat(focus): let useFocusManager listen on a given event target #63); before it, the argument was ignored. The demo app still passes the removed hold options there and threw on target.addEventListener against 1.6.3. It falls back to document again.
  • Internally, handleKeyEvents returns whether the event was consumed, and a press the global throttle drops counts as consumed, as one an element's throttleInput drops already did.
  • Docs: the Event Target section and a new Consumed Keys section in useFocusManager.md; the flag in the config list of render.md.

Off by default because in a browser it changes what a handled key does: a handled arrow no longer scrolls the page, say.

Tests

  • tests/focusManagerPreventDefault.test.tsx: called for a consumed press, not for an unhandled one, not with the flag off, for a consumed release, and for a press the global throttle dropped.
  • tests/focusManagerTarget.test.tsx: listens on document when the second argument cannot listen.
  • pnpm test: 332 passing. pnpm lint: no errors. pnpm build clean.

The flag is what the NativeScript host sets in its boot file; the demo app on the Apple TV leaves to the Home screen from its start page and nowhere else through it.

🤖 Generated with Claude Code

A host with no browser between the remote and the app decides for itself
what an unhandled press does: on tvOS a Menu press nothing handled returns
to the Home screen. It needs to know which presses the app consumed, and
until now only a handler calling `preventDefault()` itself could say so.
`Config.preventDefaultOnHandledKeys`, off by default, makes the focus
manager call it on every key event the app consumed: a handler returned
true, or the focus manager dropped the press itself, a throttled press or
a suppressed repeat. `KeyEventLike` gains an optional `preventDefault`.

Also a compatibility fix: 1.6.3 reads the second `useFocusManager`
argument as the event target, where earlier releases ignored it. An app
still passing the removed hold options there threw on
`target.addEventListener`. An argument that cannot listen is ignored
again, in favour of `document`.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
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