From a19ceeffe257337bf0918fc27b9c27b2273617ea Mon Sep 17 00:00:00 2001 From: Oliver Eyton-Williams Date: Tue, 17 Sep 2019 15:32:23 +0200 Subject: [PATCH] feat: add navigation hotkeys --- client/package-lock.json | 33 +++++++++++++++++++ client/package.json | 1 + .../templates/Challenges/classic/Editor.js | 25 ++++++++++++++ .../src/templates/Challenges/classic/Show.js | 24 ++++++++++++-- .../Challenges/components/Challenge-Title.js | 13 ++++++++ 5 files changed, 93 insertions(+), 3 deletions(-) diff --git a/client/package-lock.json b/client/package-lock.json index fba9845639..775d03bb41 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -16612,6 +16612,39 @@ } } }, + "react-hotkeys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/react-hotkeys/-/react-hotkeys-2.0.0.tgz", + "integrity": "sha512-3n3OU8vLX/pfcJrR3xJ1zlww6KS1kEJt0Whxc4FiGV+MJrQ1mYSYI3qS/11d2MJDFm8IhOXMTFQirfu6AVOF6Q==", + "requires": { + "prop-types": "^15.6.1" + }, + "dependencies": { + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "prop-types": { + "version": "15.7.2", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", + "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", + "requires": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.8.1" + } + }, + "react-is": { + "version": "16.9.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.9.0.tgz", + "integrity": "sha512-tJBzzzIgnnRfEm046qRcURvwQnZVXmuCbscxUO5RWrGTXpon2d4c8mI0D8WE6ydVIm29JiLB6+RslkIvym9Rjw==" + } + } + }, "react-identicons": { "version": "1.1.7", "resolved": "https://registry.npmjs.org/react-identicons/-/react-identicons-1.1.7.tgz", diff --git a/client/package.json b/client/package.json index 846f2f0251..8eb26b0134 100644 --- a/client/package.json +++ b/client/package.json @@ -53,6 +53,7 @@ "react-final-form": "^6.3.0", "react-ga": "^2.6.0", "react-helmet": "^5.2.1", + "react-hotkeys": "^2.0.0", "react-identicons": "^1.1.7", "react-instantsearch-dom": "^5.7.0", "react-monaco-editor": "^0.30.1", diff --git a/client/src/templates/Challenges/classic/Editor.js b/client/src/templates/Challenges/classic/Editor.js index c35bf32f08..1ab4070708 100644 --- a/client/src/templates/Challenges/classic/Editor.js +++ b/client/src/templates/Challenges/classic/Editor.js @@ -3,6 +3,7 @@ import PropTypes from 'prop-types'; import { bindActionCreators } from 'redux'; import { connect } from 'react-redux'; import { createSelector } from 'reselect'; +import { navigate } from 'gatsby'; import { executeChallenge, updateFile } from '../redux'; import { userSelector, isDonationModalOpenSelector } from '../../../redux'; @@ -17,6 +18,8 @@ const propTypes = { executeChallenge: PropTypes.func.isRequired, ext: PropTypes.string, fileKey: PropTypes.string, + nextChallengePath: PropTypes.string.isRequired, + prevChallengePath: PropTypes.string.isRequired, theme: PropTypes.string, updateFile: PropTypes.func.isRequired }; @@ -119,6 +122,28 @@ class Editor extends Component { ], run: this.props.executeChallenge }); + this._editor.addAction({ + id: 'navigate-prev', + label: 'Navigate to previous challenge', + keybindings: [ + /* eslint-disable no-bitwise */ + monaco.KeyMod.chord( + monaco.KeyMod.CtrlCmd | monaco.KeyMod.Shift | monaco.KeyCode.PageDown + ) + ], + run: () => navigate(this.props.prevChallengePath) + }); + this._editor.addAction({ + id: 'navigate-next', + label: 'Navigate to next challenge', + keybindings: [ + /* eslint-disable no-bitwise */ + monaco.KeyMod.chord( + monaco.KeyMod.CtrlCmd | monaco.KeyMod.Shift | monaco.KeyCode.PageUp + ) + ], + run: () => navigate(this.props.nextChallengePath) + }); }; onChange = editorValue => { diff --git a/client/src/templates/Challenges/classic/Show.js b/client/src/templates/Challenges/classic/Show.js index 98f2900472..eb56ea0d79 100644 --- a/client/src/templates/Challenges/classic/Show.js +++ b/client/src/templates/Challenges/classic/Show.js @@ -69,7 +69,12 @@ const propTypes = { initTests: PropTypes.func.isRequired, output: PropTypes.string, pageContext: PropTypes.shape({ - challengeMeta: PropTypes.object + challengeMeta: PropTypes.shape({ + id: PropTypes.string, + introPath: PropTypes.string, + nextChallengePath: PropTypes.string, + prevChallengePath: PropTypes.string + }) }), tests: PropTypes.arrayOf( PropTypes.shape({ @@ -214,10 +219,23 @@ class ShowClassic extends Component { } renderEditor() { - const { files } = this.props; + const { + files, + pageContext: { + challengeMeta: { prevChallengePath, nextChallengePath } + } + } = this.props; + const challengeFile = first(Object.keys(files).map(key => files[key])); return ( - challengeFile && + challengeFile && ( + + ) ); } diff --git a/client/src/templates/Challenges/components/Challenge-Title.js b/client/src/templates/Challenges/components/Challenge-Title.js index 3a8d91b46e..4bc702bf09 100644 --- a/client/src/templates/Challenges/components/Challenge-Title.js +++ b/client/src/templates/Challenges/components/Challenge-Title.js @@ -1,10 +1,17 @@ import React from 'react'; import PropTypes from 'prop-types'; import Link from '../../../components/helpers/Link'; +import { GlobalHotKeys } from 'react-hotkeys'; +import { navigate } from 'gatsby'; import './challenge-title.css'; import GreenPass from '../../../assets/icons/GreenPass'; +const keyMap = { + NAVIGATE_PREV: 'ctrl+shift+pagedown', + NAVIGATE_NEXT: 'ctrl+shift+pageup' +}; + const propTypes = { children: PropTypes.string, introPath: PropTypes.string, @@ -22,8 +29,14 @@ function ChallengeTitle({ prevChallengePath, showPrevNextBtns }) { + const handlers = { + NAVIGATE_PREV: () => navigate(prevChallengePath), + NAVIGATE_NEXT: () => navigate(nextChallengePath) + }; + return (
+ {showPrevNextBtns ? (