1
+ import * as React from "react" ;
2
+ import { getActionShortcut , SHORTCUT_NAMES } from "../shortcut_formatting" ;
3
+
4
+ const KeyboardControlsHelp = ( ) => {
5
+ const ref = React . useRef < HTMLElement > ( null ) ;
6
+ React . useEffect ( ( ) => {
7
+ ref . current ?. focus ( )
8
+ } , [ ] ) ;
9
+ const ctrl = lf ( "Ctrl" ) ;
10
+ const cmd = pxt . BrowserUtils . isMac ( ) ? "⌘" : ctrl ;
11
+ const optionOrCtrl = pxt . BrowserUtils . isMac ( ) ? "⌥" : ctrl ;
12
+ const contextMenuRow = < Row name = { lf ( "Open context menu" ) } shortcuts = { [ SHORTCUT_NAMES . MENU ] } />
13
+ const cleanUpRow = < Row name = { lf ( "Workspace: Format code" ) } shortcuts = { [ SHORTCUT_NAMES . CLEAN_UP ] } />
14
+ const orAsJoiner = lf ( "or" )
15
+ const enterOrSpace = { shortcuts : [ [ lf ( "Enter" ) ] , [ lf ( "Space" ) ] ] , joiner : orAsJoiner }
16
+ const editOrConfirmRow = < Row name = { lf ( "Edit or confirm" ) } { ...enterOrSpace } />
17
+ return (
18
+ < aside id = "keyboardnavhelp" aria-label = { lf ( "Keyboard Controls" ) } ref = { ref } tabIndex = { 0 } >
19
+ < h2 > { lf ( "Keyboard Controls" ) } </ h2 >
20
+ < table >
21
+ < tbody >
22
+ < Row name = { lf ( "Show/hide shortcut help" ) } shortcuts = { [ SHORTCUT_NAMES . LIST_SHORTCUTS ] } />
23
+ < Row name = { lf ( "Jump to region" ) } shortcuts = { [ [ cmd , "U" ] ] } />
24
+ < Row name = { lf ( "Block and toolbox navigation" ) } shortcuts = { [ SHORTCUT_NAMES . UP , SHORTCUT_NAMES . DOWN , SHORTCUT_NAMES . LEFT , SHORTCUT_NAMES . RIGHT ] } />
25
+ < Row name = { lf ( "Toolbox or insert" ) } shortcuts = { [ SHORTCUT_NAMES . TOOLBOX , SHORTCUT_NAMES . INSERT ] } joiner = { orAsJoiner } />
26
+ { editOrConfirmRow }
27
+ < Row name = { lf ( "Move mode" ) } shortcuts = { [ [ "M" ] ] } >
28
+ < br /> < span className = "hint" > { lf ( "Move with arrow keys" ) } </ span >
29
+ < br /> < span className = "hint" > { lf ( "Hold {0} for free movement" , optionOrCtrl ) } </ span >
30
+ </ Row >
31
+ < Row name = { lf ( "Copy / paste" ) } shortcuts = { [ SHORTCUT_NAMES . COPY , SHORTCUT_NAMES . PASTE ] } joiner = "/" />
32
+ { cleanUpRow }
33
+ { contextMenuRow }
34
+ </ tbody >
35
+ </ table >
36
+ < h3 > { lf ( "Editor Overview" ) } </ h3 >
37
+ < table >
38
+ < tbody >
39
+ < Row name = { lf ( "Move between menus, simulator and the workspace" ) } shortcuts = { [ [ lf ( "Tab" ) ] , [ lf ( "Shift" ) , lf ( "Tab" ) ] ] } joiner = "row" />
40
+ < Row name = { lf ( "Jump to region" ) } shortcuts = { [ [ cmd , "U" ] ] } />
41
+ < Row name = { lf ( "Exit" ) } shortcuts = { [ SHORTCUT_NAMES . EXIT ] } />
42
+ < Row name = { lf ( "Toolbox" ) } shortcuts = { [ SHORTCUT_NAMES . TOOLBOX ] } />
43
+ < Row name = { lf ( "Toolbox: Move in and out of categories" ) } shortcuts = { [ SHORTCUT_NAMES . LEFT , SHORTCUT_NAMES . RIGHT ] } />
44
+ < Row name = { lf ( "Toolbox: Navigate categories or blocks" ) } shortcuts = { [ SHORTCUT_NAMES . UP , SHORTCUT_NAMES . DOWN ] } />
45
+ < Row name = { lf ( "Toolbox: Insert block" ) } { ...enterOrSpace } />
46
+ < Row name = { lf ( "Workspace: Select workspace" ) } shortcuts = { [ [ "W" ] ] } />
47
+ { cleanUpRow }
48
+ </ tbody >
49
+ </ table >
50
+ < h3 > { lf ( "Edit Blocks" ) } </ h3 >
51
+ < table >
52
+ < tbody >
53
+ < Row name = { lf ( "Move in and out of a block" ) } shortcuts = { [ SHORTCUT_NAMES . LEFT , SHORTCUT_NAMES . RIGHT ] } />
54
+ { editOrConfirmRow }
55
+ < Row name = { lf ( "Cancel or exit" ) } shortcuts = { [ SHORTCUT_NAMES . EXIT ] } />
56
+ < Row name = { lf ( "Insert block at current position" ) } shortcuts = { [ SHORTCUT_NAMES . INSERT ] } />
57
+ < Row name = { lf ( "Copy" ) } shortcuts = { [ SHORTCUT_NAMES . COPY ] } />
58
+ < Row name = { lf ( "Paste" ) } shortcuts = { [ SHORTCUT_NAMES . PASTE ] } />
59
+ < Row name = { lf ( "Cut" ) } shortcuts = { [ SHORTCUT_NAMES . CUT ] } />
60
+ < Row name = { lf ( "Delete" ) } shortcuts = { [ SHORTCUT_NAMES . DELETE , [ lf ( "Backspace" ) ] ] } joiner = { orAsJoiner } />
61
+ < Row name = { lf ( "Undo" ) } shortcuts = { [ SHORTCUT_NAMES . UNDO ] } />
62
+ < Row name = { lf ( "Redo" ) } shortcuts = { [ SHORTCUT_NAMES . REDO ] } />
63
+ { contextMenuRow }
64
+ </ tbody >
65
+ </ table >
66
+ < h3 > { lf ( "Moving Blocks" ) } </ h3 >
67
+ < table >
68
+ < tbody >
69
+ < Row name = { lf ( "Move mode" ) } shortcuts = { [ [ "M" ] ] } />
70
+ < Row name = { lf ( "Move mode: Move to new position" ) } shortcuts = { [ SHORTCUT_NAMES . UP , SHORTCUT_NAMES . DOWN , SHORTCUT_NAMES . LEFT , SHORTCUT_NAMES . RIGHT ] } />
71
+ < Row name = { lf ( "Move mode: Free movement" ) } >
72
+ { lf ( "Hold {0} and press arrow keys" , optionOrCtrl ) }
73
+ </ Row >
74
+ < Row name = { lf ( "Move mode: Confirm" ) } { ...enterOrSpace } />
75
+ < Row name = { lf ( "Move mode: Cancel" ) } shortcuts = { [ SHORTCUT_NAMES . EXIT ] } />
76
+ < Row name = { lf ( "Disconnect blocks" ) } shortcuts = { [ SHORTCUT_NAMES . DISCONNECT ] } />
77
+ </ tbody >
78
+ </ table >
79
+ </ aside >
80
+ ) ;
81
+ }
82
+
83
+ const Shortcut = ( { keys } : { keys : string [ ] } ) => {
84
+ const joiner = pxt . BrowserUtils . isMac ( ) ? " " : " + "
85
+ return (
86
+ < span className = "shortcut" >
87
+ { keys . reduce ( ( acc , key ) => {
88
+ return acc . length === 0
89
+ ? [ ...acc , < Key key = { key } value = { key } /> ]
90
+ : [ ...acc , joiner , < Key key = { key } value = { key } /> ]
91
+ } , [ ] ) }
92
+ </ span >
93
+ ) ;
94
+ }
95
+
96
+ interface RowProps {
97
+ name : string ;
98
+ shortcuts ?: Array < string | string [ ] > ;
99
+ joiner ?: string ;
100
+ children ?: React . ReactNode ;
101
+ }
102
+
103
+ const Row = ( { name, shortcuts = [ ] , joiner, children} : RowProps ) => {
104
+ const shortcutElements = shortcuts . map ( ( s , idx ) => {
105
+ if ( typeof s === "string" ) {
106
+ // Pull keys from shortcut registry.
107
+ return < Shortcut key = { idx } keys = { getActionShortcut ( s ) } />
108
+ } else {
109
+ // Display keys as specified.
110
+ return < Shortcut key = { idx } keys = { s } />
111
+ }
112
+ } )
113
+ return joiner === "row" ? (
114
+ < >
115
+ < tr >
116
+ < td width = "50%" rowSpan = { shortcuts . length } > { name } </ td >
117
+ < td width = "50%" >
118
+ { shortcutElements [ 0 ] }
119
+ </ td >
120
+ </ tr >
121
+ { shortcutElements . map ( ( el , idx ) => idx === 0
122
+ ? undefined
123
+ : ( < tr key = { idx } >
124
+ < td width = "50%" >
125
+ { el }
126
+ </ td >
127
+ </ tr > ) ) }
128
+ </ >
129
+ ) : (
130
+ < tr >
131
+ < td width = "50%" > { name } </ td >
132
+ < td width = "50%" >
133
+ { shortcutElements . reduce ( ( acc , shortcut ) => {
134
+ return acc . length === 0
135
+ ? [ ...acc , shortcut ]
136
+ : [ ...acc , joiner ? ` ${ joiner } ` : " " , shortcut ]
137
+ } , [ ] ) }
138
+ { children }
139
+ < br />
140
+ </ td >
141
+ </ tr >
142
+ )
143
+ }
144
+
145
+ const Key = ( { value } : { value : string } ) => {
146
+ let aria ;
147
+ switch ( value ) {
148
+ case "↑" : {
149
+ aria = lf ( "Up Arrow" ) ;
150
+ break ;
151
+ }
152
+ case "↓" : {
153
+ aria = lf ( "Down Arrow" ) ;
154
+ break ;
155
+ }
156
+ case "←" : {
157
+ aria = lf ( "Left Arrow" ) ;
158
+ break ;
159
+ }
160
+ case "→" : {
161
+ aria = lf ( "Right Arrow" ) ;
162
+ break ;
163
+ }
164
+ case "⌘" : {
165
+ aria = lf ( "Command" ) ;
166
+ break ;
167
+ }
168
+ case "⌥" : {
169
+ aria = lf ( "Option" ) ;
170
+ break ;
171
+ }
172
+ }
173
+ return < span className = "key" aria-label = { aria } > { lf ( "{0}" , value ) } </ span >
174
+ }
175
+
176
+ export default KeyboardControlsHelp ;
0 commit comments