{"id":17772552,"url":"https://github.com/jamesplease/lrud","last_synced_at":"2025-10-24T03:23:04.909Z","repository":{"id":38838120,"uuid":"287107203","full_name":"jamesplease/lrud","owner":"jamesplease","description":"A React library for managing focus in TV apps.","archived":false,"fork":false,"pushed_at":"2024-09-13T01:28:08.000Z","size":289,"stargazers_count":42,"open_issues_count":2,"forks_count":22,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-10-21T01:38:05.248Z","etag":null,"topics":["lrud","react","tv"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jamesplease.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-08-12T20:16:32.000Z","updated_at":"2025-03-05T15:37:53.000Z","dependencies_parsed_at":"2024-04-15T20:02:01.522Z","dependency_job_id":"64cac12f-a882-4fad-bf6d-e35b2e4b37df","html_url":"https://github.com/jamesplease/lrud","commit_stats":{"total_commits":282,"total_committers":6,"mean_commits":47.0,"dds":0.5780141843971631,"last_synced_commit":"f22c31eaead5ffeef5df1beeca9e10b24a21416f"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/jamesplease/lrud","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jamesplease%2Flrud","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jamesplease%2Flrud/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jamesplease%2Flrud/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jamesplease%2Flrud/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jamesplease","download_url":"https://codeload.github.com/jamesplease/lrud/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jamesplease%2Flrud/sbom","scorecard":{"id":504142,"data":{"date":"2025-08-11","repo":{"name":"github.com/jamesplease/lrud","commit":"d2dfc787f568109b96d1b24ddb6502325db324e8"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3,"checks":[{"name":"Code-Review","score":0,"reason":"Found 1/11 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'main'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 28 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-19T22:51:45.353Z","repository_id":38838120,"created_at":"2025-08-19T22:51:45.353Z","updated_at":"2025-08-19T22:51:45.353Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":280729869,"owners_count":26381093,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-24T02:00:06.418Z","response_time":73,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["lrud","react","tv"],"created_at":"2024-10-26T21:39:44.661Z","updated_at":"2025-10-24T03:23:04.891Z","avatar_url":"https://github.com/jamesplease.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# lrud\n\n[![Travis build status](https://img.shields.io/travis/com/jamesplease/lrud/main)](https://travis-ci.com/github/jamesplease/lrud)\n[![npm version](https://img.shields.io/npm/v/@please/lrud.svg?color=brightgreen)](https://www.npmjs.com/package/@please/lrud)\n[![Code coverage](https://img.shields.io/coveralls/github/jamesplease/lrud?color=brightgreen)](https://coveralls.io/github/jamesplease/lrud)\n\nA React library for managing focus in LRUD applications.\n\n## Motivation\n\nThe native focus system that ships with browsers is one-dimensional: users may only move forward and back. Some applications\n(typically those that use remote controls or video game controllers) require a two dimensional focus system.\n\nBecause of this, it is up to the application to manage its own focus state. That's where this library comes in: it makes working\nwith two dimensional focus seamless.\n\n## Installation\n\nInstall using [npm](https://www.npmjs.com):\n\n```\nnpm install @please/lrud\n```\n\nor [yarn](https://yarnpkg.com/):\n\n```\nyarn add @please/lrud\n```\n\nThis library has the following peer dependencies:\n\n- [`react@^16.8.0 || react@^17.0.0`](https://www.npmjs.com/package/react)\n\n## Table of Contents\n\n- [**Guides**](#guides)\n  - [Basic Setup](#basic-setup)\n  - [Getting Started](#getting-started)\n  - [FAQ](#faq)\n- [**API Reference**](#api-reference)\n  - [\\\u003cFocusRoot/\\\u003e](#focusroot-)\n  - [\\\u003cFocusNode/\\\u003e](#FocusNode-)\n  - [useFocusNodeById()](#usefocusnodebyid-focusid-)\n  - [useLeafFocusedNode()](#useleaffocusednode)\n  - [useActiveNode()](#useactivenode)\n  - [useSetFocus()](#usesetfocus)\n  - [useNodeEvents()](#usenodeevents-focusid-events-)\n  - [useFocusHierarchy()](#usefocushierarchy)\n  - [useProcessKey()](#useprocesskey)\n  - [useFocusStoreDangerously()](#usefocusstoredangerously)\n- [**Interfaces**](#interfaces)\n  - [FocusNode](#focusnode)\n  - [LRUDEvent](#lrudevent)\n  - [MoveEvent](#moveevent)\n  - [GridMoveEvent](#gridmoveevent)\n  - [FocusStore](#focusstore)\n  - [FocusState](#focusstate)\n- [**Examples**](#examples)\n- [**Prior Art**](#prior-art)\n\n## Guides\n\n### Basic Setup\n\nRender the `FocusRoot` high up in your application's component tree.\n\n```jsx\nimport { FocusRoot } from '@please/lrud';\n\nexport default function App() {\n  return (\n    \u003cFocusRoot\u003e\n      \u003cAppContents /\u003e\n    \u003c/FocusRoot\u003e\n  );\n}\n```\n\nYou may then use FocusNode components to create a focusable elements on the page.\n\n```jsx\nimport { FocusNode } from '@please/lrud';\n\nexport default function Profile() {\n  return \u003cFocusNode className=\"profile\"\u003eProfile\u003c/FocusNode\u003e;\n}\n```\n\nThis library automatically moves the focus between the FocusNodes as the user inputs\nLRUD commands on their keyboard or remote control.\n\nThis behavior can be configured through the props of the FocusNode component. To\nlearn more about those props, refer to the API documentation below.\n\n### Getting Started\n\nThe recommended way to familiarize yourself with this library is to begin by looking at the [examples](#examples). The examples\ndo a great job at demonstrating the kinds of interfaces you can create with this library using little code.\n\nOnce you've checked out a few examples you should be in a better position to read through these API docs!\n\n### FAQ\n\n#### What is LRUD?\n\nLRUD is an acronym that stands for left-right-up-down, and it refers to the directional buttons typically found on remotes. In LRUD systems,\ninput devices usually also have some kind of \"submit\" button, and, less commonly, a back button.\n\n## API Reference\n\nThis section of the documentation describes the library's named exports.\n\n### `\u003cFocusRoot /\u003e`\n\nServes as the root node of a new focus hierarchy. There should only ever be one `FocusRoot` in each application.\n\nAll props are optional.\n\n| Prop            | Type    | Default value  | Description                                                                                             |\n| --------------- | ------- | -------------- | ------------------------------------------------------------------------------------------------------- |\n| `orientation`   | string  | `'horizontal'` | Whether the children of the root node are arranged horizontally or vertically.                          |\n| `wrapping`      | boolean | `false`        | Set to `true` for the navigation to wrap when the user reaches the start or end of the root's children. |\n| `pointerEvents` | boolean | `false`        | Set to `true` to enable pointer events. [Read the guide.](./guides/pointer-events.md) |\n\n```jsx\nimport { FocusRoot } from '@please/lrud';\n\nexport default function App() {\n  return (\n    \u003cFocusRoot orientation=\"vertical\"\u003e\n      \u003cAppContents /\u003e\n    \u003c/FocusRoot\u003e\n  );\n}\n```\n\n### `\u003cFocusNode /\u003e`\n\nA [Component](https://reactjs.org/docs/react-component.html) that represents a focusable node in your application.\n\nAll props are optional. Example usage appears beneath the props table.\n\n| Prop                        | Type                | Default value    | Description                                                                                                                                                                            |\n| --------------------------- | ------------------- | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `propsFromNode`             | function            |                  | A function you can supply to compute additional props to apply to the element. The function is passed one argument, the [focus node](#focusnode).                                      |\n| `className`                 | string              |                  | A class name to apply to this element.                                                                                                                                                 |\n| `focusedClass`              | string              | \"isFocused\"      | A class name that is applied when this element is focused.                                                                                                                             |\n| `focusedLeafClass`          | string              | \"isFocusedLeaf\"  | A class name that is applied this element is a focused leaf.                                                                                                                           |\n| `activeClass`               | string              | \"isActive\"       | A class name that is applied this element is active.                                                                                                                                   |\n| `disabledClass`             | string              | \"focusDisabled\"  | A class name that is applied this element is disabled.                                                                                                                                 |\n| `elementType`               | string\\|elementType | 'div'            | The React element type to render. For instance, `\"img\"` or [`motion.div`](https://www.framer.com/api/motion/component/).                                                               |\n| `focusId`                   | string              | `{unique_id}`    | A unique identifier for this node. Specify this yourself for debugging purposes, or when you will need to manually set focus to the node.                                              |\n| `orientation`               | string              | 'horizontal'     | Whether the children of this node are arranged horizontally or vertically. Pass `\"vertical\"` for vertical lists.                                                                       |\n| `wrapping`                  | boolean             | `false`          | Set to `true` for the navigation to wrap when the user reaches the start or end of the children list. For grids this sets wrapping in both directions.                                 |\n| `wrapGridHorizontal`        | boolean             | `false`          | Set to `true` for horizontal navigation in grids to wrap.                                                                                                                              |\n| `wrapGridVertical`          | boolean             | `false`          | Set to `true` for vertical navigation in grids to wrap.                                                                                                                                |\n| `disabled`                  | boolean             | `false`          | This node will not receive focus when `true`.                                                                                                                                          |\n| `isGrid`                    | boolean             | `false`          | Pass `true` to make this a grid.                                                                                                                                                       |\n| `isTrap`                    | boolean             | `false`          | Pass `true` to make this a focus trap.                                                                                                                                                 |\n| `forgetTrapFocusHierarchy`  | boolean             | `false`          | Pass `true` and, if this node is a trap, it will not restore their previous focus hierarchy when becoming focused again.                                                               |\n| `onMountAssignFocusTo`      | string              |                  | A focus ID of a nested child to default focus to when this node mounts.                                                                                                                |\n| `defaultFocusChild`         | number\\|function   |                  | The child index that should receive focus when focus is assigned to this focus node. Does not work with grids. We strongly recommend using [`useCallback`](https://reactjs.org/docs/hooks-reference.html#usecallback) when using the function form to avoid infinite render loops.     |\n| `defaultFocusColumn`        | number              | `0`              | The column index that should receive focus when focus is assigned to this focus node. Applies to grids only.                                                                           |\n| `defaultFocusRow`           | number              | `0`              | The row index that should receive focus when focus is assigned to this focus node. Applies to grids only.                                                                              |\n| `isExiting`                 | boolean             |                  | Pass `true` to signal that this node is animating out. Useful for certain kinds of exit transitions.                                                                                   |\n| `onFocused`                 | function            |                  | A function that is called when the node receives focus. Passed one argument, an [FocusEvent](#focusevent).                                                                             |\n| `onBlurred`                 | function            |                  | A function that is called when the node loses focus. Passed one argument, an [FocusEvent](#focusevent).                                                                                |\n| `onKey`                     | function            |                  | A function that is called when the user presses any TV remote key while this element has focus. Passed one argument, an [LRUDEvent](#lrudevent).                                       |\n| `onArrow`                   | function            |                  | A function that is called when the user presses a directional button. Passed one argument, an [LRUDEvent](#lrudevent).                                                                 |\n| `onLeft`                    | function            |                  | A function that is called when the user presses the left button. Passed one argument, an [LRUDEvent](#lrudevent).                                                                      |\n| `onUp`                      | function            |                  | A function that is called when the user presses the up button. Passed one argument, an [LRUDEvent](#lrudevent).                                                                        |\n| `onDown`                    | function            |                  | A function that is called when the user presses the down button. Passed one argument, an [LRUDEvent](#lrudevent).                                                                      |\n| `onRight`                   | function            |                  | A function that is called when the user presses the right button. Passed one argument, an [LRUDEvent](#lrudevent).                                                                     |\n| `onSelected`                | function            |                  | A function that is called when the user pressed the select button. Passed one argument, an [LRUDEvent](#lrudevent).                                                                    |\n| `onBack`                    | function            |                  | A function that is called when the user presses the back button. Passed one argument, an [LRUDEvent](#lrudevent).                                                                      |\n| `onMove`                    | function            |                  | A function that is called when the focused child index of this node changes. Only called for nodes with children that are _not_ grids. Passed one argument, a [MoveEvent](#moveevent). |\n| `onGridMove`                | function            |                  | A function that is called when the focused child index of this node changes. Only called for grids. Passed one argument, a [GridMoveEvent](#gridmoveevent).                            |\n| `children`                  | React Node(s)       |                  | Children of the Focus Node.                                                                                                                                                            |\n| `...rest`                   | any                 |                  | All other props are applied to the underlying DOM node.                                                                                                                                |\n\n```jsx\nimport { FocusNode } from '@please/lrud';\n\nexport default function Profile() {\n  return (\n    \u003cFocusNode\n      elementType=\"button\"\n      className=\"profileBtn\"\n      onSelected={({ node }) =\u003e {\n        console.log('The user just selected this profile', node);\n      }}\u003e\n      Profile\n    \u003c/FocusNode\u003e\n  );\n}\n```\n\n### `useFocusNodeById( focusId )`\n\nA [Hook](https://reactjs.org/docs/hooks-intro.html) that returns the focus node with ID `focusId`. If the node does not exist,\nthen `null` will be returned instead.\n\n```js\nimport { useFocusNodeById } from '@please/lrud';\n\nexport default function MyComponent() {\n  const navFocusNode = useFocusNodeById('nav');\n\n  console.log('Is the nav focused?', navFocusNode?.isFocused);\n}\n```\n\n### `useLeafFocusedNode()`\n\nA [Hook](https://reactjs.org/docs/hooks-intro.html) that returns the currently-in-focus leaf node.\n\n```js\nimport { useLeafFocusedNode } from '@please/lrud';\n\nexport default function MyComponent() {\n  const currentFocusedNode = useLeafFocusedNode();\n\n  console.log('Currently focused node', currentFocusedNode);\n}\n```\n\n### `useActiveNode()`\n\nA [Hook](https://reactjs.org/docs/hooks-intro.html) that returns the [active focus node](./guides/active.md), or `null` if\nno node is active. As a reminder, the active node is whatever node was selected last (similar to how interactive elements\nin the DOM become active after being clicked).\n\n```js\nimport { useActiveNode } from '@please/lrud';\n\nexport default function MyComponent() {\n  const activeNode = useActiveNode();\n\n  console.log('The active node:', activeNode);\n}\n```\n\n### `useSetFocus()`\n\nA [Hook](https://reactjs.org/docs/hooks-intro.html) that returns the `setFocus` function, which allows you to imperatively set\nthe focus.\n\nThis can be used to:\n\n- override the default navigation behavior of the library\n- focus modals or traps\n- exit traps\n\n```js\nimport { useSetFocus } from '@please/lrud';\n\nexport default function MyComponent() {\n  const setFocus = useSetFocus();\n\n  useEffect(() =\u003e {\n    setFocus('nav');\n  }, []);\n}\n```\n\n### `useNodeEvents( focusId, events )`\n\nA [Hook](https://reactjs.org/docs/hooks-intro.html) that allows you to tap into a focus nodes' focus lifecycle events. Use this hook when\nyou need to respond to the focus lifecycle for a node that is not in your current component.\n\n```js\nimport { useNodeEvents } from '@please/lrud';\n\nexport default function MyComponent() {\n  useNodeEvents('nav', {\n    focus(navNode) {\n      console.log('The nav node is focused', navNode);\n    }\n\n    blur(navNode) {\n      console.log('The nav node is no longer focused', navNode);\n    }\n  });\n}\n```\n\nEach callback receives a single argument, the [focus node](#focusnode).\n\nThe available event keys are:\n\n| Event key  | Called when                         |\n| ---------- | ----------------------------------- |\n| `focus`    | the focus node receives focus.      |\n| `blur`     | the focus node loses focus.         |\n| `active`   | the focus node becomes active.      |\n| `inactive` | the focus node is no longer active. |\n| `disabled` | the focus node is set as disabled.  |\n| `enabled`  | the focus node is enabled.          |\n\n### `useFocusHierarchy()`\n\nA [Hook](https://reactjs.org/docs/hooks-intro.html) that returns an array representing the focus hierarchy, which are the nodes\nthat are currently focused. Each entry in the array is a focus node.\n\n```js\nimport { useFocusHierarchy } from '@please/lrud';\n\nexport default function MyComponent() {\n  const focusHierarchy = useFocusHierarchy();\n\n  console.log(focusHierarchy);\n  // =\u003e [\n  //   { focusId: 'root', ... },\n  //   { focusId: 'homePage', ... },\n  //   { focusId: 'mainNav', ... },\n  // ]\n}\n```\n\n### `useProcessKey()`\n\nA [Hook](https://reactjs.org/docs/hooks-intro.html) that allows you to imperatively trigger LRUD key presses.\n\n```js\nimport { useProcessKey } from '@please/lrud';\n\nfunction MyComponent() {\n  const processKey = useProcessKey();\n\n  useEffect(() =\u003e {\n    // Imperatively trigger a down key press\n    processKey.down();\n\n    // Imperatively trigger a back key press\n    processKey.select();\n\n    // ...same, for the back button.\n    processKey.back();\n  }, []);\n}\n```\n\nThe full API is as follows.\n\n```ts\ninterface ProcessKey {\n  select: () =\u003e void;\n  back: () =\u003e void;\n  down: () =\u003e void;\n  left: () =\u003e void;\n  right: () =\u003e void;\n  up: () =\u003e void;\n}\n```\n\n### `useFocusStoreDangerously()`\n\n\u003e ⚠️ Heads up! The FocusStore is an internal API. We strongly discourage you from accessing properties or calling\n\u003e methods on the FocusStore directly!\n\nA [Hook](https://reactjs.org/docs/hooks-intro.html) that returns the\n[FocusStore](#focusstore). Typically, you should not need to use this hook.\n\nOne use-case for this hook is attaching the `focusStore` to the window when developing, which can be useful\nfor debugging purposes.\n\n```js\nimport { useFocusStoreDangerously } from '@please/lrud';\n\nexport default function MyComponent() {\n  const focusStore = useFocusStoreDangerously();\n\n  useEffect(() =\u003e {\n    if (process.env.NODE_ENV !== 'production') {\n      window.focusStore = focusStore;\n    }\n  }, []);\n}\n```\n\n## Interfaces\n\nThese are the objects you will encounter when using this library.\n\n### `FocusNode`\n\nA focus node. Each `\u003cFocusNode/\u003e` React component creates one of these.\n\n| Property                      | Type             | Description                                                                                                                       |\n| ----------------------------- | ---------------- | --------------------------------------------------------------------------------------------------------------------------------- |\n| `elRef`                       | ref              | A ref containing the HTML element for this node.                                                                                  |\n| `focusId`                     | string           | A unique identifier for this node.                                                                                                |\n| `children`                    | Array\\\u003cstring\\\u003e  | An array of focus IDs representing the children of this node.                                                                     |\n| `focusedChildIndex`           | number\\|null     | The index of the focused child of this node, if there is one.                                                                     |\n| `prevFocusedChildIndex`       | number\\|null     | The index of the previously-focused child of this node, if there was one.                                                         |\n| `isFocused`                   | boolean          | `true` when this node is focused.                                                                                                 |\n| `isFocusedLeaf`               | boolean          | Whether or not this node is the leaf of the focus hierarchy.                                                                      |\n| `active`                      | boolean          | `true` this node is active.                                                                                                       |\n| `disabled`                    | boolean          | `true` when this node is disabled.                                                                                                |\n| `isExiting`                   | boolean          | Set to `true` to indicate that the node will be animating out. Useful for certain exit animations.                                |\n| `wrapping`                    | boolean          | `true` when the navigation at the end of the node will wrap around to the other side.                                             |\n| `wrapGridVertical`                | boolean          | `true` when grid rows will wrap.                                                                                                  |\n| `wrapGridHorizontal`             | boolean          | `true` when grid columns will wrap.                                                                                               |\n| `isRoot`                      | boolean          | `true` this is the root node.                                                                                                     |\n| `trap`                        | boolean          | `true` when this node is a focus trap.                                                                                            |\n| `forgetTrapFocusHierarchy`   | boolean          | Set to `false` and a focus trap will restore its previous hierarchy upon becoming re-focused.                                      |\n| `parentId`                    | string \\| `null` | The focus ID of the parent node. `null` for the root node.                                                                        |\n| `orientation`                 | string           | A string representing the orientation of the node (either `\"horizontal\"` or `\"vertical\"`)                                         |\n| `navigationStyle`             | string           | One of `'first-child'` or `'grid'`                                                                                                |\n| `nodeNavigationItem`          | string           | How this node is used in the navigation algorithm. Possible values are 'default'`, 'grid-container'`, `'grid-row'`, `'grid-item'` |\n| `defaultFocusColumn`          | number           | The column index that should receive focus when focus is assigned to this focus node. Applies to grids only.                      |\n| `defaultFocusRow`             | number           | The row index that should receive focus when focus is assigned to this focus node. Applies to grids only.                         |\n| `_gridColumnIndex`            | number \\| `null` | The focused column index of a grid.                                                                                               |\n| `_gridRowIndex`               | number \\| `null` | The focused row index of a grid.                                                                                                  |\n| `_focusTrapPreviousHierarchy` | Array\\\u003cstring\\\u003e  | The previous focus hierarchy of a trap.                                                                                           |\n\n### `LRUDEvent`\n\nAn object that is passed to you in the LRUD-related callbacks of a [`FocusNode` component](#FocusNode-):\n\n- `onKey`\n- `onArrow`\n- `onLeft`\n- `onRight`\n- `onUp`\n- `onDown`\n- `onSelected`\n- `onBack`\n\n| Property          | Type                    | Description                                                                                                            |\n| ----------------- | ----------------------- | ---------------------------------------------------------------------------------------------------------------------- |\n| `key`             | string                  | A string representing the key that was pressed. One of `\"left\"`, `\"right\"`, `\"up\"`, `\"down\"`, `\"select\"`, or `\"back\"`. |\n| `isArrow`         | boolean                 | Whether or not this key is an arrow.                                                                                   |\n| `node`            | [FocusNode](#focusnode) | The current [`FocusNode`](#focusnode) that received this event as the event propagates up the focus hierarchy. Analagous to [`event.currentTarget`](https://developer.mozilla.org/en-US/docs/Web/API/Event/currentTarget) |\n| `targetNode`      | [FocusNode](#focusnode) | The leaf [`FocusNode`](#focusnode) from which this event propagated. Analagous to [`event.target`](https://developer.mozilla.org/en-US/docs/Web/API/Event/target) |\n| `preventDefault`  | function                | Call this to stop the default behavior of the event. Commonly used to override the navigation behavior                 |\n| `stopPropagation` | function                | Call this to stop the propagation of the event.                                                                        |\n\n### `MoveEvent`\n\nAn object that is passed to you in the `onMove` callback of a [`FocusNode` component](#FocusNode-).\n\n| Property         | Type                              | Description                                                                  |\n| ---------------- | --------------------------------- | ---------------------------------------------------------------------------- |\n| `orientation`    | string                            | The orientation of the move. Either `\"horizontal\"` or `\"vertical\"`.          |\n| `direction`      | string                            | The direction of the move. Either `\"forward\"` or `\"back\"`.                   |\n| `arrow`          | string                            | The arrow that was pressed. One of `\"up\"`, `\"down\"`, `\"left\"`, or `\"right\"`. |\n| `prevChildIndex` | number                            | The index of the previously-focused child [`FocusNode`](#focusnode).         |\n| `nextChildIndex` | number                            | The index of the child [`FocusNode`](#focusnode) that is now focused.        |\n| `prevChildNode`  | [FocusNode](#focusnode) \\| `null` | The previously-focused [`FocusNode`](#focusnode).                            |\n| `nextChildNode`  | [FocusNode](#focusnode)           | The child [`FocusNode`](#focusnode) that is now focused.                     |\n\n### `GridMoveEvent`\n\nAn object that is passed to you in the `onGridMove` callback of a [`FocusNode` component](#FocusNode-) that is a grid.\n\n| Property          | Type   | Description                                                                  |\n| ----------------- | ------ | ---------------------------------------------------------------------------- |\n| `orientation`     | string | The orientation of the move. Either `\"horizontal\"` or `\"vertical\"`.          |\n| `direction`       | string | The direction of the move. Either `\"forward\"` or `\"back\"`.                   |\n| `arrow`           | string | The arrow that was pressed. One of `\"up\"`, `\"down\"`, `\"left\"`, or `\"right\"`. |\n| `prevRowIndex`    | number | The index of the previously-focused row.                                     |\n| `nextRowIndex`    | number | The index of the newly-focused row.                                          |\n| `prevColumnIndex` | number | The index of the previously-focused column.                                  |\n| `nextColumnIndex` | number | The index of the newly-focused column.                                       |\n\n### `FocusEvent`\n\nAn object that is passed to you in the `onFocused` and `onBlurred` callbacks of a [`FocusNode` component](#FocusNode-).\n\n| Property      | Type                                 | Description                                                                                                                                                                                               |\n| ------------- | ------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `focusNode`   | [FocusNode](#focusnode)\\|`undefined` | The newly-focused leaf [`FocusNode`](#focusnode).                                                                                                                                                         |\n| `blurNode`    | [FocusNode](#focusnode)\\|`undefined` | The previously-focused leaf [`FocusNode`](#focusnode).                                                                                                                                                    |\n| `currentNode` | [FocusNode](#focusnode)              | The [`FocusNode`](#focusnode) that is receiving the event, as it traverses the focus hierarchy. Analogous to [event.currentTarget](https://developer.mozilla.org/en-US/docs/Web/API/Event/currentTarget). |\n\n### `FocusStore`\n\n\u003e ⚠️ Heads up! This is an internal API. We strongly discourage you from accessing properties or calling\n\u003e methods on the FocusStore directly!\n\nAn object that represents the store that contains all of the state related to what is in focus.\nTypically, you should not need to interact with this object directly, but it is made available to you\nfor advanced use cases that you may have.\n\n| Property                 | Type     | Description                                                            |\n| ------------------------ | -------- | ---------------------------------------------------------------------- |\n| `getState`               | function | Returns the current [FocusState](#focusstate)                          |\n| `createNodes`            | function | Creates one or more focus nodes in the tree.                           |\n| `deleteNode`             | function | Deletes a focus node from the tree.                                    |\n| `setFocus`               | function | Imperatively assign focus to a particular focus node.                  |\n| `updateNode`             | function | Update an existing node. Used to, for example, set a node as disabled. |\n| `processKey `            | object   | Contains methods to trigger key presses.                               |\n| `handleArrow`            | function | Updates store state after arrow key presses.                           |\n| `handleSelect`           | function | Updates store state after select button presses.                       |\n| `configurePointerEvents` | function | Enable or disable pointer events. Receives one argument, a `boolean`.  | \n| `destroy`                | function | Call when disposing of the store. Cleans up event listeners.           |\n\n\n### `FocusState`\n\n\u003e ⚠️ Heads up! This is an internal API. We strongly discourage you from accessing this object directly in your application.\n\nAn object representing the state of the focus in the app.\n\n| Property                   | Type             | Description                                                             |\n| -------------------------- | ---------------- | ----------------------------------------------------------------------- |\n| `focusedNodeId`            | string           | The ID of the leaf node in the focus hierarchy.                         |\n| `focusHierarchy`           | Array            | An array of node IDs representing the focus hierarchy.                  |\n| `activeNodeId`             | string \\| `null` | The ID of the active node, if there is one.                             |\n| `nodes`                    | Object           | A mapping of all of the focus nodes that exist.                         |\n| `interactionMode`          | string           | The active interaction mode of the app. Either `\"lrud\"` or `\"pointer\"`. |\n| `_hasPointerEventsEnabled` | boolean          | A boolean used internally for managing the creation of nested nodes.    |\n| `_hasPointerEventsEnabled` | boolean          | Whether or not pointer events are currently enabled.                    |\n| `_updatingFocusIsLocked`   | boolean          | A boolean used internally for managing the creation of nested nodes.    |\n\n## Examples\n\nThis repository contains example projects showing common patterns when using this library. Each example\nis located in the [`./examples`](./examples) folder.\n\nInstructions for running the examples are found in the README file for each example.\n\n#### Basic Examples\n\n- [**Basic Layout**](./examples/basic/basic-layout) - Demonstrates using the `orientation` prop for vertical and horizontal lists\n- [**Wrapping**](./examples/basic/wrapping) - Includes usage of the `wrapping` prop\n- [**Grid**](./examples/basic/grid) - Shows how to build a grid of focus nodes\n- [**Disabled Focus Nodes**](./examples/basic/disabled-focus-nodes) - Shows how to disable focus nodes using the `disabled` prop\n- [**Focus Trap**](./examples/basic/focus-trap) - Demonstrates how to create focus traps\n\n#### Advanced Examples\n\n- [**Modal**](./examples/advanced/modal) - Show and hide an animated modal overlay\n- [**Animated Page Transition**](./examples/advanced/animated-page-transition) - Animate between two pages\n- [**`isExiting` Page Transition**](./examples/advanced/animated-page-transition) - Animating between pages _without_ user input\n- [**Pointer Events**](./examples/advanced/pointer-events) - An interface that supports both pointer and LRUD input\n- [**Grid That Remembers Position**](./examples/advanced/grid-remembers-position) - A grid that returns you to your previous position when you exit and re-enter it\n- [**TV App**](./examples/advanced/tv-app) - A simple TV app\n\n## Prior Art\n\n- [\"Pass the Remote\" on the Netflix Tech Blog](https://medium.com/netflix-techblog/pass-the-remote-user-input-on-tv-devices-923f6920c9a8)\n- [bbc/lrud](https://github.com/bbc/lrud)\n- [react-tv](https://github.com/raphamorim/react-tv)\n- [@xdproto/focus](https://github.com/jamesplease/focus) _(the predecessor of this library)_\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjamesplease%2Flrud","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjamesplease%2Flrud","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjamesplease%2Flrud/lists"}