npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

tachyon-tv-nav

v32.0.0

Published

TV Navigation Helpers

Downloads

5

Readme

Tachyon TV Nav

Provides 2D UI navigation support for arbitrary user keypress-based input.

Features:

  • Compatible with React Concurrent mode
  • Arrow key support out of the box
  • Highly composable
  • Virtualization support
  • Minimizes expensive sub-tree re-renders with subscription based focus listeners

Testing / Demo

This package has a corresponding package-example. It is extremely handy for developing against as well to test any changes.

Setting Up

import { NavigationRoot } from 'tachyon-tv-nav';

function App() {
  // Without defining a custom navigation key mapping, the package will
  // listen on arrow key codes and trigger navigation events accordingly.
  const customKeyMap = { [KeyValue.Escape]: window.history.back // ... };

  return (
    <NavigationRoot customKeyMap={customKeyMap} focusSeed={someDynamicPageRelatedValue}>
      {/* app */}
    </NavigationRoot>
  );
}

Basic Use

Creating Navigation Elements

The useFocus hook is used to determine when an element is focused within a navigation area. An optional refFocusHandler function can be used to call ".focus()" on any component that supports the interface.

import type { FC } from 'react';
import { refFocusHandler, useFocus } from 'tachyon-tv-nav';

export const Card: FC<{ focusIndex: number }> = ({ focusIndex }) => {
  const { focused } = useFocus(focusIndex);
  return <div ref={refFocusHandler(focused)}>{/* ... */}</div>;
};

Imperatively Taking Focus

Focus can be imperatively taken by a registered navigation element using takeFocus:

import type { FC } from 'react';
import { refFocusHandler, useFocus } from 'tachyon-tv-nav';

export const Card: FC<{ focusIndex: number }> = ({ focusIndex }) => {
  const { focused, takeFocus } = useFocus(focusIndex);
  return <div ref={refFocusHandler(focused)}>{/* ... */}</div>;
};

Creating Navigation Areas

A navigation area is a grouping of focusable children that are logically related based on a common navigation direction or paradigm between them.

There are 4 types of navigation areas:

  • NodeNav: for a single focusable child
  • HorizontalNav: for a horizontal line of focusable children
  • VerticalNav: for a vertical line of focusable children
  • GridNav: for a grid (with fixed width and height based on number of children) of focusable children

Horizontal and Vertical Nav

Use HorizontalNav or VerticalNav to create a navigation area in their respective orientations:

import type { FC } from 'react';
import { HorizontalNav } from 'tachyon-tv-nav';
import { Card } from './Card';

/**
 * Left / right arrow key presses navigate between the elements in the navigation area.
 */
export const ListFeature: FC<{ focusIndex: number }> = ({ focusIndex }) => (
  <HorizontalNav focusIndex={focusIndex} elementCount={2}>
    <Card focusIndex={0} />
    <Card focusIndex={1} />
  </HorizontalNav>
);

GridNav

import type { FC } from 'react';
import { GridNav } from 'tachyon-tv-nav';
import { Card } from './Card';

/**
 * Left/right/up/down arrow key presses are all used to navigate between the elements in the navigation area.
 * It is up to you to ensure that the Grid is laid out correctly.
 */
export const GridFeature: FC<{ focusIndex: number }> = ({ focusIndex }) => (
  <GridNav focusIndex={focusIndex} elementsPerRow={2} elementCount={40}>
    {/* First row */}
    <Card focusIndex={0} />
    <Card focusIndex={1} />
    {/* Second row */}
    <Card focusIndex={3} />
    <Card focusIndex={4} />
    {/* ... */}
  </GridNav>
);

Composing Areas

All navigation areas can be composed together to build complex layouts. Focus will transition between areas when the current area does not have another element in the direction the user is trying to navigate assuming that there is another area in that direction with elements. See the package example for more inspiration.

import type { FC } from 'react';
import { HorizontalNav, VerticalNav } from 'tachyon-tv-nav';

export const PageLayout: FC<{ focusIndex: number }> = ({ focusIndex }) => (
  <VerticalNav focusIndex={focusIndex} elementCount={2} >
    <HorizontalNav focusIndex={0} elementCount={...}>{/* ... */}</HorizontalNav>
    <HorizontalNav focusIndex={1} elementCount={...}>{/* ... */}</HorizontalNav>
  </VerticalNav>
);

Advanced Use

Preceding and Precluding The Default Input Handler Behaviors

Each nav area exposes onDown, onLeft, onRight, and onUp which takes a callback that will be invoked when the area is focused and the intent is triggered (E.G. pressing right when the area is focused will trigger onRight).

If the callback returns true then the normal nav behavior will be precluded.

Note: Stable functions should be used to avoid expensive re-renders when possible.

import type { FC } from 'react';
import { HorizontalNav } from 'tachyon-tv-nav';
import { FeatureRequiringSpecialActions } from './FeatureRequiringSpecialActions';

const onLeft = () => {
  // some special actions
  // stop propagation
  return true;
};

const onRight = () => {
  // some special actions
  // propagate to the normal nav handler (move focus right)
  return;
};

export const GridFeature: FC<{ focusIndex: number }> = ({ focusIndex }) => (
  <HorizontalNav
    focusIndex={focusIndex}
    elementCount={1}
    onLeft={onLeft}
    onRight={onRight}
  >
    <FeatureRequiringSpecialActions focusIndex={0} />
  </HorizontalNav>
);

Listening On An Area's Child Focus Index

Sometimes it's useful to know the index of a parent navigation area's current focused child (like when you want to manage child virtualization). We provide useAreaChildFocusIndex for this purpose:

import { useAreaChildFocusIndex } from 'tachyon-tv-nav';

function useCustomNavBehavior() {
  const childFocusIndex = useAreaChildFocusIndex();
  // do something interesting with the value
}