createBoundaryState

Shared logic for FocusBoundary and RootFocusBoundary

Available since @roku-sdk/navigation 2.19.0

import { createBoundaryState } from "@roku-sdk/navigation";
createBoundaryState(opts: BoundaryStateOptions): { canNavigate: (focusId: object | null, direction: "up" | "down" | "left" | "right") => boolean; focusedId: Accessor<object | null>; focusOrder: Accessor<object[]>; handleKeyEvent: (data: IFocusBoundaryKeyData) => boolean; handlerRegistry: WeakMap<object, RegisteredHandlers>; hasFootprint: (focusId: object) => boolean; hasOwnReturnFootprint: Accessor<boolean>; hasReturnFootprint: (focusId: object) => boolean; isFocused: (focusId: object) => boolean; registerFocusable: (focusId: object, handlers: RegisteredHandlers) => void; releaseCaptures: () => void; remembersLastFocus: (focusId: object) => boolean | undefined; resetFocus: () => void; returnTarget: Accessor<object | null>; setFocus: (focusId: object) => void; unregisterFocusable: (focusId: object) => void }

Deprecated.

Test hook that was never meant to be public. It will be removed from the public exports in the next major release.

Description

Shared logic for FocusBoundary and RootFocusBoundary. Encapsulates handler registry, focus order, registration, and the key dispatch chain.

Dispatch chain (per key event):

  1. Focused child's onKey — consumed? stop.
  2. Directional navigation — moved? stop. Key-down only.
  3. Boundary-level onKey prop — consumed? stop.
  4. return false — bubble to parent boundary.

A handler in steps 1 or 3 declines (passes the key on down this chain) by returning bubble.

Presses and releases take the same route; only step 2 is press-only, since a key-up never moves focus. An unconsumed release bubbles just as an unconsumed press does.

Parameters

NameTypeDescription
optsBoundaryStateOptionsGetters for the boundary's props and its shared capture registry.

Return values

The boundary's registry, focus state and key dispatch functions.

Types

BoundaryStateOptions

Deprecated.

Test hook that was never meant to be public. It will be removed from the public exports in the next major release.

Options for createBoundaryState. All prop reads are wrapped in getters so the factory's dispatch functions always see the current prop value at call time.

NameTypeDescription
captureRegistry?KeyPressCaptureRegistryShared registry from the root boundary. Capture is inert when omitted.
getCaptureKeyPress?() => boolean | undefinedSee IFocusBoundaryProps.captureKeyPress. Omitted in tests that do not exercise capture.
getInitialFocus() => objectReads IFocusBoundaryProps.initialFocus, the id that receives focus on first entry.
getNavigation() => "vertical" | "horizontal" | "grid" | undefinedReads IFocusBoundaryProps.navigation, the arrow-key layout of the boundary's children.
getNavigationMap() => NavigationMap | undefinedReads IFocusBoundaryProps.navigationMap, the explicit per-id arrow-key targets.
getOnUnhandledKey() => KeyPressHandlerMap | undefinedReads IFocusBoundaryProps.onUnhandledKey, the handlers for keys no child consumed.
getRememberLastFocus() => boolean | undefinedReads IFocusBoundaryProps.rememberLastFocus, whether re-entry restores the last focused id.
hasOwnReturnFootprint?Accessor<boolean>Whether this boundary itself sits on the return path while inactive. Defaults to () => !isActive(), which treats a lone state as its own root. FocusBoundary supplies a chain-aware accessor instead.
isActiveAccessor<boolean>Whether the boundary is on the active focus path and may handle keys.
reactivatesInPlace?booleanThe root reactivates in place. Its focusActive transition re-fires onFocusGained on the current focusedId and never calls resetFocus, so rememberLastFocus does not choose the return target. Nested boundaries leave this unset: their parent re-enters them through resetFocus.

IFocusBoundaryKeyData

Key event data forwarded from BrightScript to TypeScript.

NameTypeDescription
keyRemoteKeyNameThe remote key, for example "OK" or "back".
pressbooleantrue on key-down and false on key-up.

RegisteredHandlers

Deprecated.

Test hook that was never meant to be public. It will be removed from the public exports in the next major release.

Extended handler interface used internally by the registry. Adds fields only set by nested FocusBoundary registrations — never by end users.

NameTypeDescription
derivedKeys?Accessor<Record<string, boolean>>Reactive accessor returning the full set of keys this component handles. Only set by nested FocusBoundary registrations.
dispatchKey?(key: RemoteKeyName, press: boolean) => booleanFull key dispatch function used by nested boundaries. When present, takes precedence over the declarative onKey map in the parent's dispatch chain. Returns true to consume, false to bubble.
remembersLastFocus?Accessor<boolean>Whether this nested boundary restores its last focused child on re-entry. See below.

Extends FocusHandlers.

RegisteredHandlers.remembersLastFocus

Whether this nested boundary restores its last focused child on re-entry. Only set by nested FocusBoundary registrations.

Exposed so a parent that reuses a registered identity across different content — a FlexGrid parking focus on a recycled cell's boundary — can detect the stale-sub-focus configuration and warn in development.


What’s Next

Did this page help you?