|
1 | 1 | <div align="center"> |
2 | 2 | <div align="center"><img src="./logo.svg" width="170" height="170" alt="Sunbeam Logo"/></div> |
3 | | -<h1 align="center">react-sunbeam</h1> |
| 3 | +<h1 align="center">React Sunbeam</h1> |
4 | 4 | <p> |
5 | | - Spatial navigation and key press management solution for React apps |
| 5 | + Spatial navigation and key press management for React apps |
6 | 6 | </p> |
7 | 7 | <p align="center"> |
8 | 8 | <a href="https://github.com/vovaguguiev/react-sunbeam/actions?workflow=Test"><img src="https://github.com/vovaguguiev/react-sunbeam/workflows/Test/badge.svg" alt="Test Status"></a> |
|
11 | 11 | </p> |
12 | 12 | </div> |
13 | 13 |
|
14 | | -## Installation |
15 | | - |
16 | 14 | ```bash |
17 | 15 | npm install react-sunbeam |
18 | 16 | ``` |
19 | 17 |
|
20 | | -or |
| 18 | +## 🧐 Why |
21 | 19 |
|
22 | | -```bash |
23 | | -yarn add react-sunbeam |
24 | | -``` |
| 20 | +React Sunbeam provides a flexible and easy-to-use solution for spacial navigation and focus management. |
| 21 | +There is wide variety of applications that can benefit from it. |
25 | 22 |
|
26 | | -## Usage |
| 23 | +### 🎮 TV and Gaming Console Apps |
27 | 24 |
|
28 | | -```js |
29 | | -// app.js |
30 | | -import React, { useCallback, useEffect } from "react" |
31 | | -import { Root, Focusable, FocusManager, useFocusManager } from "react-sunbeam" |
32 | | -import { FocusableCard } from "./FocusableCard" |
33 | | - |
34 | | -function App() { |
35 | | - const { setFocus, moveFocusLeft, moveFocusRight, moveFocusUp, moveFocusDown } = useFocusManager() |
36 | | - |
37 | | - const onKeyDown = useCallback( |
38 | | - (event) => { |
39 | | - if (!(event instanceof KeyboardEvent)) return |
40 | | - switch (event.key) { |
41 | | - case "ArrowRight": |
42 | | - moveFocusRight() |
43 | | - return |
44 | | - case "ArrowLeft": |
45 | | - moveFocusLeft() |
46 | | - return |
47 | | - case "ArrowUp": |
48 | | - moveFocusUp() |
49 | | - return |
50 | | - case "ArrowDown": |
51 | | - moveFocusDown() |
52 | | - return |
53 | | - } |
54 | | - }, |
55 | | - [focusManager] |
56 | | - ) |
| 25 | +Most of the applications that are running on leanback devices and controlled with a remote or gaming controller need an implementation of directional navigation. |
| 26 | +A lot of companies end up rolling out their own custom solutions for that. |
| 27 | +Usually those solutions are either very simple, like using column and row indices, or implementing spatial navigation which works much better for the end user but is tricky to implement correctly and hard to maintain. |
| 28 | +React Sunbeam provides a well-tested, opensource implementation of spatial navigation that is easy to integrate into any existing React app. |
57 | 29 |
|
58 | | - useEffect(() => { |
59 | | - document.addEventListener("keydown", onKeyDown) |
60 | | - return () => document.removeEventListener("keydown", onKeyDown) |
61 | | - }, [onKeyDown]) |
| 30 | +### ⌨️ Web Apps with Keyboard Navigation |
62 | 31 |
|
63 | | - return ( |
64 | | - <div> |
65 | | - <FocusableCard focusKey="card-1" /> |
66 | | - <Focusable focusKey="item1"> |
67 | | - {({ focused }) => <div>{focused ? "I am focused" : "I am not focused"}</div>} |
68 | | - </Focusable> |
69 | | - <Focusable focusKey="menuContainer"> |
70 | | - <div> |
71 | | - <Focusable focusKey="menuItem1"> |
72 | | - {({ focused }) => ( |
73 | | - <div style={{ backgroundColor: focused ? "salmon" : "deepskyblue" }}> |
74 | | - You can nest Focusables |
75 | | - </div> |
76 | | - )} |
77 | | - </Focusable> |
78 | | - <Focusable focusKey="menuItem2"> |
79 | | - {({ focused }) => ( |
80 | | - <div style={{ backgroundColor: focused ? "salmon" : "deepskyblue" }}> |
81 | | - In this case Sunbeam will try to find the best candidate for the focus within the common |
82 | | - Focusable parent first |
83 | | - </div> |
84 | | - )} |
85 | | - </Focusable> |
86 | | - </div> |
87 | | - </Focusable> |
88 | | - <Focusable focusKey="item2"> |
89 | | - {({ focused, path }) => ( |
90 | | - <div style={{ textDecoration: focused ? "underline" : "none" }} onClick={() => setFocus(path)}> |
91 | | - You can also programmatically change focus by using `setFocus` API |
92 | | - </div> |
93 | | - )} |
94 | | - </Focusable> |
95 | | - </div> |
96 | | - ) |
97 | | -} |
| 32 | +React Sunbeam can also be very useful in the regular web apps that want to add support for directional keyboard navigation and thus make this app more accessible to the keyboard-only users. |
| 33 | +Spreadsheets and tables, different kinds of dashboards etc, all can benefit from React Sunbeam. |
98 | 34 |
|
99 | | -const focusManager = new FocusManager({ |
100 | | - initialFocusPath: ["menuContainer", "menuItem2"], |
101 | | -}) |
| 35 | +## 🪄 Demo |
102 | 36 |
|
103 | | -render( |
104 | | - <Root focusManager={focusManager}> |
105 | | - <App /> |
106 | | - </Root>, |
107 | | - document.getElementById("app") |
108 | | -) |
| 37 | +Try our [demos](https://sunbeam.vova.codes/#demo-selector) on the documentation website to see what kind of interactions you can build with React Sunbeam. |
| 38 | + |
| 39 | +### [Home Screen](https://sunbeam.vova.codes/console-ui) |
| 40 | + |
| 41 | + |
| 42 | + |
| 43 | +### [Setting Menu](https://sunbeam.vova.codes/settings-menu) |
109 | 44 |
|
110 | | -// FocusableCard.js |
111 | | -import React, { useRef } from "react" |
| 45 | + |
| 46 | + |
| 47 | +## 🎬 Getting Started |
| 48 | + |
| 49 | +**Create a focusable component** |
| 50 | + |
| 51 | +```tsx |
112 | 52 | import { useFocusable } from "react-sunbeam" |
113 | 53 |
|
114 | | -export function FocusableCard({ focusKey }) { |
| 54 | +export function Button() { |
115 | 55 | const elementRef = useRef(null) |
116 | | - const { focused } = useFocusable({ focusKey, elementRef }) |
| 56 | + const { focused } = useFocusable({ elementRef }) |
117 | 57 |
|
118 | 58 | return ( |
119 | | - <div ref={elementRef} style={{ border: focused ? "1px solid salmon" : "1px solid transparent" }}> |
120 | | - Card |
121 | | - </div> |
| 59 | + <button |
| 60 | + ref={ref} |
| 61 | + style={{ |
| 62 | + border: focused ? "2px solid black" : "none", |
| 63 | + }} |
| 64 | + > |
| 65 | + Click me |
| 66 | + </button> |
122 | 67 | ) |
123 | 68 | } |
124 | 69 | ``` |
125 | 70 |
|
126 | | -## API |
| 71 | +**Start managing focus** |
| 72 | + |
| 73 | +```tsx |
| 74 | +import { FocusManager, Root } from "react-sunbeam" |
| 75 | + |
| 76 | +// 1. Create a `FocusManager`. |
| 77 | +const focusManager = new FocusManager() |
| 78 | + |
| 79 | +// 2. Assign its method calls to an event listener of your choice. |
| 80 | +// This can be a keyboard `keydown` event or your custom code using Gamepad API. |
| 81 | +window.addEventListener("keydown", (event) => { |
| 82 | + switch (event.key) { |
| 83 | + case "ArrowRight": |
| 84 | + event.preventDefault() |
| 85 | + focusManager.moveRight() |
| 86 | + return |
| 87 | + |
| 88 | + case "ArrowLeft": |
| 89 | + event.preventDefault() |
| 90 | + focusManager.moveLeft() |
| 91 | + return |
| 92 | + |
| 93 | + case "ArrowUp": |
| 94 | + event.preventDefault() |
| 95 | + focusManager.moveUp() |
| 96 | + return |
| 97 | + |
| 98 | + case "ArrowDown": |
| 99 | + event.preventDefault() |
| 100 | + focusManager.moveDown() |
| 101 | + return |
| 102 | + } |
| 103 | +}) |
| 104 | + |
| 105 | +// 3. Pass the `focusMananger` instance into the `Root` provider that wraps the rest of your app. |
| 106 | +ReactDOM.render( |
| 107 | + <Root focusManager={focusManager}> |
| 108 | + <App /> |
| 109 | + </Root>, |
| 110 | + rootElement |
| 111 | +) |
| 112 | +``` |
| 113 | + |
| 114 | +## 🍉 API |
127 | 115 |
|
128 | 116 | ### `FocusManager` |
129 | 117 |
|
|
0 commit comments