1 | <h1 align="center">
|
2 | <br>
|
3 | <br>
|
4 | <img width="300" alt="Ink" src="media/logo.png">
|
5 | <br>
|
6 | <br>
|
7 | <br>
|
8 | </h1>
|
9 |
|
10 | > React for CLIs. Build and test your CLI output using components.
|
11 |
|
12 | [![Build Status](https://travis-ci.org/vadimdemedes/ink.svg?branch=master)](https://travis-ci.org/vadimdemedes/ink)
|
13 |
|
14 |
|
15 | ## Install
|
16 |
|
17 | ```
|
18 | $ npm install ink react
|
19 | ```
|
20 |
|
21 |
|
22 | ## Usage
|
23 |
|
24 | ```jsx
|
25 | import React, {Component} from 'react';
|
26 | import {render, Color} from 'ink';
|
27 |
|
28 | class Counter extends Component {
|
29 | constructor() {
|
30 | super();
|
31 |
|
32 | this.state = {
|
33 | i: 0
|
34 | };
|
35 | }
|
36 |
|
37 | render() {
|
38 | return (
|
39 | <Color green>
|
40 | {this.state.i} tests passed
|
41 | </Color>
|
42 | );
|
43 | }
|
44 |
|
45 | componentDidMount() {
|
46 | this.timer = setInterval(() => {
|
47 | this.setState({
|
48 | i: this.state.i + 1
|
49 | });
|
50 | }, 100);
|
51 | }
|
52 |
|
53 | componentWillUnmount() {
|
54 | clearInterval(this.timer);
|
55 | }
|
56 | }
|
57 |
|
58 | render(<Counter/>);
|
59 | ```
|
60 |
|
61 | <img src="media/demo.svg" width="600">
|
62 |
|
63 | You can also check it out live on [repl.it sandbox](https://ink-counter-demo.vadimdemedes.repl.run/).
|
64 | Feel free to play around with the code and fork this repl at [https://repl.it/@vadimdemedes/ink-counter-demo](https://repl.it/@vadimdemedes/ink-counter-demo).
|
65 |
|
66 |
|
67 | ## Built with Ink
|
68 |
|
69 | - [emoj](https://github.com/sindresorhus/emoj) - Find relevant emoji on the command-line.
|
70 | - [emma](https://github.com/maticzav/emma-cli) - Terminal assistant to find and install npm packages.
|
71 | - [swiff](https://github.com/simple-integrated-marketing/swiff) - Multi-environment command line tools for time-saving web developers.
|
72 | - [changelog-view](https://github.com/jdeniau/changelog-view) - Tool view changelog in console.
|
73 |
|
74 |
|
75 | ## Contents
|
76 |
|
77 | - [Getting Started](#getting-started)
|
78 | - [Examples](#examples)
|
79 | - [API](#api)
|
80 | - [Building Layouts](#building-layouts)
|
81 | - [Built-in Components](#built-in-components)
|
82 | - [Useful Components](#useful-components)
|
83 | - [Testing](#testing)
|
84 |
|
85 |
|
86 | ## Getting Started
|
87 |
|
88 | Ink's goal is to provide the same component-based UI building experience that React provides, but for command-line apps. It uses [yoga-layout](https://github.com/facebook/yoga) to allow Flexbox layouts in the terminal. If you are already familiar with React, you already know Ink.
|
89 |
|
90 | The key difference you have to remember is that the rendering result isn't a DOM, but a string, which Ink writes to the output.
|
91 |
|
92 | To ensure all examples work and you can begin your adventure with Ink, make sure to set up Babel with a React preset. After [installing Babel](https://babeljs.io/docs/en/usage), configure it in `package.json`:
|
93 |
|
94 | ```json
|
95 | {
|
96 | "babel": {
|
97 | "presets": [
|
98 | "@babel/preset-react"
|
99 | ]
|
100 | }
|
101 | }
|
102 | ```
|
103 |
|
104 | Don't forget to import `React` into every file that contains JSX:
|
105 |
|
106 | ```jsx
|
107 | import React from 'react';
|
108 | import {render, Box} from 'ink';
|
109 |
|
110 | const Demo = () => (
|
111 | <Box>
|
112 | Hello World
|
113 | </Box>
|
114 | );
|
115 |
|
116 | render(<Demo/>);
|
117 | ```
|
118 |
|
119 |
|
120 | ## Examples
|
121 |
|
122 | - [Jest](examples/jest/jest.js) - Implementation of basic Jest UI [(live demo)](https://ink-jest-demo.vadimdemedes.repl.run/).
|
123 | - [Counter](examples/counter/counter.js) - Simple counter that increments every 100ms [(live demo)](https://ink-counter-demo.vadimdemedes.repl.run/).
|
124 |
|
125 |
|
126 | ## API
|
127 |
|
128 | Since Ink is a React renderer, it means that all features of React are supported.
|
129 | Head over to [React](https://reactjs.org) website for documentation on how to use it.
|
130 | In this readme only Ink's methods will be documented.
|
131 |
|
132 | #### render(tree, options)
|
133 |
|
134 | Returns: `Instance`
|
135 |
|
136 | Mount a component and render the output.
|
137 |
|
138 | ##### tree
|
139 |
|
140 | Type: `ReactElement`
|
141 |
|
142 | ##### options
|
143 |
|
144 | Type: `Object`
|
145 |
|
146 | ###### stdout
|
147 |
|
148 | Type: `stream.Writable`<br>
|
149 | Default: `process.stdout`
|
150 |
|
151 | Output stream where app will be rendered.
|
152 |
|
153 | ###### stdin
|
154 |
|
155 | Type: `stream.Readable`<br>
|
156 | Default: `process.stdin`
|
157 |
|
158 | Input stream where app will listen for input.
|
159 |
|
160 | ###### exitOnCtrlC
|
161 |
|
162 | Type: `boolean`<br>
|
163 | Default: `true`
|
164 |
|
165 | Configure whether Ink should listen to Ctrl+C keyboard input and exit the app.
|
166 | This is needed in case `process.stdin` is in [raw mode](https://nodejs.org/api/tty.html#tty_readstream_setrawmode_mode), because then Ctrl+C is ignored by default and process is expected to handle it manually.
|
167 |
|
168 | ###### debug
|
169 |
|
170 | Type: `boolean`<br>
|
171 | Default: `false`
|
172 |
|
173 | If `true`, each update will be rendered as a separate output, without replacing the previous one.
|
174 |
|
175 | ```jsx
|
176 | import React, {Component} from 'react';
|
177 | import {render, Box} from 'ink';
|
178 |
|
179 | class Counter extends Component {
|
180 | constructor() {
|
181 | super();
|
182 |
|
183 | this.state = {
|
184 | i: 0
|
185 | };
|
186 | }
|
187 |
|
188 | render() {
|
189 | return (
|
190 | <Box>
|
191 | Iteration #{this.state.i}
|
192 | </Box>
|
193 | );
|
194 | }
|
195 |
|
196 | componentDidMount() {
|
197 | this.timer = setInterval(() => {
|
198 | this.setState(prevState => ({
|
199 | i: prevState.i + 1
|
200 | }));
|
201 | }, 100);
|
202 | }
|
203 |
|
204 | componentWillUnmount() {
|
205 | clearInterval(this.timer);
|
206 | }
|
207 | }
|
208 |
|
209 | const app = render(<Counter/>);
|
210 |
|
211 | setTimeout(() => {
|
212 | // Enough counting
|
213 | app.unmount();
|
214 | }, 1000);
|
215 | ```
|
216 |
|
217 | There's also a shortcut to avoid passing `options` object:
|
218 |
|
219 | ```jsx
|
220 | render(<Counter>, process.stdout);
|
221 | ```
|
222 |
|
223 | #### Instance
|
224 |
|
225 | This is the object that `render()` returns.
|
226 |
|
227 | ##### rerender
|
228 |
|
229 | Replace previous root node with a new one or update props of the current root node.
|
230 |
|
231 | ```jsx
|
232 | // Update props of the root node
|
233 | const {rerender} = render(<Counter count={1}/>);
|
234 | rerender(<Counter count={2}/>);
|
235 |
|
236 | // Replace root node
|
237 | const {rerender} = render(<OldCounter/>);
|
238 | rerender(<NewCounter/>);
|
239 | ```
|
240 |
|
241 | ##### unmount
|
242 |
|
243 | Manually unmount the whole Ink app.
|
244 |
|
245 | ```jsx
|
246 | const {unmount} = render(<MyApp/>);
|
247 | unmount();
|
248 | ```
|
249 |
|
250 | ##### waitUntilExit
|
251 |
|
252 | Returns a promise, which resolves when app is unmounted.
|
253 |
|
254 | ```jsx
|
255 | const {unmount, waitUntilExit} = render(<MyApp/>);
|
256 |
|
257 | setTimeout(unmount, 1000);
|
258 |
|
259 | await waitUntilExit(); // resolves after `unmount()` is called
|
260 | ```
|
261 |
|
262 | ## Building Layouts
|
263 |
|
264 | Ink uses [Yoga](https://github.com/facebook/yoga) - a Flexbox layout engine to build great user interfaces for your CLIs.
|
265 | It's important to remember that each element is a Flexbox container.
|
266 | Think of it as if each `<div>` in the browser had `display: flex`.
|
267 | See `<Box>` built-in component below for documentation on how to use Flexbox layouts in Ink.
|
268 |
|
269 |
|
270 | ### Built-in Components
|
271 |
|
272 | #### `<Box>`
|
273 |
|
274 | `<Box>` it's an essential Ink component to build your layout. It's like a `<div style="display: flex">` in a browser.
|
275 |
|
276 | Import:
|
277 |
|
278 | ```js
|
279 | import {Box} from 'ink';
|
280 | ```
|
281 |
|
282 | ##### Dimensions
|
283 |
|
284 | ###### width
|
285 |
|
286 | Type: `number`, `string`
|
287 |
|
288 | Width of the element in spaces. You can also set it in percent, which will calculate the width based on the width of parent element.
|
289 |
|
290 | ```jsx
|
291 | <Box width={4}>X</Box> //=> 'X '
|
292 | ```
|
293 |
|
294 | ```jsx
|
295 | <Box width={10}>
|
296 | <Box width="50%">X</Box>
|
297 | Y
|
298 | </Box> //=> 'X Y'
|
299 | ```
|
300 |
|
301 | ###### height
|
302 |
|
303 | Type: `number`, `string`
|
304 |
|
305 | Height of the element in lines (rows). You can also set it in percent, which will calculate the height based on the height of parent element.
|
306 |
|
307 | ```jsx
|
308 | <Box height={4}>X</Box> //=> 'X\n\n\n'
|
309 | ```
|
310 |
|
311 | ```jsx
|
312 | <Box height={6} flexDirection="column">
|
313 | <Box height="50%">X</Box>
|
314 | Y
|
315 | </Box> //=> 'X\n\n\nY\n\n'
|
316 | ```
|
317 |
|
318 | ###### minWidth
|
319 |
|
320 | Type: `number`
|
321 |
|
322 | Sets a minimum width of the element. Percentages aren't supported yet, see https://github.com/facebook/yoga/issues/872.
|
323 |
|
324 | ###### minHeight
|
325 |
|
326 | Type: `number`
|
327 |
|
328 | Sets a minimum height of the element. Percentages aren't supported yet, see https://github.com/facebook/yoga/issues/872.
|
329 |
|
330 | ##### Wrapping
|
331 |
|
332 | ###### textWrap
|
333 |
|
334 | Type: `string`<br>
|
335 | Values: `wrap` `truncate` `truncate-start` `truncate-middle` `truncate-end`
|
336 |
|
337 | This property tells Ink to wrap or truncate text content of `<Box>` if its width is larger than container. If `wrap` is passed, Ink will wrap text and split it into multiple lines. If `truncate-*` is passed, Ink will truncate text instead, which will result in one line of text with the rest cut off.
|
338 |
|
339 | *Note:* Ink doesn't wrap text by default.
|
340 |
|
341 | ```jsx
|
342 | <Box textWrap="wrap">Hello World</Box>
|
343 | //=> 'Hello\nWorld'
|
344 |
|
345 | // `truncate` is an alias to `truncate-end`
|
346 | <Box textWrap="truncate">Hello World</Box>
|
347 | //=> 'Hello…'
|
348 |
|
349 | <Box textWrap="truncate-middle">Hello World</Box>
|
350 | //=> 'He…ld'
|
351 |
|
352 | <Box textWrap="truncate-start">Hello World</Box>
|
353 | //=> '…World'
|
354 | ```
|
355 |
|
356 | ##### Padding
|
357 |
|
358 | ###### paddingTop
|
359 |
|
360 | Type: `number`<br>
|
361 | Default: `0`
|
362 |
|
363 | ###### paddingBottom
|
364 |
|
365 | Type: `number`<br>
|
366 | Default: `0`
|
367 |
|
368 | ###### paddingLeft
|
369 |
|
370 | Type: `number`<br>
|
371 | Default: `0`
|
372 |
|
373 | ###### paddingRight
|
374 |
|
375 | Type: `number`<br>
|
376 | Default: `0`
|
377 |
|
378 | ###### paddingX
|
379 |
|
380 | Type: `number`<br>
|
381 | Default: `0`
|
382 |
|
383 | ###### paddingY
|
384 |
|
385 | Type: `number`<br>
|
386 | Default: `0`
|
387 |
|
388 | ###### padding
|
389 |
|
390 | Type: `number`<br>
|
391 | Default: `0`
|
392 |
|
393 | ```jsx
|
394 | <Box paddingTop={2}>Top</Box>
|
395 | <Box paddingBottom={2}>Bottom</Box>
|
396 | <Box paddingLeft={2}>Left</Box>
|
397 | <Box paddingRight={2}>Right</Box>
|
398 | <Box paddingX={2}>Left and right</Box>
|
399 | <Box paddingY={2}>Top and bottom</Box>
|
400 | <Box padding={2}>Top, bottom, left and right</Box>
|
401 | ```
|
402 |
|
403 | ##### Margin
|
404 |
|
405 | ###### marginTop
|
406 |
|
407 | Type: `number`<br>
|
408 | Default: `0`
|
409 |
|
410 | ###### marginBottom
|
411 |
|
412 | Type: `number`<br>
|
413 | Default: `0`
|
414 |
|
415 | ###### marginLeft
|
416 |
|
417 | Type: `number`<br>
|
418 | Default: `0`
|
419 |
|
420 | ###### marginRight
|
421 |
|
422 | Type: `number`<br>
|
423 | Default: `0`
|
424 |
|
425 | ###### marginX
|
426 |
|
427 | Type: `number`<br>
|
428 | Default: `0`
|
429 |
|
430 | ###### marginY
|
431 |
|
432 | Type: `number`<br>
|
433 | Default: `0`
|
434 |
|
435 | ###### margin
|
436 |
|
437 | Type: `number`<br>
|
438 | Default: `0`
|
439 |
|
440 | ```jsx
|
441 | <Box marginTop={2}>Top</Box>
|
442 | <Box marginBottom={2}>Bottom</Box>
|
443 | <Box marginLeft={2}>Left</Box>
|
444 | <Box marginRight={2}>Right</Box>
|
445 | <Box marginX={2}>Left and right</Box>
|
446 | <Box marginY={2}>Top and bottom</Box>
|
447 | <Box margin={2}>Top, bottom, left and right</Box>
|
448 | ```
|
449 |
|
450 | ##### Flex
|
451 |
|
452 | ###### flexGrow
|
453 |
|
454 | Type: `number`<br>
|
455 | Default: `0`
|
456 |
|
457 | See [flex-grow](https://css-tricks.com/almanac/properties/f/flex-grow/).
|
458 |
|
459 | ```jsx
|
460 | <Box>
|
461 | Label:
|
462 | <Box flexGrow={1}>
|
463 | Fills all remaining space
|
464 | </Box>
|
465 | </Box>
|
466 | ```
|
467 |
|
468 | ###### flexShrink
|
469 |
|
470 | Type: `number`<br>
|
471 | Default: `1`
|
472 |
|
473 | See [flex-shrink](https://css-tricks.com/almanac/properties/f/flex-shrink/).
|
474 |
|
475 | ```jsx
|
476 | <Box width={20}>
|
477 | <Box flexShrink={2} width={10}>
|
478 | Will be 1/4
|
479 | </Box>
|
480 | <Box width={10}>
|
481 | Will be 3/4
|
482 | </Box>
|
483 | </Box>
|
484 | ```
|
485 |
|
486 | ###### flexBasis
|
487 |
|
488 | Type: `number`, `string`<br>
|
489 |
|
490 | See [flex-basis](https://css-tricks.com/almanac/properties/f/flex-basis/).
|
491 |
|
492 | ```jsx
|
493 | <Box width={6}>
|
494 | <Box flexBasis={3}>X</Box>
|
495 | Y
|
496 | </Box> //=> 'X Y'
|
497 | ```
|
498 |
|
499 | ```jsx
|
500 | <Box width={6}>
|
501 | <Box flexBasis="50%">X</Box>
|
502 | Y
|
503 | </Box> //=> 'X Y'
|
504 | ```
|
505 |
|
506 | ###### flexDirection
|
507 |
|
508 | Type: `string`<br>
|
509 | Allowed values: `row`, `row-reverse`, `column` and `column-reverse`
|
510 |
|
511 | See [flex-direction](https://css-tricks.com/almanac/properties/f/flex-direction/).
|
512 |
|
513 | ```jsx
|
514 | <Box>
|
515 | <Box marginRight={1}>X</Box>
|
516 | <Box>Y</Box>
|
517 | </Box>
|
518 | // X Y
|
519 |
|
520 | <Box flexDirection="row-reverse">
|
521 | <Box>X</Box>
|
522 | <Box marginRight={1}>Y</Box>
|
523 | </Box>
|
524 | // Y X
|
525 |
|
526 | <Box flexDirection="column">
|
527 | <Box>X</Box>
|
528 | <Box>Y</Box>
|
529 | </Box>
|
530 | // X
|
531 | // Y
|
532 |
|
533 | <Box flexDirection="column-reverse">
|
534 | <Box>X</Box>
|
535 | <Box>Y</Box>
|
536 | </Box>
|
537 | // Y
|
538 | // X
|
539 | ```
|
540 |
|
541 | ###### alignItems
|
542 |
|
543 | Type: `string`<br>
|
544 | Allowed values: `flex-start`, `center` and `flex-end`
|
545 |
|
546 | See [align-items](https://css-tricks.com/almanac/properties/f/align-items/).
|
547 |
|
548 | ```jsx
|
549 | <Box alignItems="flex-start">
|
550 | <Box marginRight={1}>X</Box>
|
551 | <Box>{`A\nB\nC`}</Box>
|
552 | </Box>
|
553 | // X A
|
554 | // B
|
555 | // C
|
556 |
|
557 | <Box alignItems="center">
|
558 | <Box marginRight={1}>X</Box>
|
559 | <Box>{`A\nB\nC`}</Box>
|
560 | </Box>
|
561 | // A
|
562 | // X B
|
563 | // C
|
564 |
|
565 | <Box alignItems="flex-end">
|
566 | <Box marginRight={1}>X</Box>
|
567 | <Box>{`A\nB\nC`}</Box>
|
568 | </Box>
|
569 | // A
|
570 | // B
|
571 | // X C
|
572 | ```
|
573 |
|
574 | ###### justifyContent
|
575 |
|
576 | Type: `string`<br>
|
577 | Allowed values: `flex-start`, `center`, `flex-end`, `space-between` and `space-around`.
|
578 |
|
579 | See [justify-content](https://css-tricks.com/almanac/properties/f/justify-content/).
|
580 |
|
581 | ```jsx
|
582 | <Box justifyContent="flex-start">
|
583 | <Box>X</Box>
|
584 | </Box>
|
585 | // [X ]
|
586 |
|
587 | <Box justifyContent="center">
|
588 | <Box>X</Box>
|
589 | </Box>
|
590 | // [ X ]
|
591 |
|
592 | <Box justifyContent="flex-end">
|
593 | <Box>X</Box>
|
594 | </Box>
|
595 | // [ X]
|
596 |
|
597 | <Box justifyContent="space-between">
|
598 | <Box>X</Box>
|
599 | <Box>Y</Box>
|
600 | </Box>
|
601 | // [X Y]
|
602 |
|
603 | <Box justifyContent="space-around">
|
604 | <Box>X</Box>
|
605 | <Box>Y</Box>
|
606 | </Box>
|
607 | // [ X Y ]
|
608 | ```
|
609 |
|
610 |
|
611 | #### `<Color>`
|
612 |
|
613 | The `<Color>` component is a simple wrapper around [the `chalk` API](https://github.com/chalk/chalk#api).
|
614 | It supports all of the chalk's methods as `props`.
|
615 |
|
616 | Import:
|
617 |
|
618 | ```js
|
619 | import {Color} from 'ink';
|
620 | ```
|
621 |
|
622 | Usage:
|
623 |
|
624 | ```jsx
|
625 | <Color rgb={[255, 255, 255]} bgKeyword="magenta">
|
626 | Hello!
|
627 | </Color>
|
628 |
|
629 | <Color hex="#000000" bgHex="#FFFFFF">
|
630 | Hey there
|
631 | </Color>
|
632 |
|
633 | <Color blue>
|
634 | I'm blue
|
635 | </Color>
|
636 | ```
|
637 |
|
638 | #### `<Text>`
|
639 |
|
640 | This component can change the style of the text, make it bold, underline, italic or strikethrough.
|
641 |
|
642 | Import:
|
643 |
|
644 | ```js
|
645 | import {Text} from 'ink';
|
646 | ```
|
647 |
|
648 | ##### bold
|
649 |
|
650 | Type: `boolean`<br>
|
651 | Default: `false`
|
652 |
|
653 | ##### italic
|
654 |
|
655 | Type: `boolean`<br>
|
656 | Default: `false`
|
657 |
|
658 | ##### underline
|
659 |
|
660 | Type: `boolean`<br>
|
661 | Default: `false`
|
662 |
|
663 | ##### strikethrough
|
664 |
|
665 | Type: `boolean`<br>
|
666 | Default: `false`
|
667 |
|
668 | Usage:
|
669 |
|
670 | ```jsx
|
671 | <Text bold>I am bold</Text>
|
672 | <Text italic>I am italic</Text>
|
673 | <Text underline>I am underline</Text>
|
674 | <Text strikethrough>I am strikethrough</Text>
|
675 | ```
|
676 |
|
677 | #### `<Static>`
|
678 |
|
679 | `<Static>` component allows permanently rendering output to stdout and preserving it across renders.
|
680 | Components passed to `<Static>` as children will be written to stdout only once and will never be rerendered.
|
681 | `<Static>` output comes first, before any other output from your components, no matter where it is in the tree.
|
682 | In order for this mechanism to work properly, at most one `<Static>` component must be present in your node tree and components that were rendered must never update their output. Ink will detect new children appended to `<Static>` and render them to stdout.
|
683 |
|
684 | **Note:** `<Static>` accepts only an array of children and each of them must have a unique key.
|
685 |
|
686 | Example use case for this component is Jest's output:
|
687 |
|
688 | ![](https://jestjs.io/img/content/feature-fast.png)
|
689 |
|
690 | Jest continuously writes the list of completed tests to the output, while updating test results at the bottom of the output in real-time. Here's how this user interface could be implemented with Ink:
|
691 |
|
692 | ```jsx
|
693 | <>
|
694 | <Static>
|
695 | {tests.map(test => (
|
696 | <Test key={test.id} title={test.title}/>
|
697 | ))}
|
698 | </Static>
|
699 |
|
700 | <Box marginTop={1}>
|
701 | <TestResults passed={results.passed} failed={results.failed}/>
|
702 | </Box>
|
703 | </>
|
704 | ```
|
705 |
|
706 | See [examples/jest](examples/jest/jest.js) for a basic implementation of Jest's UI.
|
707 |
|
708 | #### `<AppContext>`
|
709 |
|
710 | `<AppContext>` is a [React context](https://reactjs.org/docs/context.html#reactcreatecontext), which exposes a method to manually exit the app (unmount).
|
711 |
|
712 | Import:
|
713 |
|
714 | ```js
|
715 | import {AppContext} from 'ink';
|
716 | ```
|
717 |
|
718 | ##### exit
|
719 |
|
720 | Type: `Function`
|
721 |
|
722 | Exit (unmount) the whole Ink app.
|
723 |
|
724 | Usage:
|
725 |
|
726 | ```jsx
|
727 | <AppContext.Consumer>
|
728 | {({ exit }) => (
|
729 | {/* Calling `onExit()` from within <MyApp> will unmount the app */}
|
730 | <MyApp onExit={exit}/>
|
731 | )}
|
732 | </AppContext.Consumer>
|
733 | ```
|
734 |
|
735 | If `exit` is called with an Error, `waitUntilExit` will reject with that error.
|
736 |
|
737 | #### `<StdinContext>`
|
738 |
|
739 | `<StdinContext>` is a [React context](https://reactjs.org/docs/context.html#reactcreatecontext), which exposes input stream.
|
740 |
|
741 | Import:
|
742 |
|
743 | ```js
|
744 | import {StdinContext} from 'ink';
|
745 | ```
|
746 |
|
747 | ##### stdin
|
748 |
|
749 | Type: `stream.Readable`<br>
|
750 | Default: `process.stdin`
|
751 |
|
752 | Stdin stream passed to `render()` in `options.stdin` or `process.stdin` by default.
|
753 | Useful if your app needs to handle user input.
|
754 |
|
755 | Usage:
|
756 |
|
757 | ```jsx
|
758 | <StdinContext.Consumer>
|
759 | {({ stdin }) => (
|
760 | <MyComponent stdin={stdin}/>
|
761 | )}
|
762 | </StdinContext.Consumer>
|
763 | ```
|
764 |
|
765 | ##### setRawMode
|
766 |
|
767 | Type: `function`<br>
|
768 |
|
769 | See [`setRawMode`](https://nodejs.org/api/tty.html#tty_readstream_setrawmode_mode).
|
770 | Ink exposes this function via own `<StdinContext>` to be able to handle <kbd>Ctrl</kbd>+<kbd>C</kbd>, that's why you should use Ink's `setRawMode` instead of `process.stdin.setRawMode`. Ink also enables `keypress` events via [`readline.emitKeypressEvents()`](https://nodejs.org/api/readline.html#readline_readline_emitkeypressevents_stream_interface) when raw mode is enabled.
|
771 |
|
772 | Usage:
|
773 |
|
774 | ```jsx
|
775 | <StdinContext.Consumer>
|
776 | {({ setRawMode }) => (
|
777 | <MyComponent setRawMode={setRawMode}/>
|
778 | )}
|
779 | </StdinContext.Consumer>
|
780 | ```
|
781 |
|
782 | #### `<StdoutContext>`
|
783 |
|
784 | `<StdoutContext>` is a [React context](https://reactjs.org/docs/context.html#reactcreatecontext), which exposes stdout stream, where Ink renders your app.
|
785 |
|
786 | Import:
|
787 |
|
788 | ```js
|
789 | import {StdoutContext} from 'ink';
|
790 | ```
|
791 |
|
792 | ##### stdout
|
793 |
|
794 | Type: `stream.Writable`<br>
|
795 | Default: `process.stdout`
|
796 |
|
797 | Usage:
|
798 |
|
799 | ```jsx
|
800 | <StdoutContext.Consumer>
|
801 | {({ stdout }) => (
|
802 | <MyComponent stdout={stdout}/>
|
803 | )}
|
804 | </StdoutContext.Consumer>
|
805 | ```
|
806 |
|
807 |
|
808 | ## Useful Components
|
809 |
|
810 | - [ink-text-input](https://github.com/vadimdemedes/ink-text-input) - Text input.
|
811 | - [ink-spinner](https://github.com/vadimdemedes/ink-spinner) - Spinner.
|
812 | - [ink-select-input](https://github.com/vadimdemedes/ink-select-input) - Select (dropdown) input.
|
813 | - [ink-link](https://github.com/sindresorhus/ink-link) - Link component.
|
814 | - [ink-box](https://github.com/sindresorhus/ink-box) - Styled box component.
|
815 | - [ink-gradient](https://github.com/sindresorhus/ink-gradient) - Gradient color component.
|
816 | - [ink-big-text](https://github.com/sindresorhus/ink-big-text) - Awesome text component.
|
817 | - [ink-image](https://github.com/kevva/ink-image) - Display images inside the terminal.
|
818 | - [ink-tab](https://github.com/jdeniau/ink-tab) - Tab component.
|
819 | - [ink-color-pipe](https://github.com/LitoMore/ink-color-pipe) - Create color text with simpler style strings in Ink.
|
820 |
|
821 | ### Incompatible components
|
822 |
|
823 | These are components that haven't migrated to Ink 2 yet:
|
824 |
|
825 | - [ink-progress-bar](https://github.com/brigand/ink-progress-bar) - Configurable component for rendering progress bars.
|
826 | - [ink-console](https://github.com/ForbesLindesay/ink-console) - Render output from `console[method]` calls in a scrollable panel.
|
827 | - [ink-confirm-input](https://github.com/kevva/ink-confirm-input) - Yes/No confirmation input.
|
828 | - [ink-checkbox-list](https://github.com/MaxMEllon/ink-checkbox-list) - Checkbox.
|
829 | - [ink-quicksearch](https://github.com/aicioara/ink-quicksearch) - Select Component with fast quicksearch-like navigation
|
830 | - [ink-autocomplete](https://github.com/maticzav/ink-autocomplete) - Autocomplete.
|
831 | - [ink-table](https://github.com/maticzav/ink-table) - Table component.
|
832 | - [ink-broadcast](https://github.com/jimmed/ink-broadcast) - Implementation of react-broadcast for Ink.
|
833 | - [ink-router](https://github.com/jimmed/ink-router) - Implementation of react-router for Ink.
|
834 | - [ink-select](https://github.com/karaggeorge/ink-select) - Select component.
|
835 | - [ink-scrollbar](https://github.com/karaggeorge/ink-scrollbar) - Scrollbar component.
|
836 | - [ink-text-animation](https://github.com/yardnsm/ink-text-animation) - Text animation component.
|
837 | - [ink-figlet](https://github.com/KimotoYanke/ink-figlet) - Large text component with Figlet fonts.
|
838 | - [ink-divider](https://github.com/JureSotosek/ink-divider) - A divider component.
|
839 |
|
840 |
|
841 | ## Testing
|
842 |
|
843 | Ink components are simple to test with [ink-testing-library](https://github.com/vadimdemedes/ink-testing-library).
|
844 | Here's a simple example that checks how component is rendered:
|
845 |
|
846 | ```jsx
|
847 | import React from 'react';
|
848 | import {Text} from 'ink';
|
849 | import {render} from 'ink-testing-library';
|
850 |
|
851 | const Test = () => <Text>Hello World</Text>;
|
852 | const {lastFrame} = render(<Test/>);
|
853 |
|
854 | lastFrame() === 'Hello World'; //=> true
|
855 | ```
|
856 |
|
857 | Visit [ink-testing-library](https://github.com/vadimdemedes/ink-testing-library) for more examples and full documentation.
|
858 |
|
859 |
|
860 | ## Maintainers
|
861 |
|
862 | - [Vadim Demedes](https://github.com/vadimdemedes)
|
863 | - [Sindre Sorhus](https://github.com/sindresorhus)
|
864 |
|
865 |
|
866 | ## License
|
867 |
|
868 | MIT
|