feat: add navigation hotkeys
This commit is contained in:
committed by
mrugesh
parent
5de58ad98d
commit
a19ceeffe2
33
client/package-lock.json
generated
33
client/package-lock.json
generated
@ -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": {
|
"react-identicons": {
|
||||||
"version": "1.1.7",
|
"version": "1.1.7",
|
||||||
"resolved": "https://registry.npmjs.org/react-identicons/-/react-identicons-1.1.7.tgz",
|
"resolved": "https://registry.npmjs.org/react-identicons/-/react-identicons-1.1.7.tgz",
|
||||||
|
@ -53,6 +53,7 @@
|
|||||||
"react-final-form": "^6.3.0",
|
"react-final-form": "^6.3.0",
|
||||||
"react-ga": "^2.6.0",
|
"react-ga": "^2.6.0",
|
||||||
"react-helmet": "^5.2.1",
|
"react-helmet": "^5.2.1",
|
||||||
|
"react-hotkeys": "^2.0.0",
|
||||||
"react-identicons": "^1.1.7",
|
"react-identicons": "^1.1.7",
|
||||||
"react-instantsearch-dom": "^5.7.0",
|
"react-instantsearch-dom": "^5.7.0",
|
||||||
"react-monaco-editor": "^0.30.1",
|
"react-monaco-editor": "^0.30.1",
|
||||||
|
@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
|
|||||||
import { bindActionCreators } from 'redux';
|
import { bindActionCreators } from 'redux';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { createSelector } from 'reselect';
|
import { createSelector } from 'reselect';
|
||||||
|
import { navigate } from 'gatsby';
|
||||||
|
|
||||||
import { executeChallenge, updateFile } from '../redux';
|
import { executeChallenge, updateFile } from '../redux';
|
||||||
import { userSelector, isDonationModalOpenSelector } from '../../../redux';
|
import { userSelector, isDonationModalOpenSelector } from '../../../redux';
|
||||||
@ -17,6 +18,8 @@ const propTypes = {
|
|||||||
executeChallenge: PropTypes.func.isRequired,
|
executeChallenge: PropTypes.func.isRequired,
|
||||||
ext: PropTypes.string,
|
ext: PropTypes.string,
|
||||||
fileKey: PropTypes.string,
|
fileKey: PropTypes.string,
|
||||||
|
nextChallengePath: PropTypes.string.isRequired,
|
||||||
|
prevChallengePath: PropTypes.string.isRequired,
|
||||||
theme: PropTypes.string,
|
theme: PropTypes.string,
|
||||||
updateFile: PropTypes.func.isRequired
|
updateFile: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
@ -119,6 +122,28 @@ class Editor extends Component {
|
|||||||
],
|
],
|
||||||
run: this.props.executeChallenge
|
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 => {
|
onChange = editorValue => {
|
||||||
|
@ -69,7 +69,12 @@ const propTypes = {
|
|||||||
initTests: PropTypes.func.isRequired,
|
initTests: PropTypes.func.isRequired,
|
||||||
output: PropTypes.string,
|
output: PropTypes.string,
|
||||||
pageContext: PropTypes.shape({
|
pageContext: PropTypes.shape({
|
||||||
challengeMeta: PropTypes.object
|
challengeMeta: PropTypes.shape({
|
||||||
|
id: PropTypes.string,
|
||||||
|
introPath: PropTypes.string,
|
||||||
|
nextChallengePath: PropTypes.string,
|
||||||
|
prevChallengePath: PropTypes.string
|
||||||
|
})
|
||||||
}),
|
}),
|
||||||
tests: PropTypes.arrayOf(
|
tests: PropTypes.arrayOf(
|
||||||
PropTypes.shape({
|
PropTypes.shape({
|
||||||
@ -214,10 +219,23 @@ class ShowClassic extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderEditor() {
|
renderEditor() {
|
||||||
const { files } = this.props;
|
const {
|
||||||
|
files,
|
||||||
|
pageContext: {
|
||||||
|
challengeMeta: { prevChallengePath, nextChallengePath }
|
||||||
|
}
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
const challengeFile = first(Object.keys(files).map(key => files[key]));
|
const challengeFile = first(Object.keys(files).map(key => files[key]));
|
||||||
return (
|
return (
|
||||||
challengeFile && <Editor {...challengeFile} fileKey={challengeFile.key} />
|
challengeFile && (
|
||||||
|
<Editor
|
||||||
|
nextChallengePath={nextChallengePath}
|
||||||
|
prevChallengePath={prevChallengePath}
|
||||||
|
{...challengeFile}
|
||||||
|
fileKey={challengeFile.key}
|
||||||
|
/>
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,10 +1,17 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import Link from '../../../components/helpers/Link';
|
import Link from '../../../components/helpers/Link';
|
||||||
|
import { GlobalHotKeys } from 'react-hotkeys';
|
||||||
|
import { navigate } from 'gatsby';
|
||||||
|
|
||||||
import './challenge-title.css';
|
import './challenge-title.css';
|
||||||
import GreenPass from '../../../assets/icons/GreenPass';
|
import GreenPass from '../../../assets/icons/GreenPass';
|
||||||
|
|
||||||
|
const keyMap = {
|
||||||
|
NAVIGATE_PREV: 'ctrl+shift+pagedown',
|
||||||
|
NAVIGATE_NEXT: 'ctrl+shift+pageup'
|
||||||
|
};
|
||||||
|
|
||||||
const propTypes = {
|
const propTypes = {
|
||||||
children: PropTypes.string,
|
children: PropTypes.string,
|
||||||
introPath: PropTypes.string,
|
introPath: PropTypes.string,
|
||||||
@ -22,8 +29,14 @@ function ChallengeTitle({
|
|||||||
prevChallengePath,
|
prevChallengePath,
|
||||||
showPrevNextBtns
|
showPrevNextBtns
|
||||||
}) {
|
}) {
|
||||||
|
const handlers = {
|
||||||
|
NAVIGATE_PREV: () => navigate(prevChallengePath),
|
||||||
|
NAVIGATE_NEXT: () => navigate(nextChallengePath)
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='challenge-title-wrap'>
|
<div className='challenge-title-wrap'>
|
||||||
|
<GlobalHotKeys handlers={handlers} keyMap={keyMap} />
|
||||||
{showPrevNextBtns ? (
|
{showPrevNextBtns ? (
|
||||||
<Link
|
<Link
|
||||||
aria-label='Previous lesson'
|
aria-label='Previous lesson'
|
||||||
|
Reference in New Issue
Block a user