react-tv-space-navigation

repository·main·Indexed 43 days ago

https://github.com/bamlab/react-tv-space-navigation

A React wrapper for the LRUD library providing cross-platform spatial navigation for TV applications on AndroidTV, tvOS, and Web. It offers a declarative API with components like SpatialNavigationRoot, SpatialNavigationView, and SpatialNavigationFocusableView to manage focus and navigation trees across different platforms.

Tokens
18.9K
Snippets
45
Records
67
Agent score
55%

What's inside react-tv-space-navigation

  1. Overview of react-tv-space-navigation

    main

    What is react-tv-space-navigation?

    react-tv-space-navigation is a React wrapper around the LRUD library, designed to provide consistent spatial navigation across multiple platforms, including AndroidTV, tvOS, and web-based TV devices.

    When to use it

    • Use this package if you need a cross-platform solution that works on Web, AndroidTV, and tvOS.
    • Do NOT use this package if you are building a native app and do not require web support; in that case, the native react-native-tvos solution is recommended.

    Core Philosophy

    The library aims for a simple, declarative API. Instead of managing complex hooks or state manually, you declare components that participate in the spatial navigation tree.

  2. Use the SpatialNavigationNode child function parameters

    main

    When isFocusable is set to true, the children prop can be a function that receives an object containing the following state properties. These allow you to style or conditionally render elements based on the navigation state:

    • isFocused: boolean - Returns whether the node is focused right now.
    • isActive: boolean - Returns whether the node is active (the node itself or one of its descendants is focused).
    • isRootActive: boolean - Returns whether the root is active. Useful for hiding focus indicators on page elements when a different root (like a side-menu) is active.
  3. Understand SpatialNavigationDeviceTypeProvider

    main

    The SpatialNavigationDeviceTypeProvider manages the context of which device is currently controlling focus. This allows the library to switch input handling logic based on user interaction.

    Supported Device Types

    • remoteKeys: Represents standard TV remote button presses.
    • remotePointer: Represents a TV remote pointer (e.g., LG Magic Remote) or a mouse on the web.

    Behavior

    • By default, the provider uses remoteKeys.
    • The provider automatically updates its value to the most recent device type used by the user. For example, if a user moves a mouse, the context switches to remotePointer.
    • You should place this provider at the top level of your application to ensure all components can react to device changes.
  4. Configure remote control for Spatial Navigation

    main

    To enable Spatial Navigation in either the Web or TV applications, you must configure the remoteControlManager. This involves mapping your keyboard or remote control keys to LRUD (Left, Right, Up, Down) directions.

    For implementation details on managing platform-specific remote controls, refer to the components in ./src/components/remote-control/.

  5. Handle side menus with multiple navigators

    main
    For persistent side menus that exist outside the main page content, use a dedicated spatial navigator for the menu. To switch between the menu and the page, detect when the user navigates 'off-screen' using onDirectionHandledWithoutMovement and isActive from SpatialNavigationRoot. When the user moves off-screen, disable the page's navigator and enable the menu's navigator.
  6. Use SpatialNavigationVirtualizedGrid for grid layouts

    main

    The SpatialNavigationVirtualizedGrid is a specialized version of a virtualized list designed to render rows of items.

    • Structure: It renders a series of horizontal rows, where each row contains numberOfColumns items.
    • Orientation: It only supports vertical orientation (the list scrolls vertically), but users can still navigate between elements in any direction (up, down, left, right) using spatial navigation.
  7. Handle conditional rendering and dynamic ordering in spatial navigation

    main

    When navigation elements are conditionally rendered or part of a dynamic list, the library's internal Left-Right-Up-Down (LRUD) order can become corrupted. This happens because the library relies on React's rendering order to determine spatial relationships. If an element is removed and re-added, it may be registered in the wrong position relative to other elements.

    To prevent this, always wrap conditionally rendered elements or elements within a dynamic list with a <SpatialNavigationNode /> that remains present in the React tree even when its children change.

    Conditional Visibility

    Instead of rendering an element directly based on a condition, wrap the condition inside a <SpatialNavigationNode /> so the node itself is never removed from the tree.

    Dynamic Lists

    When mapping over a list that can change, wrap each element in a <SpatialNavigationNode /> using the list index as the key. While using indices as keys is generally discouraged in React, it is necessary here to ensure the SpatialNavigationNode components remain stable even if the list items move, preserving the correct spatial order.

    // Handling conditional visibility
    // DON'T ❌
    <View>
      <Text>Title</Text>
      {isVisible && <Element />}
    </View>
    
    // DO ✅
    <View>
      <Text>Title</Text>
      <SpatialNavigationNode>{isVisible && <Element />}</SpatialNavigationNode>
    </View>
    
    // Handling dynamic lists
    // DON'T ❌
    <View>
      {elements.map((element) => <Element element={element} />)}
    </View>
    
    // DO ✅
    <View>
      {elements.map((element, index) =>
        <SpatialNavigationNode key={index}>
          <Element element={element} />
        </SpatialNavigationNode>
      )}
    </View>
  8. Handle text inputs and keyboards

    main

    When using text inputs on a TV, follow these steps:

    1. Use SpatialNavigationKeyboardLocker to lock the remote control when the keyboard appears.
    2. Wrap your TextInput in a SpatialNavigationNode to catch custom focus events.
    3. Pass the native focus down to the underlying native TextInput component.
  9. Run the Hoppix TVOS application on Apple TV or Android TV

    main

    You can run the demo application on Apple TV or Android TV using React Native's Metro bundler. Ensure you have the necessary emulator or device configurations set up in Xcode (for Apple TV) or Android Studio (for Android TV).

    Use one of the following commands to start the application:

    • yarn start: Initiates the Metro bundler.
    • yarn ios: Runs the application on an iOS/Apple TV target.
    • yarn android: Runs the application on an Android/Android TV target.
    yarn start
    yarn ios
    yarn android