44import * as React from "react" ;
55import { VIEW_STATE } from "../../constants" ;
66import { ViewStateContext } from "../../context" ;
7+ import CONSTANTS , { MICROBIT_BUTTON_STYLING_CLASSES } from "../../constants" ;
78import "../../styles/Microbit.css" ;
89import { IRefObject , MicrobitSvg } from "./Microbit_svg" ;
910
1011interface EventTriggers {
1112 onMouseUp : ( event : Event , buttonKey : string ) => void ;
1213 onMouseDown : ( event : Event , buttonKey : string ) => void ;
1314 onMouseLeave : ( event : Event , buttonKey : string ) => void ;
15+ onKeyEvent : ( event : KeyboardEvent , active : boolean , key : string ) => void ;
1416}
1517interface IProps {
1618 eventTriggers : EventTriggers ;
@@ -22,6 +24,11 @@ const BUTTON_CLASSNAME = {
2224 DEACTIVATED : "sim-button-deactivated" ,
2325} ;
2426
27+ export enum BUTTONS_KEYS {
28+ BTN_A = "BTN_A" ,
29+ BTN_B = "BTN_B" ,
30+ BTN_AB = "BTN_AB" ,
31+ }
2532// Displays the SVG and call necessary svg modification.
2633export class MicrobitImage extends React . Component < IProps , { } > {
2734 private svgRef : React . RefObject < MicrobitSvg > = React . createRef ( ) ;
@@ -33,6 +40,7 @@ export class MicrobitImage extends React.Component<IProps, {}> {
3340 if ( svgElement ) {
3441 updateAllLeds ( this . props . leds , svgElement . getLeds ( ) ) ;
3542 setupAllButtons ( this . props . eventTriggers , svgElement . getButtons ( ) ) ;
43+ this . setupKeyPresses ( this . props . eventTriggers . onKeyEvent ) ;
3644 }
3745 }
3846 componentDidUpdate ( ) {
@@ -48,9 +56,56 @@ export class MicrobitImage extends React.Component<IProps, {}> {
4856 }
4957 }
5058 }
59+ componentWillUnmount ( ) {
60+ window . document . removeEventListener ( "keydown" , this . handleKeyDown ) ;
61+ window . document . removeEventListener ( "keyup" , this . handleKeyUp ) ;
62+ }
63+ setupKeyPresses = (
64+ onKeyEvent : ( event : KeyboardEvent , active : boolean , key : string ) => void
65+ ) => {
66+ window . document . addEventListener ( "keydown" , this . handleKeyDown ) ;
67+ window . document . addEventListener ( "keyup" , this . handleKeyUp ) ;
68+ } ;
69+ handleKeyDown = ( event : KeyboardEvent ) => {
70+ const keyEvents = [ event . key , event . code ] ;
71+ // Don't listen to keydown events for the run button, restart button and enter key
72+ if (
73+ ! (
74+ keyEvents . includes ( CONSTANTS . KEYBOARD_KEYS . CAPITAL_F ) ||
75+ keyEvents . includes ( CONSTANTS . KEYBOARD_KEYS . CAPITAL_R ) ||
76+ keyEvents . includes ( CONSTANTS . KEYBOARD_KEYS . ENTER )
77+ )
78+ ) {
79+ this . props . eventTriggers . onKeyEvent ( event , true , event . key ) ;
80+ }
81+ } ;
82+ handleKeyUp = ( event : KeyboardEvent ) => {
83+ this . props . eventTriggers . onKeyEvent ( event , false , event . key ) ;
84+ } ;
5185 render ( ) {
5286 return < MicrobitSvg ref = { this . svgRef } /> ;
5387 }
88+ public updateButtonAttributes ( key : BUTTONS_KEYS , isActive : boolean ) {
89+ if ( this . svgRef . current ) {
90+ const button = this . svgRef . current . getButtons ( ) [ key ] . current ;
91+ if ( button ) {
92+ button . focus ( ) ;
93+ if ( isActive ) {
94+ button . children [ 0 ] . setAttribute (
95+ "class" ,
96+ MICROBIT_BUTTON_STYLING_CLASSES . KEYPRESSED
97+ ) ;
98+ } else {
99+ button . children [ 0 ] . setAttribute (
100+ "class" ,
101+ MICROBIT_BUTTON_STYLING_CLASSES . DEFAULT
102+ ) ;
103+ }
104+ button . setAttribute ( "pressed" , `${ isActive } ` ) ;
105+ button . setAttribute ( "aria-pressed" , `${ isActive } ` ) ;
106+ }
107+ }
108+ }
54109}
55110
56111MicrobitImage . contextType = ViewStateContext ;
@@ -59,8 +114,8 @@ const setupButton = (
59114 eventTriggers : EventTriggers ,
60115 key : string
61116) => {
62- buttonElement . setAttribute ( "class" , BUTTON_CLASSNAME . ACTIVE ) ;
63117 buttonElement . onmousedown = e => {
118+ buttonElement . focus ( ) ;
64119 eventTriggers . onMouseDown ( e , key ) ;
65120 } ;
66121 buttonElement . onmouseup = e => {
@@ -69,6 +124,16 @@ const setupButton = (
69124 buttonElement . onmouseleave = e => {
70125 eventTriggers . onMouseLeave ( e , key ) ;
71126 } ;
127+ buttonElement . onkeydown = e => {
128+ // ensure that the keydown is enter,
129+ // or else it may register shortcuts twice
130+ if ( e . key === CONSTANTS . KEYBOARD_KEYS . ENTER ) {
131+ eventTriggers . onKeyEvent ( e , true , key ) ;
132+ }
133+ } ;
134+ buttonElement . onkeyup = e => {
135+ eventTriggers . onKeyEvent ( e , false , key ) ;
136+ } ;
72137} ;
73138const setupAllButtons = (
74139 eventTriggers : EventTriggers ,
@@ -87,6 +152,8 @@ const disableAllButtons = (buttonRefs: IRefObject) => {
87152 ref . current . onmousedown = null ;
88153 ref . current . onmouseup = null ;
89154 ref . current . onmouseleave = null ;
155+ ref . current . onkeydown = null ;
156+ ref . current . onkeyup = null ;
90157 ref . current . setAttribute ( "class" , BUTTON_CLASSNAME . DEACTIVATED ) ;
91158 }
92159 }
0 commit comments