RootFocusBoundary

The root of a virtual focus tree for a single anchor node

import { RootFocusBoundary } from "@roku-sdk/navigation";
RootFocusBoundary(props: IRootFocusBoundaryProps): JSX.Element

Description

The root of a virtual focus tree for a single anchor node.

RootFocusBoundary owns every anchor-node-wired concern:

  • keyEvent: the SceneGraph key input it dispatches through the tree
  • focusActive: whether this anchor node holds SceneGraph focus
  • setHandledKeys: the handledKeys output BrightScript reads to claim keys
  • setFocusPath: optional automation output for the focused leaf's SG path

Nested subtrees use FocusBoundary (with focusId) and receive key events through the dispatch chain.

An app may have more than one RootFocusBoundary — one per anchor node that extends FocusBoundaryBase (e.g. a scene plus a separately-rendered overlay). Each observes its own keyEvent, and only the one in the SceneGraph focus chain is active at a time via focusActive.

Parameters

NameTypeDescription
propsIRootFocusBoundaryPropsThe anchor node's inputs and outputs, and the tree's content.

Example

const MyScene: AnchorNodeImplementation<typeof Desc> = (inputs, outputs) => (
  <RootFocusBoundary
    keyEvent={inputs.keyEvent}
    focusActive={inputs.focusActive}
    setHandledKeys={outputs.handledKeys}
    setFocusPath={outputs.focusPath}
    initialFocus={itemId}
    navigation="vertical"
  >
    <MenuItem focusId={itemId} label="First" />
    <MenuItem label="Second" />
  </RootFocusBoundary>
);

Types

IRootFocusBoundaryProps

Props for the RootFocusBoundary component.

NameTypeDescription
captureKeyPress?booleanWhen true, a key-down consumed by a handler this boundary owns binds the matching key-up to that handler. See IFocusBoundaryProps.captureKeyPress.
children?ElementThe focus tree's content. Descendants that call useFocusable register with this boundary, unless a nearer FocusBoundary sits between them.
focusActive?booleanReactive signal indicating whether this anchor node currently has SceneGraph focus. Wire from the anchor node's focusActive input field. See IFocusBoundaryProps.focusActive.
handleAllKeys?booleanWhen true, the boundary claims ALL key events unconditionally. See IFocusBoundaryProps.handleAllKeys.
initialFocusobjectThe focusId object that will receive virtual focus when the boundary mounts. Must match the focusId (UseFocusableOptions.focusId) passed to one of the descendant useFocusable calls.
keyEvent?RemoteKeyEvent | nullThe reactive key event from the anchor node's keyEvent input field. See below.
navigation?"vertical" | "horizontal" | "grid"Navigation direction used when the focused component does not consume a directional key. See IFocusBoundaryProps.navigation.
navigationMap?NavigationMapExplicit navigation map for irregular layouts. See IFocusBoundaryProps.navigationMap.
onUnhandledKey?KeyPressHandlerMapDeclarative boundary-level key handlers. See IFocusBoundaryProps.onUnhandledKey.
setFocusPath?(path: string) => voidOpt-in automation output. See below.
setHandledKeys?(keys: Record<string, boolean>) => voidOutput setter for the handledKeys SceneGraph field on the anchor node. Pass outputs.handledKeys. See IFocusBoundaryProps.setHandledKeys.

IRootFocusBoundaryProps.keyEvent

The reactive key event from the anchor node's keyEvent input field.

The anchor node must extends: "FocusBoundaryBase" as const and declare keyEvent: FieldType.Object() as an input field; forward the reactive value here so this boundary becomes the dispatch entry point for the virtual focus tree.

IRootFocusBoundaryProps.setFocusPath

Opt-in automation output. When provided, RootFocusBoundary renders a wrapper <group>, tracks which SceneGraph node currently holds virtual focus, and writes the focused leaf's "/"-separated SG child-index path (e.g. "0/1/0/3/0") through this setter — clearing to "" when nothing is focused or focusActive is false.

Pass the anchor node's focusPath output field:

<RootFocusBoundary
  keyEvent={inputs.keyEvent}
  focusActive={inputs.focusActive}
  setHandledKeys={outputs.handledKeys}
  setFocusPath={outputs.focusPath}
  initialFocus={itemId}
>

When omitted, no wrapper <group> is rendered and no path tracking occurs — the root boundary stays node-less.


Did this page help you?