Ecosyste.ms: Awesome

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

Awesome Lists | Featured Topics | Projects

https://github.com/vadimdemedes/ink-ui

💄 Ink-redible command-line interfaces made easy
https://github.com/vadimdemedes/ink-ui

ink react terminal tui tuikit ui uikit

Last synced: about 2 months ago
JSON representation

💄 Ink-redible command-line interfaces made easy

Awesome Lists containing this project

README

        

[![](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/banner2-direct.svg)](https://github.com/vshymanskyy/StandWithUkraine/blob/main/docs/README.md)

# Ink UI [![test](https://github.com/vadimdemedes/ink-ui/actions/workflows/test.yml/badge.svg)](https://github.com/vadimdemedes/ink-ui/actions/workflows/test.yml)

> Collection of customizable UI components for CLIs made with [Ink](https://term.ink).

## Install

```sh
npm install @inkjs/ui
```

_This assumes you've already set up [Ink](https://term.ink). The easiest way to get started is [create-ink-app](https://github.com/vadimdemedes/create-ink-app)._

## Components

### Text input

[Documentation](docs/text-input.md)

`TextInput` is used for entering any single-line input with an optional autocomplete.

```jsx
import {TextInput} from '@inkjs/ui';

{
// `name` contains user input
}}
/>;
```

### Email input

[Documentation](docs/email-input.md)

`EmailInput` is used for entering an email. After "@" character is entered, domain can be autocompleted from the list of most popular email providers.

```jsx
import {EmailInput} from '@inkjs/ui';

{
// `email` contains user input
}}
/>;
```

### Password input

[Documentation](docs/password-input.md)

`PasswordInput` is used for entering sensitive data, like passwords, API keys and so on. It works the same way as `TextInput`, except input value is masked and replaced with asterisks ("\*").

```jsx
import {PasswordInput} from '@inkjs/ui';

{
// `password` contains user input
}}
/>;
```

### Confirm input

[Documentation](docs/confirm-input.md)

`ConfirmInput` shows a common "Y/n" input to confirm or cancel an operation your CLI wants to perform.

```jsx
import {ConfirmInput} from '@inkjs/ui';

{
// confirmed
}}
onCancel={() => {
// cancelled
}}
/>;
```

### Select

[Documentation](docs/select.md)

`Select` shows a scrollable list of options for a user to choose from.

```jsx
import {Select} from '@inkjs/ui';

{
// `newValue` equals the `value` field of the selected option
// For example, "yellow"
}}
/>;
```

### Multi select

[Documentation](docs/multi-select.md)

`MultiSelect` is similar to `Select`, except user can choose multiple options.

```jsx
import {MultiSelect} from '@inkjs/ui';

{
// `newValue` is an array of `value` fields of the selected options
// For example, ["green", "yellow"]
}}
/>;
```

### Spinner

[Documentation](docs/spinner.md)

`Spinner` indicates that something is being processed and CLI is waiting for it to complete.

```jsx
import {Spinner} from '@inkjs/ui';

;
```

### Progress bar

[Documentation](docs/progress-bar.md)

`ProgressBar` is an extended version of `Spinner`, where it's possible to calculate a progress percentage.

```jsx
import {ProgressBar} from '@inkjs/ui';

// `progress` must be a number between 0 and 100
;
```

### Badge

[Documentation](docs/badge.md)

`Badge` can be used to indicate a status of a certain item, usually positioned nearby the element it's related to.

```jsx
import {Badge} from '@inkjs/ui';

Pass
Fail
Warn
Todo
```

### Status message

[Documentation](docs/status-message.md)

`StatusMessage` can also be used to indicate a status, but when longer explanation of such status is required.

```jsx
import {StatusMessage} from '@inkjs/ui';

New version is deployed to production

Failed to deploy a new version of this app

Health checks aren't configured

This version is already deployed

```

### Alert

[Documentation](docs/alert.md)

`Alert` is used to focus user's attention to important messages.

```jsx
import {Alert} from '@inkjs/ui';

A new version of this CLI is available

Your license is expired

Current version of this CLI has been deprecated

API won't be available tomorrow night

```

### Unordered list

[Documentation](docs/unordered-list.md)

`UnorderedList` is used to show lists of items.

```jsx
import {UnorderedList} from '@inkjs/ui';


Red


Green



Light


Dark



Blue

;
```

### Ordered list

[Documentation](docs/ordered-list.md)

`OrderedList` is used to show lists of numbered items.

```jsx
import {OrderedList} from '@inkjs/ui';


Red


Green



Light


Dark



Blue

;
```

## Theming

All component have their styles defined in a theme, which is accessible to components via React context. Ink UI ships with a default theme and it can be customized or replaced with a different theme altogether.

Let's get a quick look on how to customize a `Spinner`'s component theme. Here's how it looks by default:

First, look up component's default theme, which will give an overview which parts does this component consist of. Documentation of each component includes a link to component's `theme.ts` file on top. In the case of `Spinner`, it's [source/components/spinner/theme.ts](source/components/spinner/theme.ts).

Here's the part we care about:

```tsx
const theme = {
styles: {
container: (): BoxProps => ({
gap: 1,
}),
frame: (): TextProps => ({
color: 'blue',
}),
label: (): TextProps => ({}),
},
} satisfies ComponentTheme;

export default theme;
```

This component theme hints that `Spinner` has 3 parts: container, frame and a label. So to customize the color of the spinner itself, we'd want to change the `color` prop returned from the `frame` function.

To customize the default theme, use `extendTheme` function and make that custom theme available to children components via `ThemeProvider`.

```tsx
import {render, type TextProps} from 'ink';
import {Spinner, ThemeProvider, extendTheme, defaultTheme} from '@inkjs/ui';

const customTheme = extendTheme(defaultTheme, {
components: {
Spinner: {
styles: {
frame: (): TextProps => ({
color: 'magenta',
}),
},
},
},
});

function Example() {
return (



);
}

render();
```

With custom theme applied, `Spinner` now renders a magenta spinner, instead of the default blue one.

There are also cases where styles change based on some condition. For example, [`StatusMessage`](docs/status-message.md) changes the color of an icon based on the `variant` prop.

Here's a sample code from its [theme](source/components/status-message/theme.ts).

```ts
const colorByVariant = {
success: 'green',
error: 'red',
warning: 'yellow',
info: 'blue',
};

const theme = {
styles: {
icon: ({variant}) => ({
color: colorByVariant[variant],
}),
},
};
```

Since each field in `styles` object is a function, it can return different styles based on the props that were passed in or a state of a component.

Component themes can also include configuration for rendering a component in a `config` object, that's not related to styling. For example, [`UnorderedList`](docs/unordered-list.md) specifies a `marker`, which is a character that's rendered before each list item.

Here's a sample code from its [theme](source/components/unordered-list/theme.ts).

```ts
const theme = {
config: () => ({
marker: '─',
}),
};
```

Changing `marker` to `'+'` would render this:

Components shipped in Ink UI automatically read the necessary styles and configuration from a theme. However, if you're adding a new custom component and a theme for it, use `useComponentTheme` hook to access it.

```tsx
import React, {render, Text, type TextProps} from 'ink';
import {
ThemeProvider,
defaultTheme,
extendTheme,
useComponentTheme,
type ComponentTheme,
} from '@inkjs/ui';

const customLabelTheme = {
styles: {
label: (): TextProps => ({
color: 'green',
}),
},
} satisfies ComponentTheme;

type CustomLabelTheme = typeof customLabelTheme;

const customTheme = extendTheme(defaultTheme, {
components: {
CustomLabel: customLabelTheme,
},
});

function CustomLabel() {
const {styles} = useComponentTheme('CustomLabel');

return Hello world;
}

function Example() {
return (



);
}

render();
```