ink
ink copied to clipboard
Multiple inputs show multiple cursors at the same time?
I am trying to make a simple 5 input CLI. Currently, it has 2 inputs. And it always shows cursors on both.
I have searched all over GitHub & it looks like some examples are creating a single component containing TextInput
like renderTitle
& renderDescription
.
How do I solve it? Do I have to use useFocus
or useFocusManager
? I did try them but got nowhere. I'd appreciate an example of just seeing 2 inputs one-below-the-other or just point me to a repo.
Here's my code right now:
Input
import React from 'react'
import { Box, Text } from 'ink'
import TextInput from 'ink-text-input'
interface IInput {
label: string
onChange: (s: string) => string
onSubmit: (s: string) => void
}
export const Input = ({ label, onChange, onSubmit }: IInput) => {
const [query, setQuery] = React.useState('')
const [submitted, setSubmitted] = React.useState(false)
const handleChange = (q: string) => {
setQuery(q)
onChange(q)
}
const handleSubmit = (q: string) => {
onSubmit(q)
setSubmitted(true)
}
if (submitted) return null
return (
<Box flexDirection="column">
<Text>
<Text color="#6EE7B7">❯ {label}: </Text>
<TextInput value={query} onChange={handleChange} onSubmit={handleSubmit} />
</Text>
</Box>
)
}
ui.tsx
import React from 'react'
import { Box } from 'ink'
import Gradient from 'ink-gradient'
import BigText from 'ink-big-text'
import { Input } from './components/Input'
interface IApp {}
const App = ({}: IApp) => {
const [_, setTitle] = React.useState('')
const [__, setDescription] = React.useState('')
return (
<Box flexDirection="column">
<Gradient name="fruit">
<BigText text="Compose New Post" font="tiny" />
</Gradient>
<Input
label="Title"
onChange={(q: string) => {
setTitle(q)
return q
}}
onSubmit={(q) => {
console.log('submit', q)
}}
/>
<Input
label="Description"
onChange={(q: string) => {
setDescription(q)
return q
}}
onSubmit={(q) => {
console.log('submit', q)
}}
/>
</Box>
)
}
module.exports = App
export default App
I solved it by using a boolean fieldNo
state which I feel is unnecessary.
import React from 'react'
import { Box, Text, useApp, useInput } from 'ink'
import Gradient from 'ink-gradient'
import BigText from 'ink-big-text'
const App = () => {
const [fieldNo, setFieldNo] = React.useState(1)
const { exit } = useApp()
useInput((_, key) => {
if (key.escape) {
setTimeout(() => {
exit()
}, 500)
}
})
return (
<Box flexDirection="column">
<Gradient name="fruit">
<BigText text="Compose New Post" font="tiny" />
</Gradient>
<Text italic color="#708090">
All fields are optional*
</Text>
<Input
label="Title"
placeholder={`default: "Untitled"`}
focus={fieldNo === 1}
onSubmit={(title: string) => {
if (title.trim() === '') {
state.title = 'Untitled'
} else {
state.title = title
}
setFieldNo(2)
}}
/>
{fieldNo >= 2 && (
<Input
label="Description"
placeholder={`default: ""`}
focus={fieldNo === 2}
onSubmit={(description: string) => {
state.description = description
setFieldNo(3)
exit()
}}
/>
)}
)
}
Isn't there any way to make this work without having to write an unnecessary field?
I'm also wondering about this. I haven't found any examples with >1 input (text input, select, or any other prompt).
@deadcoder0904 Your solution is a correct one, since Input
component needs to know whether it's focused or not. Otherwise both of them will receive keyboard input.