1
1
import * as React from "react"
2
- import { useCallback , useEffect , useState } from "react"
2
+ import { useCallback , useState } from "react"
3
3
import { render } from "react-dom"
4
4
import {
5
5
Direction ,
@@ -9,61 +9,78 @@ import {
9
9
FocusManager ,
10
10
SunbeamProvider ,
11
11
unstable_defaultGetPreferredChildOnFocusReceive ,
12
- useSunbeam ,
12
+ KeyPressManager ,
13
13
} from "react-sunbeam"
14
14
15
15
import { ProfilesMenu } from "./ProfilesMenu"
16
16
import { GamesGallery } from "./GamesGallery"
17
17
import { NavigationMenu } from "./NavigationMenu"
18
18
19
- export function App ( ) {
20
- const [ selectedItem , setSelectedItem ] = useState < string | null > ( null )
21
- const [ screen , setScreen ] = useState ( "home" )
22
-
23
- const { moveFocusLeft, moveFocusRight, moveFocusUp, moveFocusDown } = useSunbeam ( )
24
- const onKeyDown = useCallback (
25
- ( event : Event ) => {
26
- if ( ! ( event instanceof KeyboardEvent ) ) return
27
-
28
- switch ( event . key ) {
29
- case "ArrowRight" :
30
- event . preventDefault ( )
31
- moveFocusRight ( )
32
- return
33
-
34
- case "ArrowLeft" :
35
- event . preventDefault ( )
36
- moveFocusLeft ( )
37
- return
38
-
39
- case "ArrowUp" :
40
- event . preventDefault ( )
41
- moveFocusUp ( )
42
- return
43
-
44
- case "ArrowDown" :
45
- event . preventDefault ( )
46
- moveFocusDown ( )
47
- return
19
+ const focusManager = new FocusManager ( {
20
+ initialFocusPath : [ "gallery" , "1" ] ,
21
+ } )
22
+ const keyPressManager = new KeyPressManager ( )
23
+ keyPressManager . addListener ( event => {
24
+ switch ( event . key ) {
25
+ case "ArrowRight" :
26
+ event . preventDefault ( )
27
+ focusManager . moveRight ( )
28
+ return
29
+
30
+ case "ArrowLeft" :
31
+ event . preventDefault ( )
32
+ focusManager . moveLeft ( )
33
+ return
34
+
35
+ case "ArrowUp" :
36
+ event . preventDefault ( )
37
+ focusManager . moveUp ( )
38
+ return
39
+
40
+ case "ArrowDown" :
41
+ event . preventDefault ( )
42
+ focusManager . moveDown ( )
43
+ return
44
+ }
45
+ } )
48
46
49
- case " " :
50
- case "Enter" :
51
- event . preventDefault ( )
52
- if ( screen !== "detail" ) setScreen ( "detail" )
53
- return
54
- case "Backspace" :
55
- event . preventDefault ( )
56
- if ( screen !== "home" ) setScreen ( "home" )
57
- return
47
+ render (
48
+ < SunbeamProvider
49
+ focusManager = { focusManager }
50
+ keyPressManager = { keyPressManager }
51
+ onFocusUpdate = { handleFocusUpdate }
52
+ // unstable_passFocusBetweenChildren={({ focusableChildren, focusOrigin, direction }) => {
53
+ // if (direction === "LEFT" || direction === "RIGHT") {
54
+ // return "KEEP_FOCUS_UNCHANGED"
55
+ // }
56
+ //
57
+ // return unstable_defaultPassFocusBetweenChildren({focusableChildren, focusOrigin, direction})
58
+ // }}
59
+ unstable_getPreferredChildOnFocusReceive = { ( {
60
+ focusableChildren,
61
+ focusOrigin,
62
+ direction,
63
+ } : {
64
+ focusableChildren : Map < string , FocusableTreeNode >
65
+ focusOrigin ?: FocusableTreeNode
66
+ direction ?: Direction
67
+ } ) => {
68
+ if ( ! focusOrigin || ! direction ) {
69
+ // focus the gallery initially
70
+ if ( focusableChildren . has ( "gamesGallery" ) ) return focusableChildren . get ( "gamesGallery" )
58
71
}
59
- } ,
60
- [ focusManager , selectedItem , screen ]
61
- )
62
- useEffect ( ( ) => {
63
- document . addEventListener ( "keydown" , onKeyDown )
64
72
65
- return ( ) => document . removeEventListener ( "keydown" , onKeyDown )
66
- } , [ onKeyDown ] )
73
+ return unstable_defaultGetPreferredChildOnFocusReceive ( { focusableChildren, focusOrigin, direction } )
74
+ } }
75
+ >
76
+ < App />
77
+ </ SunbeamProvider > ,
78
+ document . getElementById ( "app" )
79
+ )
80
+
81
+ function App ( ) {
82
+ const [ selectedItem , setSelectedItem ] = useState < string | null > ( null )
83
+ const [ screen , setScreen ] = useState ( "home" )
67
84
68
85
const handleItemFocus = useCallback (
69
86
( event : FocusEvent ) => {
@@ -87,7 +104,15 @@ export function App() {
87
104
// TODO: implement Detail screen
88
105
return (
89
106
< div >
90
- < Focusable focusKey = "detail-focusable" style = { { display : "flex" } } >
107
+ < Focusable
108
+ focusKey = "detail-focusable"
109
+ style = { { display : "flex" } }
110
+ onKeyPress = { event => {
111
+ if ( event . key !== "Backspace" ) return
112
+ event . preventDefault ( )
113
+ setScreen ( "home" )
114
+ } }
115
+ >
91
116
{ ( { focused } ) => (
92
117
< div >
93
118
< h1 > Detail page for { selectedItem } </ h1 >
@@ -100,82 +125,56 @@ export function App() {
100
125
}
101
126
102
127
return (
103
- < div
104
- style = { {
105
- backgroundColor : "#2D2D2D" ,
106
- display : "flex" ,
107
- flexDirection : "column" ,
108
- height : "720px" ,
109
- width : "1280px" ,
110
- overflow : "hidden" ,
128
+ < Focusable
129
+ onKeyPress = { event => {
130
+ if ( event . key === " " || event . key === "Enter" ) {
131
+ event . preventDefault ( )
132
+ event . stopPropagation ( )
133
+ console . log ( 'Handling "Enter" key in Home Screen' )
134
+ setScreen ( "detail" )
135
+ }
111
136
} }
112
137
>
113
- < div style = { { marginTop : "32px" , marginLeft : "60px" } } >
114
- < ProfilesMenu
115
- onFocus = { handleContainerFocus }
116
- onBlur = { handleContainerBlur }
117
- onItemFocus = { handleItemFocus }
118
- onItemBlur = { handleItemBlur }
119
- />
120
- </ div >
121
- < div style = { { marginTop : "94px" , alignSelf : "center" } } >
122
- < GamesGallery
123
- onFocus = { handleContainerFocus }
124
- onBlur = { handleContainerBlur }
125
- onItemFocus = { handleItemFocus }
126
- onItemBlur = { handleItemBlur }
127
- />
128
- </ div >
129
- < div style = { { marginTop : "94px" , alignSelf : "center" } } >
130
- < NavigationMenu
131
- onFocus = { handleContainerFocus }
132
- onBlur = { handleContainerBlur }
133
- onItemFocus = { handleItemFocus }
134
- onItemBlur = { handleItemBlur }
135
- />
138
+ < div
139
+ style = { {
140
+ backgroundColor : "#2D2D2D" ,
141
+ display : "flex" ,
142
+ flexDirection : "column" ,
143
+ height : "720px" ,
144
+ width : "1280px" ,
145
+ overflow : "hidden" ,
146
+ } }
147
+ >
148
+ < div style = { { marginTop : "32px" , marginLeft : "60px" } } >
149
+ < ProfilesMenu
150
+ onFocus = { handleContainerFocus }
151
+ onBlur = { handleContainerBlur }
152
+ onItemFocus = { handleItemFocus }
153
+ onItemBlur = { handleItemBlur }
154
+ />
155
+ </ div >
156
+ < div style = { { marginTop : "94px" , alignSelf : "center" } } >
157
+ < GamesGallery
158
+ onFocus = { handleContainerFocus }
159
+ onBlur = { handleContainerBlur }
160
+ onItemFocus = { handleItemFocus }
161
+ onItemBlur = { handleItemBlur }
162
+ />
163
+ </ div >
164
+ < div style = { { marginTop : "94px" , alignSelf : "center" } } >
165
+ < NavigationMenu
166
+ onFocus = { handleContainerFocus }
167
+ onBlur = { handleContainerBlur }
168
+ onItemFocus = { handleItemFocus }
169
+ onItemBlur = { handleItemBlur }
170
+ />
171
+ </ div >
136
172
</ div >
137
- </ div >
173
+ </ Focusable >
138
174
)
139
175
}
140
176
141
- const focusManager = new FocusManager ( {
142
- initialFocusPath : [ "gallery" , "1" ] ,
143
- } )
144
-
145
- function handleFocusUpdate ( { focusPath } ) {
177
+ function handleFocusUpdate ( { focusPath } : { focusPath : readonly string [ ] } ) {
146
178
// e.g. report an analytics event
147
179
// console.log(`focus is updated, the new focusPath is: ${focusPath}`)
148
180
}
149
-
150
- render (
151
- < SunbeamProvider
152
- focusManager = { focusManager }
153
- onFocusUpdate = { handleFocusUpdate }
154
- // unstable_passFocusBetweenChildren={({ focusableChildren, focusOrigin, direction }) => {
155
- // if (direction === "LEFT" || direction === "RIGHT") {
156
- // return "KEEP_FOCUS_UNCHANGED"
157
- // }
158
- //
159
- // return unstable_defaultPassFocusBetweenChildren({focusableChildren, focusOrigin, direction})
160
- // }}
161
- unstable_getPreferredChildOnFocusReceive = { ( {
162
- focusableChildren,
163
- focusOrigin,
164
- direction,
165
- } : {
166
- focusableChildren : Map < string , FocusableTreeNode >
167
- focusOrigin ?: FocusableTreeNode
168
- direction ?: Direction
169
- } ) => {
170
- if ( ! focusOrigin || ! direction ) {
171
- // focus the gallery initially
172
- if ( focusableChildren . has ( "gamesGallery" ) ) return focusableChildren . get ( "gamesGallery" )
173
- }
174
-
175
- return unstable_defaultGetPreferredChildOnFocusReceive ( { focusableChildren, focusOrigin, direction } )
176
- } }
177
- >
178
- < App />
179
- </ SunbeamProvider > ,
180
- document . getElementById ( "app" )
181
- )
0 commit comments