From a0c426888b20f1747a6c0dd9333fdb9e06d9cc8b Mon Sep 17 00:00:00 2001 From: Matthew Grove Date: Mon, 21 Feb 2022 21:38:12 +0000 Subject: [PATCH] Add virtual keyboard during tests Optional and with support for most languages --- package-lock.json | 10 +++ package.json | 4 +- src/Progress.js | 144 ++++++++++++++++++++++++++++++--- src/SetPage.js | 2 +- src/css/Button.css | 10 +++ src/css/ConfirmationDialog.css | 6 +- src/css/PopUp.css | 9 ++- src/css/Progress.css | 48 ++++------- src/css/SetPage.css | 30 +------ src/css/UserGroups.css | 25 +----- 10 files changed, 189 insertions(+), 99 deletions(-) diff --git a/package-lock.json b/package-lock.json index 17e7e29..8a19fa8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17951,6 +17951,11 @@ "react-is": "^16.12.0 || ^17.0.0" } }, + "react-simple-keyboard": { + "version": "3.4.65", + "resolved": "https://registry.npmjs.org/react-simple-keyboard/-/react-simple-keyboard-3.4.65.tgz", + "integrity": "sha512-SSxAXk/I6ry4oWi/Z2e1s21mtwBuNJLiMOJIprTf2OAniVxEf+PH5zupWu07wbNCMDsMv3uhdaJAR/W3kVMieg==" + }, "react-test-renderer": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-17.0.2.tgz", @@ -18749,6 +18754,11 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==" }, + "simple-keyboard-layouts": { + "version": "3.1.54", + "resolved": "https://registry.npmjs.org/simple-keyboard-layouts/-/simple-keyboard-layouts-3.1.54.tgz", + "integrity": "sha512-JZ2zR0rq3rctFjmYo33YiXoZ535Y8m/LBk2qBWP/MZAOjRi2XpXaJnhQrWnVYQv6CP2kOlZZvEQAzn3DhZlQFA==" + }, "simple-swizzle": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", diff --git a/package.json b/package.json index fa4a485..fda0931 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "parandum", - "version": "2.1.8", + "version": "2.1.9", "private": true, "dependencies": { "@babel/core": "^7.16.0", @@ -27,7 +27,9 @@ "react-router-dom": "^5.3.0", "react-scripts": "^5.0.0-next.47", "react-select": "^5.2.1", + "react-simple-keyboard": "^3.4.65", "react-xarrows": "^2.0.2", + "simple-keyboard-layouts": "^3.1.54", "styled-components": "^5.3.3", "typescript": "^4.5.2", "universal-cookie": "^4.0.4", diff --git a/src/Progress.js b/src/Progress.js index 76428cc..67e04dc 100644 --- a/src/Progress.js +++ b/src/Progress.js @@ -60,6 +60,10 @@ export default withRouter(class Progress extends React.Component { sound: false, ignoreCaps: false, numberOfAnswers: null, + virtualKeyboardLanguage: "english", + virtualKeyboardLayoutName: "default", + showVirtualKeyboard: false, + showVirtualKeyboardOptions: false, } constructor(props) { @@ -73,6 +77,12 @@ export default withRouter(class Progress extends React.Component { createProgressWithIncorrect: props.functions.httpsCallable("createProgressWithIncorrect"), }, navbarItems: [ + { + type: "button", + onClick: this.showVirtualKeyboardOptions, + icon: , + hideTextMobile: true, + }, { type: "button", onClick: this.showSettings, @@ -88,6 +98,16 @@ export default withRouter(class Progress extends React.Component { ], ...this.changeableStateItems, }; + + this.keyboardLayouts = new KeyboardLayouts(); + this.keyboardLayoutsObj = this.keyboardLayouts.get(); + this.keyboardLayoutNames = Object.keys(this.keyboardLayoutsObj).map(key => + [ + key, + // title case from camel case + key.charAt(0).toUpperCase() + key.slice(1).replace(/([A-Z])/g, " $1") + ] + ); let isMounted = true; Object.defineProperty(this, "isMounted", { @@ -300,15 +320,34 @@ export default withRouter(class Progress extends React.Component { this.setState({ answerInput: event.target.value, }); + this.virtualKeyboard.setInput(event.target.value); } } - proceed = () => { + onVirtualKeyboardChange = (answerInput) => { + this.setState({ answerInput }); + }; + + onVirtualKeyboardKeyPress = (button) => { + if (button === "{shift}" || button === "{lock}") { + this.handleVirtualKeyboardShift(); + } else if (button === "{enter}") { + this.proceed("virtual"); + } + }; + + handleVirtualKeyboardShift = () => { + this.setState({ + layoutName: this.state.virtualKeyboardLayoutName === "default" ? "shift" : "default", + }); + } + + proceed = (referrer="physical") => { if (this.state.canProceed) { if (this.state.currentAnswerStatus === null) { - this.processAnswer(); + this.processAnswer(referrer); } else { - this.nextQuestion(); + this.nextQuestion(referrer); } } } @@ -325,7 +364,7 @@ export default withRouter(class Progress extends React.Component { return newString; } - processAnswer = async () => { + processAnswer = async (referrer="physical") => { if (this.state.canProceed) { this.startLoading(); @@ -484,7 +523,7 @@ export default withRouter(class Progress extends React.Component { await Promise.all(promises); this.setState(newState, () => { - if (!newState.duration) this.answerInput.focus() + if (!newState.duration && referrer === "physical") this.answerInput.focus(); }); }).catch((error) => { console.log(`Couldn't process answer: ${error}`); @@ -497,7 +536,11 @@ export default withRouter(class Progress extends React.Component { } showNextQuestion = (newState, currentState) => { - if (currentState.nextPrompt === null) newState.setComplete = true; + if (currentState.nextPrompt === null) { + newState.setComplete = true; + this.virtualKeyboard.clearInput(); + newState.showVirtualKeyboard = false; + } newState.currentCorrect = []; newState.currentPrompt = currentState.nextPrompt; newState.currentSound = currentState.nextSound; @@ -505,7 +548,7 @@ export default withRouter(class Progress extends React.Component { return newState; } - nextQuestion = () => { + nextQuestion = (referrer="physical") => { if (this.state.canProceed) { this.startLoading(); @@ -520,7 +563,12 @@ export default withRouter(class Progress extends React.Component { newState = this.showNextQuestion(newState, this.state); } - this.setState(newState, () => (this.isMounted && !this.state.setComplete) && this.answerInput.focus()); + this.setState(newState, () => { + this.virtualKeyboard.clearInput(); + if (this.isMounted && !this.state.setComplete && referrer === "physical") { + this.answerInput.focus(); + } + }); } } @@ -634,6 +682,33 @@ export default withRouter(class Progress extends React.Component { }); } + showVirtualKeyboardOptions = () => { + this.setState({ + showVirtualKeyboardOptions: true, + }) + } + + hideVirtualKeyboardOptions = () => { + this.setState({ + showVirtualKeyboardOptions: false, + }) + } + + showVirtualKeyboard = (language) => { + this.setState({ + showVirtualKeyboard: true, + showVirtualKeyboardOptions: false, + virtualKeyboardLanguage: language, + }); + } + + hideVirtualKeyboard = () => { + this.setState({ + showVirtualKeyboard: false, + showVirtualKeyboardOptions: false, + }); + } + render() { return (
@@ -665,7 +740,7 @@ export default withRouter(class Progress extends React.Component { autoCorrect="off" /> + +

Or select a language:

+
+ { + this.keyboardLayoutNames.map(([layout, formattedName]) => + + ) + } +
+ + +
+ + } +
+ (this.virtualKeyboard = r)} + onChange={this.onVirtualKeyboardChange} + onKeyPress={this.onVirtualKeyboardKeyPress} + layout={this.keyboardLayoutsObj[this.state.virtualKeyboardLanguage].layout} + layoutName={this.state.layoutName} + /> +
) } diff --git a/src/SetPage.js b/src/SetPage.js index 760b52c..9d642ad 100644 --- a/src/SetPage.js +++ b/src/SetPage.js @@ -446,7 +446,7 @@ export default withRouter(class SetPage extends React.Component { <>

Select a Group

-
+
{ Object.keys(this.state.groups).map((groupId) =>