diff --git a/package.json b/package.json index 637ea0f23..3108a4815 100644 --- a/package.json +++ b/package.json @@ -48,6 +48,7 @@ "cli-truncate": "^1.1.0", "is-ci": "^2.0.0", "jsdom": "^15.1.1", + "keycode": "^2.2.0", "lodash.throttle": "^4.1.1", "log-update": "^3.0.0", "prop-types": "^15.6.2", diff --git a/src/components/App.js b/src/components/App.js index 748c7572f..7d7e779f2 100644 --- a/src/components/App.js +++ b/src/components/App.js @@ -2,10 +2,82 @@ import readline from 'readline'; import React, {PureComponent} from 'react'; import PropTypes from 'prop-types'; import cliCursor from 'cli-cursor'; +import {default as keycode} from 'keycode'; import AppContext from './AppContext'; import StdinContext from './StdinContext'; import StdoutContext from './StdoutContext'; +class DOMKeypressDispatcher extends PureComponent { + static propTypes = { + stdin: PropTypes.object.isRequired, + setRawMode: PropTypes.func.isRequired, + document: PropTypes.any, + window: PropTypes.any + }; + + componentDidMount() { + if (this.props.document) { + const {stdin, setRawMode} = this.props; + setRawMode(true); + stdin.on('keypress', this.dispatchInput); + } + } + + componentWillUnmount() { + if (this.props.document) { + const {stdin, setRawMode} = this.props; + stdin.removeListener('keypress', this.dispatchInput); + setRawMode(false); + } + } + + render() { + return (null); + } + + dispatchInput = (str, key) => { + const code = keycode(key.name); + const downEvent = new this.props.window.KeyboardEvent('keydown', { + key: key.name, + charCode: code, + ctrlKey: key.ctrl, + shiftKey: key.shift, + keyCode: code, + which: code, + bubbles: true, + repeat: false, + location: 0, + isComposing: false + }); + this.props.document.activeElement.dispatchEvent(downEvent); + const pressEvent = new this.props.window.KeyboardEvent('keypress', { + key: key.name, + charCode: code, + ctrlKey: key.ctrl, + shiftKey: key.shift, + keyCode: code, + which: code, + bubbles: true, + repeat: false, + location: 0, + isComposing: false + }); + this.props.document.activeElement.dispatchEvent(pressEvent); + const upEvent = new this.props.window.KeyboardEvent('keyup', { + key: key.name, + charCode: code, + ctrlKey: key.ctrl, + shiftKey: key.shift, + keyCode: code, + which: code, + bubbles: true, + repeat: false, + location: 0, + isComposing: false + }); + this.props.document.activeElement.dispatchEvent(upEvent); + } +} // Root component for all Ink apps // It renders stdin and stdout contexts, so that children can access them if needed @@ -16,7 +88,9 @@ export default class App extends PureComponent { stdin: PropTypes.object.isRequired, stdout: PropTypes.object.isRequired, exitOnCtrlC: PropTypes.bool.isRequired, - onExit: PropTypes.func.isRequired + onExit: PropTypes.func.isRequired, + window: PropTypes.object, + document: PropTypes.object }; // Determines if TTY is supported on the provided stdin @@ -33,6 +107,14 @@ export default class App extends PureComponent { } render() { + const keyboardEventDispatcher = (this.props.window && this.props.document) ? ( + + ) : null; return ( + {keyboardEventDispatcher} {this.props.children} diff --git a/src/instance.js b/src/instance.js index 703d1b664..e6c95b147 100644 --- a/src/instance.js +++ b/src/instance.js @@ -102,6 +102,8 @@ export default class Instance {