ink-use-input-hints
Version:
Wrapper around ink useInput provides builder API and facilitates hint bar generation
194 lines (155 loc) • 8.35 kB
Markdown
# ink-use-input-hints
A facade for [ink](https://github.com/vadimdemedes/ink) [`useInput`](https://github.com/vadimdemedes/ink#useinputinputhandler-options) hook using a builder API and adding helpers for generating the hint bar.
## Installation
npm install ink-use-input-hints
## Quick start example
_App.js_
```javascript
const { InputHints } = require('ink-use-input-hints');
const App = () => (
<InputHints>
<SubComponent/>
</InputHints>
)
```
_SubComponent.js_
```javascript
const { useInputWithHints } = require('ink-use-input-hints');
const SubComponent = () => {
useInputWithHints((handler, { KEYS }) => handler
.add([KEYS.leftArrow, 'h'], () => {
// handle movement left
}, {description: 'move left'})
.add([KEYS.rightArrow, 'l'], () => {
// handle movement right
}, {description: 'move right'})
.add(['^'], () => {
// handle going to start
}, {description: 'go to start'})
.add(['$'], () => {
// handle going to end
}, {description: 'go to end'})
.add([KEYS.return], () => {
// submit, wont be shown in hint bar
})
);
return (<></>)
}
```
Bottom of App should show the bar with the keyboard hints:

## Contents
* [Background](#Background)
* [API](#API)
* [config](#config)
* [InputHintsContext](#InputHintsContext)
* [InputHintsContextProvider](#InputHintsContextProvider)
* [useInputWithHints](#useInputWithHints)
* [KEYS](#KEYS)
* [InputHints](#InputHints)
* [HintBar](#HintBar)
* [Development](#Development)
* [LICENSE](#LICENSE)
## Background
I enjoy Ink very much and find myself reusing the hint bar again and again, so I've decided to extract it out.
I found the builder API more intuitive for reuse (for example, having the same keys do similar things across different screens).
## API
Module exports the following members:
#### config
A [map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) with parameters configuring the library behaviour; the keys of interest are:
- `AUTO_UPDATE_HINTS` - _default: true_ When set to true, list of hints (`hints` array of [InputHintsContext](#InputHintsContext)) will be automatically updated each time `useInputWithHints` is called.
- `KEY_FORMATTER_FN` - a function which accepts an object `{index, key}` and returns a string. This is used to format the key description part of the hint.
For example, in the above screenshot, hint for handling `[KEYS.leftArrow, 'h']` is displayed as "h, <LEFT_ARROW> -- move left". Let's say we change this:
```Javascript
config.set('KEY_FORMATTER_FN', ({index, key}) => `${key} (${index+1})`)
```
Now, the hint for handling `[KEYS.leftArrow, 'h']` would be displayed as "h (0), leftArrow (1) -- move left".
- `HINT_FORMATTER_FN` - a function which accepts an object `{index, formattedKeyDescriptions, description}` and returns a string. This governs how the individual hint text is generated from the list of keys and it's description. `index` is the index of the hint. `formattedKeyDescriptions` is an array of strings representing key descriptions (as returned by `KEY_FORMATTER_FN`). `description` is a description text provided in `useInputWithHints` handler. The default value (generating the output in the above example) looks like this:
```javascript
config.set('HINT_FORMATTER_FN', ({
index,
formattedKeyDescriptions,
description
}) => {
const sortedByLength = sortBy(formattedKeyDescriptions, desc => desc.length);
const keyList = sortedByLength.join(', ');
return `${keyList} -- ${description}`;
});
```
#### InputHintsContext
A react [context](https://reactjs.org/docs/context.html) for sharing data (hints) between the `useInputWithHints` hook and the components displaying them (such as [HintBar](#HintBar)).
If you want to manually manage the hints, you can use this.
```javascript
const { InputHintsContext } = require('ink-use-input-hints');
// ...
const inputHintsContext = useContext(InputHintsContext);
inputHintsContext.setHints(['my','custom','hints']);
inputHintsContext.hints // array containing ['my','custom','hints']
```
#### InputHintsContextProvider
[Provider](https://reactjs.org/docs/context.html#contextprovider) for [`InputHintsContext`](#InputHintsContext). Provides `hints` and `setHints`. Already contained in [`InputHints`](#InputHints).
#### useInputWithHints
Hook for handling user input and adding movement hints. Similar to [`useInput`](https://github.com/vadimdemedes/ink#useinputinputhandler-options).
`const hints = useInputWithHints(definerFunction)`;
To define how the user input is handled, you pass in `definerFunction`, which will be provided with two objects -
`handler` and `options`, i.e. `definerFunction(handlersBuilder, options)`;
`handlersBuilder` has the following methods:
- `add` which accepts three arguments:
`handlersBuilder.add(keyList, handler, additionalOptions)`
- `keyList` - array of inputs for which the `handler` should be triggered. Each element is either a single character, or a string representing a "special" key ('return', 'ctrl', 'leftArrow'.. ) The list matches the possible key names of [the second argument to the original useInput](https://github.com/vadimdemedes/ink#key). All of the values are accessible via the [KEYS](#KEYS) object.
- `handler` - this is called when user presses one of the keys in `keyList`, like the [inputHandler](https://github.com/vadimdemedes/ink#useinputinputhandler-options) for `useInput`.
- `additionalOptions` - object containing a single key, `description` - used for generating hints. If it's not provided, a hint for this `keyList` won't be generated
- `describe` which accepts a single string argument - `handlersBuilder.describe(text)`
- this will just add `text` to the list of hints visible in the hint bar
The `options` argument to `definerFunction` has a single key, [`KEYS`](#KEYS), which is an object listing all the recognizable special keys.
Finally, `useInputWithHints` returns an array listing generated hints.
```javascript
const hints = useInputWithHints((handler, { KEYS }) => handler
.add([KEYS.return, 'C'], () => {
console.log('Submit!');
submitInfo();
}, {description: 'Submits the form'})
);
// hints === ['C, <RETURN> -- Submits the form']
// when user presses <RETURN> or C, the method submitInfo is called
```
#### KEYS
Contains all of the recognizable special keys:
```javascript
const KEYS = {
upArrow: 'upArrow',
downArrow: 'downArrow',
leftArrow: 'leftArrow',
rightArrow: 'rightArrow',
pageDown: 'pageDown',
pageUp: 'pageUp',
return: 'return',
escape: 'escape',
ctrl: 'ctrl',
shift: 'shift',
tab: 'tab',
backspace: 'backspace',
delete: 'delete',
meta: 'meta'
}
```
#### InputHints
A component which wraps all the children in [`InputHintsContextProvider`](#InputHintsContextProvider) and draws the [`<HintBar/>`](#HintBar) below them.
Can be passed an object (`{wrapperProps: {..}, itemWrapperProps: {..}, textProps: {..}}`) specifying how the hintBar is drawn.
```javascript
<InputHints hintBarProps={{
textProps: {backgroundColor: 'black', color: 'white'}
}}>
...
</InputHints>
```
#### HintBar
Default component for drawing default bar with hints. Accepts the following attributes:
- `hints` - array of hints. If not provided, it will use [InputHintsContextProvider](#InputHintsContextProvider)
- `wrapperProps` - object containing attributes for styling the Ink [Box](https://github.com/vadimdemedes/ink#box) containing the hint bar
- `itemWrapperProps` - object containing attributes for styling the [Box](https://github.com/vadimdemedes/ink#box) wrapping the individual hint
- `textProps` - object containing attributes for styling the text display of an individual hint. Ink's [Text](https://github.com/vadimdemedes/ink#text) component is used for this, so attributes are the same.
## Development
Currently, this is good enough for my use case - of course, suggestions and improvements are welcome.
## LICENSE
[MIT](LICENSE)