diff --git a/package-lock.json b/package-lock.json index e2b169c9b..777c7b6d1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -30807,9 +30807,9 @@ } }, "typescript": { - "version": "3.4.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.4.5.tgz", - "integrity": "sha512-YycBxUb49UUhdNMU5aJ7z5Ej2XGmaIBL0x34vZ82fn3hGvD+bgrMrVDpatgz2f7YxUMJxMkbWxJZeAvDxVe7Vw==", + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.8.3.tgz", + "integrity": "sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w==", "dev": true }, "typescript-react-intl": { diff --git a/package.json b/package.json index cc1b237bd..9d3b633ec 100644 --- a/package.json +++ b/package.json @@ -149,7 +149,7 @@ "type": "boolean", "default": false, "description": "%deviceSimulatorExpressExtension.configuration.properties.previewMode%", - "scope": "resource" + "scope": "resource" } } }, @@ -286,7 +286,7 @@ "tslint-microsoft-contrib": "^6.1.0", "tslint-react": "^3.6.0", "tslint-react-hooks": "^2.0.0", - "typescript": "^3.3.1", + "typescript": "^3.8.3", "typescript-react-intl": "^0.4.0", "version-from-git": "^1.1.1", "vsce": "^1.47.0", diff --git a/src/extension.ts b/src/extension.ts index 2baf389a5..b36de1f12 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -599,7 +599,7 @@ export async function activate(context: vscode.ExtensionContext) { // base_64 strings on UNIX systems. // added any incomplete data to beginning - let processedData = pythonProcessDataBuffer + const processedData = pythonProcessDataBuffer .join("") .concat(dataFromTheProcess); pythonProcessDataBuffer = []; diff --git a/src/view/components/clue/ClueImage.tsx b/src/view/components/clue/ClueImage.tsx index 0ad426c96..85694d4e0 100644 --- a/src/view/components/clue/ClueImage.tsx +++ b/src/view/components/clue/ClueImage.tsx @@ -10,7 +10,6 @@ import { ClueSvg, IRefObject } from "./Clue_svg"; interface EventTriggers { onMouseUp: (event: Event, buttonKey: string) => void; onMouseDown: (event: Event, buttonKey: string) => void; - onMouseLeave: (event: Event, buttonKey: string) => void; onKeyEvent: (event: KeyboardEvent, active: boolean, key: string) => void; } interface IProps { @@ -42,15 +41,13 @@ export class ClueImage extends React.Component { } } componentDidUpdate() { - if (this.svgRef.current) { - if (this.context === VIEW_STATE.PAUSE) { - disableAllButtons(this.svgRef.current.getButtons()); - } else if (this.context === VIEW_STATE.RUNNING) { - setupAllButtons( - this.props.eventTriggers, - this.svgRef.current.getButtons() - ); - } + if (this.context === VIEW_STATE.PAUSE && this.svgRef.current) { + disableAllButtons(this.svgRef.current.getButtons()); + } else if (this.context === VIEW_STATE.RUNNING && this.svgRef.current) { + setupAllButtons( + this.props.eventTriggers, + this.svgRef.current.getButtons() + ); } } componentWillUnmount() { @@ -89,24 +86,22 @@ export class ClueImage extends React.Component { ); } public updateButtonAttributes(key: BUTTONS_KEYS, isActive: boolean) { - if (this.svgRef.current) { - const button = this.svgRef.current.getButtons()[key].current; - if (button) { - button.focus(); - if (isActive) { - button.children[0].setAttribute( - "class", - BUTTON_STYLING_CLASSES.KEYPRESSED - ); - } else { - button.children[0].setAttribute( - "class", - BUTTON_STYLING_CLASSES.DEFAULT - ); - } - button.setAttribute("pressed", `${isActive}`); - button.setAttribute("aria-pressed", `${isActive}`); + const button = this.svgRef.current?.getButtons()[key].current; + if (button) { + button.focus(); + if (isActive) { + button.children[0].setAttribute( + "class", + BUTTON_STYLING_CLASSES.KEYPRESSED + ); + } else { + button.children[0].setAttribute( + "class", + BUTTON_STYLING_CLASSES.DEFAULT + ); } + button.setAttribute("pressed", `${isActive}`); + button.setAttribute("aria-pressed", `${isActive}`); } } } @@ -125,9 +120,7 @@ const setupButton = ( buttonElement.onmouseup = e => { eventTriggers.onMouseUp(e, key); }; - buttonElement.onmouseleave = e => { - eventTriggers.onMouseLeave(e, key); - }; + buttonElement.onkeydown = e => { // ensure that the keydown is enter, // or else it may register shortcuts twice @@ -155,7 +148,6 @@ const disableAllButtons = (buttonRefs: IRefObject) => { // to implement ref.current.onmousedown = null; ref.current.onmouseup = null; - ref.current.onmouseleave = null; ref.current.onkeydown = null; ref.current.onkeyup = null; ref.current.setAttribute("class", BUTTON_CLASSNAME.DEACTIVATED); diff --git a/src/view/components/clue/ClueSimulator.tsx b/src/view/components/clue/ClueSimulator.tsx index f0225d2e9..a15f101f8 100644 --- a/src/view/components/clue/ClueSimulator.tsx +++ b/src/view/components/clue/ClueSimulator.tsx @@ -7,14 +7,14 @@ import { VIEW_STATE, WEBVIEW_MESSAGES, } from "../../constants"; +import { ViewStateContext } from "../../context"; +import "../../styles/Simulator.css"; import "../../styles/Simulator.css"; import PlayLogo from "../../svgs/play_svg"; import StopLogo from "../../svgs/stop_svg"; import { sendMessage } from "../../utils/MessageUtils"; import ActionBar from "../simulator/ActionBar"; import { BUTTONS_KEYS, ClueImage } from "./ClueImage"; -import "../../styles/Simulator.css"; -import { ViewStateContext } from "../../context"; export const DEFAULT_CLUE_STATE: IClueState = { buttons: { button_a: false, button_b: false }, @@ -128,7 +128,6 @@ export class ClueSimulator extends React.Component { eventTriggers={{ onMouseDown: this.onMouseDown, onMouseUp: this.onMouseUp, - onMouseLeave: this.onMouseLeave, onKeyEvent: this.onKeyEvent, }} displayMessage={this.state.clue.displayMessage} @@ -194,18 +193,17 @@ export class ClueSimulator extends React.Component { }, }); }; + protected onMouseUp = (event: Event, key: string) => { event.preventDefault(); this.handleButtonClick(key, false); }; + protected onMouseDown = (event: Event, key: string) => { event.preventDefault(); this.handleButtonClick(key, true); }; - protected onMouseLeave = (event: Event, key: string) => { - event.preventDefault(); - console.log(`To implement onMouseLeave ${key}`); - }; + protected onKeyEvent(event: KeyboardEvent, active: boolean, key: string) { event.stopPropagation(); if ( @@ -213,57 +211,49 @@ export class ClueSimulator extends React.Component { this.context === VIEW_STATE.RUNNING ) { this.handleButtonClick(key, active); - if (this.imageRef.current) { - if (key === BUTTONS_KEYS.BTN_A) { - this.imageRef.current.updateButtonAttributes( - BUTTONS_KEYS.BTN_A, - active - ); - } else if (key === BUTTONS_KEYS.BTN_B) { - this.imageRef.current.updateButtonAttributes( - BUTTONS_KEYS.BTN_B, - active - ); - } else if (key === BUTTONS_KEYS.BTN_AB) { - this.imageRef.current.updateButtonAttributes( - BUTTONS_KEYS.BTN_AB, - active - ); - } + if (key === BUTTONS_KEYS.BTN_A) { + this.imageRef.current?.updateButtonAttributes( + BUTTONS_KEYS.BTN_A, + active + ); + } else if (key === BUTTONS_KEYS.BTN_B) { + this.imageRef.current?.updateButtonAttributes( + BUTTONS_KEYS.BTN_B, + active + ); + } else if (key === BUTTONS_KEYS.BTN_AB) { + this.imageRef.current?.updateButtonAttributes( + BUTTONS_KEYS.BTN_AB, + active + ); } } else if ( [event.code, event.key].includes(CONSTANTS.KEYBOARD_KEYS.A) && this.context === VIEW_STATE.RUNNING ) { this.handleButtonClick(BUTTONS_KEYS.BTN_A, active); - if (this.imageRef.current) { - this.imageRef.current.updateButtonAttributes( - BUTTONS_KEYS.BTN_A, - active - ); - } + this.imageRef.current?.updateButtonAttributes( + BUTTONS_KEYS.BTN_A, + active + ); } else if ( [event.code, event.key].includes(CONSTANTS.KEYBOARD_KEYS.B) && this.context === VIEW_STATE.RUNNING ) { this.handleButtonClick(BUTTONS_KEYS.BTN_B, active); - if (this.imageRef.current) { - this.imageRef.current.updateButtonAttributes( - BUTTONS_KEYS.BTN_B, - active - ); - } + this.imageRef.current?.updateButtonAttributes( + BUTTONS_KEYS.BTN_B, + active + ); } else if ( [event.code, event.key].includes(CONSTANTS.KEYBOARD_KEYS.C) && this.context === VIEW_STATE.RUNNING ) { this.handleButtonClick(BUTTONS_KEYS.BTN_AB, active); - if (this.imageRef.current) { - this.imageRef.current.updateButtonAttributes( - BUTTONS_KEYS.BTN_AB, - active - ); - } + this.imageRef.current?.updateButtonAttributes( + BUTTONS_KEYS.BTN_AB, + active + ); } else if (event.key === CONSTANTS.KEYBOARD_KEYS.CAPITAL_F) { this.togglePlayClick(); } else if (event.key === CONSTANTS.KEYBOARD_KEYS.CAPITAL_R) { diff --git a/src/view/components/clue/Clue_svg.tsx b/src/view/components/clue/Clue_svg.tsx index f5bfe5b92..bcbf4e6d2 100644 --- a/src/view/components/clue/Clue_svg.tsx +++ b/src/view/components/clue/Clue_svg.tsx @@ -2,10 +2,10 @@ // Licensed under the MIT license. import * as React from "react"; +import { CLUE_LEDS_COLORS, CONSTANTS } from "../../constants"; import "../../styles/SimulatorSvg.css"; -import { DEFAULT_CLUE_STATE } from "./ClueSimulator"; -import { CONSTANTS, CLUE_LEDS_COLORS } from "../../constants"; import svg from "../cpx/Svg_utils"; +import { DEFAULT_CLUE_STATE } from "./ClueSimulator"; export interface IRefObject { [key: string]: React.RefObject; } @@ -1124,8 +1124,8 @@ export class ClueSvg extends React.Component { } private updateDisplay() { - if (this.displayRef.current && this.props.displayImage) { - this.displayRef.current.setAttribute( + if (this.props.displayImage) { + this.displayRef.current?.setAttribute( "href", `data:image/png;base64,${this.props.displayImage}` ); @@ -1140,26 +1140,23 @@ export class ClueSvg extends React.Component { (255 - neopixel[1]) * CONSTANTS.LED_TINT_FACTOR},${neopixel[2] + (255 - neopixel[2]) * CONSTANTS.LED_TINT_FACTOR})`; - if (this.ledsRefs.neopixel.current) { - this.ledsRefs.neopixel.current.setAttribute("fill", rgbColor); - } - if (this.gradientRefs.neopixel.current) { - if (neopixel === DEFAULT_CLUE_STATE.leds.neopixel) { - this.gradientRefs.neopixel.current.setAttribute( - "stop-opacity", - "0" - ); - } else { - this.gradientRefs.neopixel.current.setAttribute( - "stop-opacity", - "1" - ); - } - this.gradientRefs.neopixel.current.setAttribute( - "stop-color", - rgbColor + this.ledsRefs.neopixel.current?.setAttribute("fill", rgbColor); + + if (neopixel === DEFAULT_CLUE_STATE.leds.neopixel) { + this.gradientRefs.neopixel.current?.setAttribute( + "stop-opacity", + "0" + ); + } else { + this.gradientRefs.neopixel.current?.setAttribute( + "stop-opacity", + "1" ); } + this.gradientRefs.neopixel.current?.setAttribute( + "stop-color", + rgbColor + ); } private updateLeds() { // update white led @@ -1167,25 +1164,21 @@ export class ClueSvg extends React.Component { this.ledsRefs.whiteLeds.map( (ledRef: React.RefObject) => { - if (ledRef.current && this.gradientRefs.whiteLed.current) { - svg.setLed( - isWhiteLedOn, - CLUE_LEDS_COLORS.WHITE_LEDS_OFF, - CLUE_LEDS_COLORS.WHITE_LEDS_ON, - ledRef.current, - this.gradientRefs.whiteLed.current - ); - } + svg.setLed( + isWhiteLedOn, + CLUE_LEDS_COLORS.WHITE_LEDS_OFF, + CLUE_LEDS_COLORS.WHITE_LEDS_ON, + ledRef.current, + this.gradientRefs.whiteLed.current + ); } ); - if (this.ledsRefs.redLed.current && this.gradientRefs.redLed.current) { - svg.setLed( - isRedLedOn, - CLUE_LEDS_COLORS.RED_LED_OFF, - CLUE_LEDS_COLORS.RED_LED_ON, - this.ledsRefs.redLed.current, - this.gradientRefs.redLed.current - ); - } + svg.setLed( + isRedLedOn, + CLUE_LEDS_COLORS.RED_LED_OFF, + CLUE_LEDS_COLORS.RED_LED_ON, + this.ledsRefs.redLed.current, + this.gradientRefs.redLed.current + ); } } diff --git a/src/view/components/cpx/Svg_utils.tsx b/src/view/components/cpx/Svg_utils.tsx index 29dab77de..92836b923 100644 --- a/src/view/components/cpx/Svg_utils.tsx +++ b/src/view/components/cpx/Svg_utils.tsx @@ -82,15 +82,15 @@ namespace svg { ledStatus: boolean, offColor: string, onColor: string, - ledElement: SVGElement, - gradientStopElement: SVGStopElement + ledElement: SVGElement | null, + gradientStopElement: SVGStopElement | null ) { if (ledStatus) { - ledElement.setAttribute("fill", onColor); - gradientStopElement.setAttribute("stop-opacity", "1"); + ledElement?.setAttribute("fill", onColor); + gradientStopElement?.setAttribute("stop-opacity", "1"); } else { - ledElement.setAttribute("fill", offColor); - gradientStopElement.setAttribute("stop-opacity", "0"); + ledElement?.setAttribute("fill", offColor); + gradientStopElement?.setAttribute("stop-opacity", "0"); } } } diff --git a/src/view/components/microbit/MicrobitImage.tsx b/src/view/components/microbit/MicrobitImage.tsx index c471ecb56..a216dceef 100644 --- a/src/view/components/microbit/MicrobitImage.tsx +++ b/src/view/components/microbit/MicrobitImage.tsx @@ -10,7 +10,6 @@ import { IRefObject, MicrobitSvg } from "./Microbit_svg"; interface EventTriggers { onMouseUp: (event: Event, buttonKey: string) => void; onMouseDown: (event: Event, buttonKey: string) => void; - onMouseLeave: (event: Event, buttonKey: string) => void; onKeyEvent: (event: KeyboardEvent, active: boolean, key: string) => void; } interface IProps { @@ -80,24 +79,22 @@ export class MicrobitImage extends React.Component { return ; } public updateButtonAttributes(key: BUTTONS_KEYS, isActive: boolean) { - if (this.svgRef.current) { - const button = this.svgRef.current.getButtons()[key].current; - if (button) { - button.focus(); - if (isActive) { - button.children[0].setAttribute( - "class", - BUTTON_STYLING_CLASSES.KEYPRESSED - ); - } else { - button.children[0].setAttribute( - "class", - BUTTON_STYLING_CLASSES.DEFAULT - ); - } - button.setAttribute("pressed", `${isActive}`); - button.setAttribute("aria-pressed", `${isActive}`); + const button = this.svgRef.current?.getButtons()[key].current; + if (button) { + button.focus(); + if (isActive) { + button.children[0].setAttribute( + "class", + BUTTON_STYLING_CLASSES.KEYPRESSED + ); + } else { + button.children[0].setAttribute( + "class", + BUTTON_STYLING_CLASSES.DEFAULT + ); } + button.setAttribute("pressed", `${isActive}`); + button.setAttribute("aria-pressed", `${isActive}`); } } } @@ -117,9 +114,7 @@ const setupButton = ( buttonElement.onmouseup = e => { eventTriggers.onMouseUp(e, key); }; - buttonElement.onmouseleave = e => { - eventTriggers.onMouseLeave(e, key); - }; + buttonElement.onkeydown = e => { // ensure that the keydown is enter, // or else it may register shortcuts twice @@ -147,7 +142,6 @@ const disableAllButtons = (buttonRefs: IRefObject) => { // to implement ref.current.onmousedown = null; ref.current.onmouseup = null; - ref.current.onmouseleave = null; ref.current.onkeydown = null; ref.current.onkeyup = null; ref.current.setAttribute("class", BUTTON_CLASSNAME.DEACTIVATED); diff --git a/src/view/components/microbit/MicrobitSimulator.tsx b/src/view/components/microbit/MicrobitSimulator.tsx index ff44ac7b2..d3507710c 100644 --- a/src/view/components/microbit/MicrobitSimulator.tsx +++ b/src/view/components/microbit/MicrobitSimulator.tsx @@ -124,7 +124,6 @@ export class MicrobitSimulator extends React.Component { eventTriggers={{ onMouseDown: this.onMouseDown, onMouseUp: this.onMouseUp, - onMouseLeave: this.onMouseLeave, onKeyEvent: this.onKeyEvent, }} leds={this.state.microbit.leds} @@ -186,18 +185,17 @@ export class MicrobitSimulator extends React.Component { }, }); }; + protected onMouseUp = (event: Event, key: string) => { event.preventDefault(); this.handleButtonClick(key, false); }; + protected onMouseDown = (event: Event, key: string) => { event.preventDefault(); this.handleButtonClick(key, true); }; - protected onMouseLeave = (event: Event, key: string) => { - event.preventDefault(); - console.log(`To implement onMouseLeave ${key}`); - }; + protected onKeyEvent(event: KeyboardEvent, active: boolean, key: string) { event.stopPropagation(); if ( @@ -205,57 +203,52 @@ export class MicrobitSimulator extends React.Component { this.context === VIEW_STATE.RUNNING ) { this.handleButtonClick(key, active); - if (this.imageRef.current) { - if (key === BUTTONS_KEYS.BTN_A) { - this.imageRef.current.updateButtonAttributes( - BUTTONS_KEYS.BTN_A, - active - ); - } else if (key === BUTTONS_KEYS.BTN_B) { - this.imageRef.current.updateButtonAttributes( - BUTTONS_KEYS.BTN_B, - active - ); - } else if (key === BUTTONS_KEYS.BTN_AB) { - this.imageRef.current.updateButtonAttributes( - BUTTONS_KEYS.BTN_AB, - active - ); - } + if (key === BUTTONS_KEYS.BTN_A) { + this.imageRef.current?.updateButtonAttributes( + BUTTONS_KEYS.BTN_A, + active + ); + } else if (key === BUTTONS_KEYS.BTN_B) { + this.imageRef.current?.updateButtonAttributes( + BUTTONS_KEYS.BTN_B, + active + ); + } else if (key === BUTTONS_KEYS.BTN_AB) { + this.imageRef.current?.updateButtonAttributes( + BUTTONS_KEYS.BTN_AB, + active + ); } } else if ( [event.code, event.key].includes(CONSTANTS.KEYBOARD_KEYS.A) && this.context === VIEW_STATE.RUNNING ) { this.handleButtonClick(BUTTONS_KEYS.BTN_A, active); - if (this.imageRef.current) { - this.imageRef.current.updateButtonAttributes( - BUTTONS_KEYS.BTN_A, - active - ); - } + + this.imageRef.current?.updateButtonAttributes( + BUTTONS_KEYS.BTN_A, + active + ); } else if ( [event.code, event.key].includes(CONSTANTS.KEYBOARD_KEYS.B) && this.context === VIEW_STATE.RUNNING ) { this.handleButtonClick(BUTTONS_KEYS.BTN_B, active); - if (this.imageRef.current) { - this.imageRef.current.updateButtonAttributes( - BUTTONS_KEYS.BTN_B, - active - ); - } + + this.imageRef.current?.updateButtonAttributes( + BUTTONS_KEYS.BTN_B, + active + ); } else if ( [event.code, event.key].includes(CONSTANTS.KEYBOARD_KEYS.C) && this.context === VIEW_STATE.RUNNING ) { this.handleButtonClick(BUTTONS_KEYS.BTN_AB, active); - if (this.imageRef.current) { - this.imageRef.current.updateButtonAttributes( - BUTTONS_KEYS.BTN_AB, - active - ); - } + + this.imageRef.current?.updateButtonAttributes( + BUTTONS_KEYS.BTN_AB, + active + ); } else if (event.key === CONSTANTS.KEYBOARD_KEYS.CAPITAL_F) { this.togglePlayClick(); } else if (event.key === CONSTANTS.KEYBOARD_KEYS.CAPITAL_R) { diff --git a/src/view/components/toolbar/SensorButton.tsx b/src/view/components/toolbar/SensorButton.tsx index 601bb80c1..b50759bd4 100644 --- a/src/view/components/toolbar/SensorButton.tsx +++ b/src/view/components/toolbar/SensorButton.tsx @@ -13,8 +13,8 @@ class SensorButton extends React.Component { public setButtonClass = (isActive: boolean) => { const isInputDisabled = this.context === VIEW_STATE.PAUSE; - if (isActive && !isInputDisabled && this.buttonRef.current) { - this.buttonRef.current.setAttribute( + if (isActive && !isInputDisabled) { + this.buttonRef.current?.setAttribute( "class", "sensor-button active-button" ); diff --git a/src/view/pages/gettingStarted.tsx b/src/view/pages/gettingStarted.tsx index 1d00df250..04f3d2c8a 100644 --- a/src/view/pages/gettingStarted.tsx +++ b/src/view/pages/gettingStarted.tsx @@ -5,20 +5,16 @@ export class GettingStartedPage extends React.Component { private selectRef: React.RefObject = React.createRef(); componentDidMount() { - if (this.selectRef.current) { - this.selectRef.current.addEventListener("change", (event: any) => { - const visibleElement = document.querySelector( - ".visibleElement" - ); - const target = document.getElementById(event!.target!.value); - if (visibleElement !== null) { - visibleElement.className = "inv"; - } - if (target !== null) { - target.className = "visibleElement"; - } - }); - } + this.selectRef.current?.addEventListener("change", (event: any) => { + const visibleElement = document.querySelector(".visibleElement"); + const target = document.getElementById(event!.target!.value); + if (visibleElement !== null) { + visibleElement.className = "inv"; + } + if (target !== null) { + target.className = "visibleElement"; + } + }); } render() { @@ -138,13 +134,13 @@ export class GettingStartedPage extends React.Component {

Tutorial for CLUE

0. Enable Preview Mode to use the CLUE (This is required)

a. Access your settings:

- Open settings + Open settings
  • Windows or Linux: press Ctrl + , or go to File -> Preferences -> Settings
  • Mac: press Cmd + , or go to Code -> Preferences -> Settings.

b. Check the "Device Simulator Express: Preview Mode" setting.

- Enable preview mode + Enable preview mode

1. Import the the main CLUE library (This is required)