Skip to content

Commit

Permalink
add useKeypress hook
Browse files Browse the repository at this point in the history
  • Loading branch information
Jonathan Dahan committed Sep 7, 2019
1 parent 9487c00 commit 844a458
Show file tree
Hide file tree
Showing 8 changed files with 137 additions and 1 deletion.
2 changes: 2 additions & 0 deletions examples/usekeypress/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
'use strict';
require('import-jsx')('./usekeypress');
42 changes: 42 additions & 0 deletions examples/usekeypress/usekeypress.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
'use strict';
const {useState} = require('react');
const React = require('react');
const {render, useKeypress, Box} = require('../..');

const {exit} = process;

const Robot = () => {
const [x, setX] = useState(1);
const [y, setY] = useState(1);

useKeypress(key => {
switch (key) {
case 'h':
setX(Math.max(1, x - 1));
break;
case 'l':
setX(Math.min(80, x + 1));
break;
case 'j':
setY(Math.min(40, y + 1));
break;
case 'k':
setY(Math.max(1, y - 1));
break;
case 'q':
exit();
break;
default:
break;
}
});

return (
<Box flexDirection="column">
<Box>Use h, j, k, and l to move the face. q to exit</Box>
<Box marginTop={y} marginLeft={x}>^_^</Box>
</Box>
);
};

render(<Robot/>);
9 changes: 9 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as React from "react";
import { Key } from 'readline';

export interface RenderOptions {
/**
Expand Down Expand Up @@ -44,6 +45,14 @@ export type Instance = {

export type Unmount = () => void;

/**
* Hook that calls keyPressHandler callback with whatever key has been pressed.
* See key to handle modifiers correctly.
*/
export function useKeypress(
keyPressHandler: (str?: string, key?: Key) => void
): void;

/**
* Mount a component and render the output.
*/
Expand Down
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,10 @@
],
"overrides": [
{
"files": "src/components/*.js",
"files": [
"src/components/*.js",
"src/hooks/*.js"
],
"rules": {
"unicorn/filename-case": "off",
"react/require-default-props": "warning"
Expand Down
15 changes: 15 additions & 0 deletions src/hooks/useKeypress.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import {useEffect, useContext} from 'react';
import {StdinContext} from '..';

export default keypressHandler => {
const {stdin, setRawMode} = useContext(StdinContext);

useEffect(() => {
setRawMode(true);
stdin.on('keypress', keypressHandler);
return () => {
stdin.off('keypress', keypressHandler);
setRawMode(false);
};
}, [stdin, setRawMode, keypressHandler]);
};
1 change: 1 addition & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ export {default as AppContext} from './components/AppContext';
export {default as StdinContext} from './components/StdinContext';
export {default as StdoutContext} from './components/StdoutContext';
export {default as Static} from './components/Static';
export {default as useKeypress} from './hooks/useKeypress';
42 changes: 42 additions & 0 deletions test/exit.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,48 @@ test('exit with thrown error', async t => {
t.true(output.includes('errored'));
});

test.cb('handles keypresses', t => {
const term = spawn('node', ['./fixtures/run', './handles-keypresses'], {
name: 'xterm',
cols: 100,
cwd: __dirname,
env: process.env
});

let output = '';

term.on('data', data => {
output += data;
});

let isExited = false;

term.on('exit', code => {
isExited = true;

if (code === 0) {
t.true(output.includes('exited'));
t.pass();
t.end();
return;
}

t.fail();
t.end();
});

setTimeout(() => {
t.false(isExited);
'abcdefghijklmnopqrstuvwxyz'.split('').forEach(key => term.write(key));
}, 100);

setTimeout(() => {
term.kill();
t.fail();
t.end();
}, 5000);
});

test.cb('don\'t exit while raw mode is active', t => {
const term = spawn('node', ['./fixtures/run', './exit-double-raw-mode'], {
name: 'xterm-color',
Expand Down
22 changes: 22 additions & 0 deletions test/fixtures/handles-keypresses.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
'use strict';
const React = require('react');
const {render, useKeypress} = require('../..');

const input = new Set('abcdefghijklmnopqrstuvwxyz'.split(''));

const KeypressTest = () => {
useKeypress(str => {
input.delete(str);
if (input.size === 0) {
app.unmount();
}
});
return null;
};

const app = render(<KeypressTest/>);

(async () => {
await app.waitUntilExit();
console.log('exited');
})();

0 comments on commit 844a458

Please sign in to comment.