react-stream-renderer v0.2.0
react-stream-renderer
Prototype of Haul CLI. You can see the source here.
Install
yarn add react-stream-renderer
Usage
import React from 'react';
import { render, View, makeTTYAdapter } from 'react-stream-renderer';
class App extends React.Component {
render() {
return (
<View style={{ color: 'green' }}>
Hello world!
</View>
);
}
}
render(<App />, makeTTYAdapter(process.stdout).makeEffects());
API
Functions
render(element: React.Element, adapter: BaseAdapter): () => void
Render given element using a given adapter (makeTTYAdapter
for TTY streams or makeTestAdapter
for testing purposes).
Returns forceRender
function which triggers full re-render.
makeTTYAdapter(ttyStream: NodeTTYStream): TTYAdapter
Creates an adapter a TTY Node stream.
Use chainable API from TTYAdapter
for configuring the behavior:
withCustomConsole(options: OverwriteConsoleOptions): TTYAdapter
Redirect console output to specified streams or files.
This method won't have any effect unless makeEffects
is called.
options: OverwriteConsoleOptions
:
exitOnWarning: boolean
- Exit on first call toconsole.warning
.exitOnError: boolean
- Exit on first call toconsole.error
.outStream: boolean | string | NodeWritableStream
- Redirect console output to:stdout.log
iftrue
- custom file if
string
with path is supplied - custom Node stream if writable node stream is supplied
errStream: boolean | string | NodeWritableStream
- Redirect console error output to:stderr.log
iftrue
- custom file if
string
with path is supplied - custom Node stream if writable node stream is supplied
hideCursor(): TTYAdapter
Hides cursor.
This method won't have any effect unless makeEffects
is called.
clearOnExit(shouldClearScrollback: boolean = false): TTYAdapter
Clear screen or scrollback (if shouldClearScrollback
is true
) when process is about to exit.
This method won't have any effect unless makeEffects
is called.
clearOnError(): TTYAdapter
Clear screen (scrollback will be untouched) when process is about to exit due to an error.
This method won't have any effect unless makeEffects
is called.
makeEffects(): TTYAdapter
Perform accumulated side effects. This method must always be called!
Example
render(
<View>Hello world</View>,
makeTTYAdapter(process.stdout)
.withCustomConsole({ outStream: true, errStream: true })
.hideCursor()
.clearOnExit(true)
.makeEffects()
);
makeTestAdapter(options: Options): TestAdapter
Creates an adapter for testing. You can provide hooks to assert the rendered content.
options: Options
height: number = 40
- Canvas height (default:40
)width: number = 80
- Canvas width (default:80
)onPrint?: (data: string) => void
- Testing hook executed on each render,data
will be a string with full rendered content.onClear: () => void
- Testing hook executed when canvas should be cleared.onSetCursorPosition: (x: number, y: number) => void
- Testing hook executed when cursor should be changed.
Example
test('render should draw content', () => {
const onDraw = jest.fn();
const adapter = makeTestAdapter({ onDraw });
render(<View>Test</View>, adapter);
expect(onDraw).toHaveBeenCalledWith('Test');
});
Components
View
(aka Text
)
Basic building block. Can render text (strings), arrays and other nested components.
Text
component is exposed for compatibility and it's the same as View
.
Props
style?: Style
- Object with Style properties
Example
class App extends React.Component {
render() {
return (
<View>
<View style={styles.title}>Hello world!</View>
Today is {new Date().toLocaleString()}
</View>
);
}
}
const styles = {
title: {
margin: '0 1',
color: 'green',
},
};
KeyPress
Attach onPress
callback to keypress
event. This component by itself doesn't render anything, but it can render passed children
.
Props
stream: ReadableStream
- (Node) Readable stream to readkeypress
events from.onPress(char: string, key: Key): void
- Callback to attach tokeypress
events.type Key = { name: string, ctrl: boolean, shift: boolean, alt: boolean, };
Example
import React from 'react';
import { KeyPress, Text } from 'react-stream-renderer';
class MyComponent extends React.Component {
_onPress = (char, key) => {
console.log(char, key);
}
render() {
return (
<KeyPress stream={process.stdin} onPress={this._onPress}>
<Text>Hello Wolrd!</Text>
</KeyPress>
);
}
}
ProgressBar
Uncontrolled component with progress bar.
Props
value: number
- Progress value normalized between 0 and 1 (values above 1 will be treated as 1).barWidth: number
- Width of progress bar includingopen
andclose
characters.chars?: Chars
- Characters and their styling to use when rendering progress bar.type CharProps = { char?: string, style?: Style, }; type Chars = { open?: CharProps, // default char: '[' close?: CharProps, // default char: ']' bar?: CharProps, // default char: '=' fill?: CharProps, // default char: ' ' };
style?: Style
- Object with Style properties
Example
import React from 'react';
import { ProgressBar, Text } from 'react-stream-renderer';
class MyComponent extends React.Component {
render() {
return (
<Text>
<ProgressBar
value={0.4}
barWidth={40}
chars={{
bar: { char: '#' },
fill: { char: '-' },
open: { style: { color: 'blue' } },
close: { style: { color: 'blue' } },
}}
/>
{' Some progress'}
</Text>
);
}
}
Spinner
Controlled spinner component based on cli-spinners
.
Props
type?: string
-cli-spinners
's type (default:dots
).interval?: number
- Interval between rendering next spinner frames (be default it is taken from specificcli-spinners
animation).frames?: string[]
- Custom animation frames.style?: Style
- Object with Style properties
Example
import React from 'react';
import { Spinner, Text } from 'react-stream-renderer';
class MyComponent extends React.Component {
render() {
return (
<Text>
<Spinner />
<Spinner type="line" />
<Spinner interval={250} />
<Spinner interval={80} frames={['-', '|', '_', '|']} />
</Text>
);
}
}
Styling and layout
Layout
Positioning
react-stream-renderer
supports both relative (default) and fixed positioning of elements. Use position: 'fixed'
(and optionally zIndex
, left
and right
) to
make the element fixed to specific position.
Display (block vs inline)
react-stream-renderer
supports both block
and inline
display of elements.
By default all <Text>
components have display: 'block'
set, whereas all inline text (usually strings) have display: inline
.
Example:
<Text>A</Text>
<Text>B</Text>
renders:
A
B
but both
<Text>A</Text>
{'B'}
and:
<Text style={{ display: 'inline' }}>A</Text>
<Text style={{ display: 'inline' }}>B</Text>
renders:
AB
Style properties
react-stream-renderer
supports every color that chalk
does.
type Style = {|
color?: string, // eg: `blue`, `rgb(255, 124, 77)`, `#ffffff`
backgroundColor?: string, // eg: `blue`, `rgb(255, 124, 77)`, `#ffffff`
fontWeight?: 'bold' | 'normal',
fontStyle?: 'italic' | 'normal',
textDecoration?: 'underline' | 'line-through' | 'normal',
textTransform?: 'none' | 'capitalize' | 'uppercase' | 'lowercase',
visibility?: 'visible' | 'hidden',
margin?: string, // <top> <right> <bottom> <left> eg: `2 1 0 3`, `1 2 3`, `2 1`, `1`
marginTop?: number,
marginBottom?: number,
marginLeft?: number,
marginRight?: number,
padding?: string, // <top> <right> <bottom> <left> eg: `2 1 0 3`, `1 2 3`, `2 1`, `1`
paddingTop?: number,
paddingBottom?: number,
paddingLeft?: number,
paddingRight?: number,
height?: number,
width?: number,
display?: 'block' | 'inline',
border?: string,
borderStyle?: 'none' | 'solid' | 'double',
borderColor?: string,
textAlign?: 'left' | 'center' | 'right',
zIndex?: number, // supports both negative, 0 and positive values
position?: 'relative' | 'fixed',
left?: number,
top?: number,
|};