-
-
Notifications
You must be signed in to change notification settings - Fork 619
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support for keypress events #138
Comments
what im doing currently as an example: const useKeyHandler = keyHandler => {
const { stdin, setRawMode } = useContext(StdinContext)
useEffect(() => {
setRawMode(true)
stdin.on('data', keyHandler)
return () => {
stdin.off('data', keyHandler)
setRawMode(false)
}
}, [stdin, setRawMode])
}
...snip
// handle keypresses
useKeyHandler(data => {
if (data === 's') setSyncing(syncing => !syncing)
if (data === 'p') setCreature(ascii())
if (data === 'q') process.exit(0)
}) from https://github.com/jedahan/creature/blob/ink/src/App.jsx |
The suggestion to just expose the handler seems nicer for those coming from nodeland - but i wonder if you have different keys in different contexts if the reactish way helps with multi-screen apps... |
Not sure if needed, but I have a use case for this. I've been looking at rewriting https://github.com/jest-community/jest-watch-typeahead to use The thing I want to achieve is this: So an text input, and below it a list over hits. If the user hits the down arrow, then I want to select from the list. It might be enough for https://github.com/vadimdemedes/ink-select-input to just have a mode for "none selected" instead of starting with the first in the list as "selected"? But I think I can also listen for keypresses myself and only make the select I render |
Ink 2.0.4 now enables As for React hook for keypress events, I like @jedahan's solution (only with |
@SimenB I think the |
I agree. @jedahan Would you be interested in doing a PR? |
I didn't mean for it to be the default behavior, just a prop 🙂 However, setting EDIT: vadimdemedes/ink-select-input#13 Back to keypresses - I tried this code in 2.0.4: // copy of @jedahan's hook with `keypress`
function useKeyHandler(keyHandler) {
const { stdin, setRawMode } = React.useContext(StdinContext);
React.useEffect(() => {
setRawMode(true);
stdin.on('keypress', keyHandler);
return () => {
stdin.off('keypress', keyHandler);
setRawMode(false);
};
}, [stdin, setRawMode]);
}
function PromptThing() {
useKeyHandler(() => {});
const items = [
{ label: 'hah', value: 'thing number 1' },
{ label: 'hah2', value: 'thing number 2' },
];
return (
<Box>
<SelectInput items={items} limit={10} />
</Box>
);
}
render(<PromptThing />); It seems to completely mess up |
Interesting, unused |
Ok, it's the |
While fixing this, discovered another interesting issue with reordering children with |
@SimenB |
Nice, thanks @vadimdemedes! Works great now 🙂 |
Thanks for your suggestions @jedahan. I'm taking your code and am having trouble with a simple example. Is this just a misunderstanding of hooks, or is there a bug in When I run this code (using ink 2.0.5), I can never get it to go past index===1, and pressing the "k" key always sets index to -1. import React, { useState, useEffect, useContext } from "react";
import { render, StdinContext, Color, Box } from "ink";
const useKeyHandler = keyHandler => {
const { stdin, setRawMode } = useContext(StdinContext);
useEffect(() => {
setRawMode(true);
stdin.on("data", keyHandler);
return () => {
stdin.off("data", keyHandler);
setRawMode(false);
};
}, [stdin, setRawMode]);
};
// since we have one handler for the whole app, our state lives up here instead of in the functions, maybe there is another way?
const App = () => {
const [index, setIndex] = useState(0);
useKeyHandler(data => {
if (data === "j") setIndex(index + 1);
if (data === "k") setIndex(index - 1);
});
return <IssuesWrapper index={index} />;
};
const IssuesWrapper = ({ index }) => {
return (
<Box>
<Box>
<Issues index={index} />
</Box>
<Box>
<>Press 'j' to move up</>
<>Press 'k' to move down</>
</Box>
</Box>
);
};
const Issues = ({ index }) => {
const [issues, setIssues] = useState([
{ id: "abc", title: "Something" },
{ id: "def", title: "Nothing" },
{ id: "213123", title: "Perspective" },
{ id: "34242", title: "Getting to nothing" }
]);
return (
<Box paddingTop={2} paddingBottom={2} flexDirection="column">
<Box paddingBottom={2}>Current index is: {index}</Box>
<Box flexDirection="column">
{issues &&
issues.map((issue, i) => {
const { id, title } = issue;
return (
<Box key={i}>
<Color bold={i === index}>
{id} :: {title}
</Color>
</Box>
);
})}
</Box>
</Box>
);
};
render(<App />); |
@xrd The issue here is how JavaScript works :) const App = () => {
const [index, setIndex] = useState(0);
useKeyHandler(data => {
setIndex(index + 1); // 0 + 1 = 1
});
}; Try passing a function to setIndex(prevIndex => prevIndex + 1);
setIndex(prevIndex => prevIndex - 1); |
@vadimdemedes Thank you! |
|
I would like |
i started hacking around with so, i sought out ways to integrate |
ah, using the ink reconciler worked! ink-box now renders correctly and i can use |
hm scratch that - see my comment in #209 - turns out react-dom is not happy with |
Released |
To be able to have the component rerendered on keypresses and get the keys.
Ink would need to do
readline.emitKeypressEvents(stdin);
in https://github.com/vadimdemedes/ink/blob/master/src/components/App.js#L61 And it requires raw-mode.After enabling the above, Ink could do something like this internally:
My initial idea was to expose a
useKeypress()
React hook. @vadimdemedes suggested we could just expose the keypresses by default if raw-mode is activated, as the output is being diffed anyway, so the cost is negligible.The text was updated successfully, but these errors were encountered: