An open API service indexing awesome lists of open source software.

https://github.com/infely/react-curse

A curses-like blazingly fast react renderer
https://github.com/infely/react-curse

Last synced: 7 months ago
JSON representation

A curses-like blazingly fast react renderer

Awesome Lists containing this project

README

          

# react-curse








A curses-like blazingly fast react renderer

- It is fast, intuitive and easy to use
- It draws only changed characters
- It uses a small amount of SSH traffic

See it in action:

![](media/demo.gif)

Still here? Let's go deeper:

- It has fancy components that are ready to use or can be tree-shaked from your final bundle
- It supports keyboard and mouse
- It works in fullscreen and inline modes
- It has cool hooks like animation with trail
- It is solely dependent on react
- It can generate an all-in-one bundle around 100 kb

You can easily build full-scale terminal UI applications like:

## Apps that use it

- [mngr](https://github.com/infely/mngr) - Database manager supports mongodb, mysql/mariadb, postgresql, sqlite and json-server
- [nfi](https://github.com/infely/nfi) - Simple nerd fonts icons cheat sheet that allows you to quickly find and copy glyph to clipboard

## Installation

Just run `npm init react-curse` answer a few questions and you are ready to go

## Examples

#### Hello world

```jsx
import React from 'react'
import ReactCurse, { Text } from 'react-curse'

const App = ({ text }) => {
return {text}
}

ReactCurse.render()
```

![](media/exampleHello.png)

#### How to handle input

```jsx
import React, { useState } from 'react'
import ReactCurse, { Text, useInput, exit } from 'react-curse'

const App = () => {
const [counter, setCounter] = useState(0)

useInput(
input => {
if (input === 'k') setCounter(counter + 1)
if (input === 'j') setCounter(counter - 1)
if (input === 'q') exit()
},
[counter]
)

return (

counter: {counter}

)
}

ReactCurse.render()
```

![](media/exampleInput.gif)

#### How to animate

```jsx
import React from 'react'
import ReactCurse, { useAnimation } from 'react-curse'

const App = () => {
const { interpolate, interpolateColor } = useAnimation(1000)

return
}

ReactCurse.render()
```

![](media/exampleAnimate.gif)

## Contents

- [Components](#components)
- [``](#text)
- [``](#input)
- [``](#banner)
- [``](#bar)
- [``](#block)
- [``](#canvas), [``](#point), [``](#line)
- [``](#frame)
- [``](#list)
- [``](#listtable)
- [``](#Scrollbar)
- [``](#separator)
- [``](#spinner)
- [``](#view)
- [Hooks](#hooks)
- [`useAnimation`](#useanimation), [`useTrail`](#usetrail), [``](#trail)
- [`useChildrenSize`](#usechildrensize)
- [`useClipboard`](#useclipboard)
- [`useInput`](#useinput)
- [`useMouse`](#usemouse)
- [`useSize`](#usesize)
- [`useWordWrap`](#useWordWrap)
- [API](#api)
- [`render`](#render)
- [`inline`](#inline)
- [`bell`](#bell)
- [`exit`](#exit)

## Components

### ``

Base component\
The only component required to do anything\
Every other component uses this one to draw

##### y?, x?: `number` | `string`

Position from top left corner relative to parent\
Content will be cropped by parent\
See `absolute` to avoid this behavior\
Example: `32, '100%', '100%-8'`

##### height?, width?: `number` | `string`

Size of block, will be cropped by parent\
See `absolute` to avoid this behavior

##### absolute?: `boolean`

Makes position and size ignoring parent container

##### background?, color?: `number` | `string`

Background and foreground color\
Example: `31, 'Red', '#f04020', '#f42'`

##### clear?: `boolean`

Clears block before drawing content\
`height` and `width`

##### block?: `boolean`

Moves cursor to a new line after its content relative to parent

##### bold?, dim?, italic?, underline?, blinking?, inverse?, strikethrough?: `boolean`

Text modifiers

#### Examples

```jsx
hello world
hello world
hello world

hello world
hello world
hello world

```

![](media/Text.png)

### ``

Text input component with cursor movement and text scroll support\
If its height is more than 1, then it switches to multiline, like textarea\
Most terminal shortcuts are supported

##### focus?: `boolean` = `true`

Makes it active

##### type?: `'text'` | `'password'` | `'hidden'` = `‘text'`

##### initialValue?: `string`

##### cursorBackground?: `number` | `string`

##### onCancel?: `() => void`

##### onChange?: `(string) => void`

##### onSubmit?: `(string) => void`

#### Examples

```jsx

```

![](media/Input-1.gif)

![](media/Input-2.gif)

### ``

Displays big text

##### y?, x?: `number` | `string`

##### background?, color?: `number` | `string`

##### children: `string`

#### Examples

```jsx
{new Date().toTimeString().substring(0, 8)}
```

![](media/Banner.png)

### ``

Displays vertical or horizontal bar with 1/8 character resolution

##### type: `'vertical'` | `'horizontal'`

##### y & height, x & width: `number`

#### Examples

```jsx
<>
{[...Array(24)].map((_, index) => (

))}
>
```

![](media/Bar-1.png)

Compare to ``

![](media/Bar-2.gif)

### ``

Aligns content

##### width?: `number`

##### align?: `'left'` | `'center'` | `'right'` = `'left'`

#### Examples

```jsx
left
center
right
```

![](media/Block.png)

### ``

Create a canvas for drawing with one these modes

##### mode: `{ h: 1, w: 1 }` | `{ h: 2, w: 1 }` | `{ h: 2, w: 2 }` | `{ h: 4, w: 2 }`

Pixels per character

##### height, width: `number`

Size in pixels

##### children: (`Point` | `Line`)`[]`

#### ``

Draws a point at the coordinates

##### y, x: `number`

##### color?: `number` | `string`

#### ``

Draws a line using coordinates

##### y, x, dy, dx: `number`

##### color?: `number` | `string`

#### Examples

```jsx


```

![](media/Canvas-1.png)

Braille's font demo (`{ h: 4, w: 2 }`)

![](media/Canvas-2.png)

### ``

Draws frame around its content

##### children: `string`

##### type?: `'single'` | `'double'` | `'rounded'` = `'single'`

##### height?, width?: `number`

#### Examples

```jsx
single border type
double border type
rounded border type
```

![](media/Frame.png)

### ``

Creates a list with navigation support\
Vim shortcuts are supported

##### focus?: `boolean`

##### initialPos?: { y: `number` }

##### data?: `any[]`

##### renderItem?: `(object) => JSX.Element`

##### height?, width?: `number`

##### scrollbar?: `boolean`

##### scrollbarBackground?: `boolean`

##### scrollbarColor?: `boolean`

##### vi?: `boolean` = `true`

##### pass?: `any`

##### onChange?: `(object) => void`

##### onSubmit?: `(object) => void`

#### Examples

```jsx
const items = [...Array(8)].map((_, index) => ({ id: index + 1, title: `Task ${index + 1}` }))
return (
{item.title}}
/>
)
```

![](media/List.gif)

### ``: ``

Creates a table with navigation support\
Vim shortcuts are supported

##### mode?: `'cell'` | `'row'` = `'cell'`

##### head?: `any[]`

##### renderHead?: `(object) => JSX.Element`

##### data?: `any[][]`

#### Examples

```jsx
const head = ['id', 'title']
const items = [...Array(8)].map((_, index) => [index + 1, `Task ${index + 1}`])
return (

item.map((i, key) => (

{i}

))
}
data={items}
renderItem={({ item, x, y, index }) =>
item.map((text, key) => (

{text}

))
}
/>
)
```

![](media/ListTable.gif)

### ``

Draws a scrollbar with 1/8 character resolution

##### type?: `'vertical'` | `'horizontal'` = `'vertical'`

##### offset: `number`

##### limit: `number`

##### length: `number`

##### background?, color?: `number` | `string`

#### Examples

```jsx

```

![](media/Scrollbar.png)

### ``

Draws a vertical or horizontal line

##### type: `'vertical'` | `'horizontal'`

##### height, width: `number`

#### Examples

```jsx

```

![](media/Separator.png)

### ``

Draws an animated spinner

##### children?: `string`

#### Examples

```jsx

-\|/
```

![](media/Spinner.gif)

### ``

Creates a scrollable viewport\
Vim shortcuts are supported

##### focus?: `boolean`

##### height?: `number`

##### scrollbar?: `boolean`

##### vi?: `boolean` = `true`

##### children: `any`

#### Examples

```jsx
{JSON.stringify(json, null, 2)}
```

![](media/View.gif)

## hooks

### `useAnimation`

##### (time: `number`, fps?: `'number'` = `60`) => `object`

Creates a timer for a specified duration\
That gives you time and interpolation functions each frame of animation

#### return

##### ms: `number`

##### interpolate: (from: `number`, to: `number`, delay?: `number`)

##### interpolateColor: (from: `string`, to: `string`: delay?: `number`)

#### Examples

```jsx
const { ms } = useAnimation(1000, 4)
return ms // 0, 250, 500, 750, 1000
```

```jsx
const { interpolate } = useAnimation(1000, 4)
return interpolate(0, 80) // 0, 20, 40, 60, 80
```

```jsx
const { interpolateColor } = useAnimation(1000, 4)
return interpolateColor('#000', '#0f8') // #000, #042, #084, #0c6, #0f8
```

![](media/useAnimation.gif)

#### ``

Mutate array of items to show one by one with latency

##### delay: `number`

##### children: `JSX.Element[]`

#### Examples

```jsx
const items = [...Array(8)].map((_, index) => ({ id: index + 1, title: `Task ${index + 1}` }))
return (

{items.map(({ id, title }) => (

{title}

))}

)
```

![](media/Trail.gif)

#### `useTrail`

##### (delay: `number`, items: `JSX.Element[]`, key?: `string` = `'key'`) => `JSX.Element[]`

Same as `` but hook\
You can pass it to `data` property of `` component for example

#### Examples

```jsx

```

### `useChildrenSize`

##### (value: `string`) => `object`

Gives you content size

#### return

##### height, width: `number`

#### Examples

```jsx
useChildrenSize('1\n22\n333') // { height: 3, width: 3 }
```

### `useClipboard`

#### () => `array`

Allows you to work with the system clipboard

#### return

##### getClipboard: `() => string`

##### setClipboard: `(value: string) => void`

#### Examples

```jsx
const { getClipboard, setClipboard } = useClipboard()
const string = getClipboard()
setClipboard(string.toUpperCase()) // copied
```

### `useInput`

##### (callback: `(string) => void`, dependencies: `any[]`) => `void`

Allows you to handle keyboard input

#### Examples

```jsx
set[(counter, setCounter)] = useState(0)

useInput(
input => {
if (input === 'k') setCounter(counter + 1)
if (input === 'j') setCounter(counter - 1)
},
[counter]
)
```

### `useMouse`

##### (callback: `(object) => void`, dependencies: `any[]`)

Allows you to handle mouse input

#### Examples

```jsx
set[(counter, setCounter)] = useState(0)

useMouse(
event => {
if (event.type === 'wheelup') setCounter(counter + 1)
if (event.type === 'wheeldown') setCounter(counter - 1)
},
[counter]
)
```

### `useSize`

##### () => `object`

Gives you terminal size\
Updates when size is changing

#### return

##### height, width: `number`

#### Examples

```jsx
useSize() // { height: 24, width: 80 }
```

### `useWordWrap`

##### (text: `string`, width?: `number`) => `object`

Gives your text a word wrap

#### return

##### height, width: `number`

#### Examples

```jsx
useWordWrap('hello world', 5) // hello\nworld
```

## API

### `render` (children: `JSX.Element`) => `void`

Renders your fullscreen application to `stdout`

### `inline` (children: `JSX.Element`) => `void`

Renders your inline application to `stdout`

### `bell`

#### () => `void`

Makes a terminal bell

```jsx
bell() // ding
```

### `exit`

##### (code: `number` = `0`) => `void`

Allows you to exit from an application that waits for user input or has timers

#### Examples

```jsx
useInput(input => {
if (input === 'q') exit()
})
```