1+ import * as React from "react" ;
2+ import { getActionShortcut , getActionShortcutsAsKeys , ShortcutNames } 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 = { [ ShortcutNames . MENU ] } />
13+ const cleanUpRow = < Row name = { lf ( "Workspace: Format code" ) } shortcuts = { [ ShortcutNames . CLEAN_UP ] } />
14+ const orAsJoiner = lf ( "or" )
15+ const enterOrSpace = { shortcuts : getActionShortcutsAsKeys ( ShortcutNames . EDIT_OR_CONFIRM ) , 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 = { [ ShortcutNames . LIST_SHORTCUTS ] } />
23+ < Row name = { lf ( "Jump to region" ) } shortcuts = { [ [ cmd , "B" ] ] } />
24+ < Row name = { lf ( "Block and toolbox navigation" ) } shortcuts = { [ ShortcutNames . UP , ShortcutNames . DOWN , ShortcutNames . LEFT , ShortcutNames . RIGHT ] } />
25+ < Row name = { lf ( "Toolbox or insert" ) } shortcuts = { [ ShortcutNames . TOOLBOX , ShortcutNames . INSERT ] } joiner = { orAsJoiner } />
26+ { editOrConfirmRow }
27+ < Row name = { lf ( "Move mode" ) } shortcuts = { [ ShortcutNames . MOVE ] } >
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 = { [ ShortcutNames . COPY , ShortcutNames . 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 , "B" ] ] } />
41+ < Row name = { lf ( "Exit" ) } shortcuts = { [ ShortcutNames . EXIT ] } />
42+ < Row name = { lf ( "Toolbox" ) } shortcuts = { [ ShortcutNames . TOOLBOX ] } />
43+ < Row name = { lf ( "Toolbox: Move in and out of categories" ) } shortcuts = { [ ShortcutNames . LEFT , ShortcutNames . RIGHT ] } />
44+ < Row name = { lf ( "Toolbox: Navigate categories or blocks" ) } shortcuts = { [ ShortcutNames . UP , ShortcutNames . DOWN ] } />
45+ < Row name = { lf ( "Toolbox: Insert block" ) } { ...enterOrSpace } />
46+ < Row name = { lf ( "Workspace: Select workspace" ) } shortcuts = { [ ShortcutNames . CREATE_WS_CURSOR ] } />
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 = { [ ShortcutNames . LEFT , ShortcutNames . RIGHT ] } />
54+ { editOrConfirmRow }
55+ < Row name = { lf ( "Cancel or exit" ) } shortcuts = { [ ShortcutNames . EXIT ] } />
56+ < Row name = { lf ( "Insert block at current position" ) } shortcuts = { [ ShortcutNames . INSERT ] } />
57+ < Row name = { lf ( "Copy" ) } shortcuts = { [ ShortcutNames . COPY ] } />
58+ < Row name = { lf ( "Paste" ) } shortcuts = { [ ShortcutNames . PASTE ] } />
59+ < Row name = { lf ( "Cut" ) } shortcuts = { [ ShortcutNames . CUT ] } />
60+ < Row name = { lf ( "Delete" ) } shortcuts = { getActionShortcutsAsKeys ( ShortcutNames . DELETE ) } joiner = { orAsJoiner } />
61+ < Row name = { lf ( "Undo" ) } shortcuts = { [ ShortcutNames . UNDO ] } />
62+ < Row name = { lf ( "Redo" ) } shortcuts = { [ ShortcutNames . REDO ] } />
63+ { contextMenuRow }
64+ </ tbody >
65+ </ table >
66+ < h3 > { lf ( "Moving Blocks" ) } </ h3 >
67+ < table >
68+ < tbody >
69+ < Row name = { lf ( "Move mode" ) } shortcuts = { [ ShortcutNames . MOVE ] } />
70+ < Row name = { lf ( "Move mode: Move to new position" ) } shortcuts = { [ ShortcutNames . UP , ShortcutNames . DOWN , ShortcutNames . LEFT , ShortcutNames . 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 = { [ ShortcutNames . EXIT ] } />
76+ < Row name = { lf ( "Disconnect blocks" ) } shortcuts = { [ ShortcutNames . 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 } > { value } </ span >
174+ }
175+
176+ export default KeyboardControlsHelp ;
0 commit comments