diff --git a/.circleci/config.yml b/.circleci/config.yml
deleted file mode 100644
index e19cc43..0000000
--- a/.circleci/config.yml
+++ /dev/null
@@ -1,37 +0,0 @@
-# Javascript Node CircleCI 2.0 configuration file
-#
-# Check https://circleci.com/docs/2.0/language-javascript/ for more details
-#
-version: 2
-jobs:
- build:
- docker:
- # specify the version you desire here
- - image: circleci/node:7.10
-
- # Specify service dependencies here if necessary
- # CircleCI maintains a library of pre-built images
- # documented at https://circleci.com/docs/2.0/circleci-images/
- # - image: circleci/mongo:3.4.4
-
- working_directory: ~/repo
-
- steps:
- - checkout
-
- # Download and cache dependencies
- - restore_cache:
- keys:
- - v1-dependencies-{{ checksum "package.json" }}
- # fallback to using the latest cache if no exact match is found
- - v1-dependencies-
-
- - run: yarn install
-
- - save_cache:
- paths:
- - node_modules
- key: v1-dependencies-{{ checksum "package.json" }}
-
- # run tests!
- - run: yarn run eslint
diff --git a/.eslintrc.js b/.eslintrc.js
deleted file mode 100644
index e3a9e21..0000000
--- a/.eslintrc.js
+++ /dev/null
@@ -1,36 +0,0 @@
-module.exports = {
- "env": {
- "es6": true,
- "node": true
- },
- "extends": [
- "eslint:recommended",
- "plugin:react/recommended"
- ],
- "parserOptions": {
- "ecmaFeatures": {
- "experimentalObjectRestSpread": true,
- "jsx": true
- },
- "sourceType": "module"
- },
- "plugins": [
- "react"
- ],
- "rules": {
- "indent": ["error", 4],
- "linebreak-style": ["error", "unix"],
- "quotes": ["error", "single"],
- "semi": ["error", "always"],
- "no-console": "off",
-
- // React
- "react/prop-types": "off", // This is ink
- "react/no-deprecated": "off", // This is ink
- },
- "settings": {
- "react": {
- "pragma": "h",
- }
- }
-};
diff --git a/.gitignore b/.gitignore
index e1c92c7..3a114f1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,6 @@
# IDE
.idea
+.vscode
# Build
build/
diff --git a/README.md b/README.md
index 30bac79..4e771d4 100644
--- a/README.md
+++ b/README.md
@@ -1,17 +1,19 @@
-# Ink Quicksearch
+# ink-quicksearch-input
-> QuickSearch Component for [Ink](https://github.com/vadimdemedes/ink)
+> QuickSearch Component for [Ink 2](https://github.com/vadimdemedes/ink), [demo on repl.it](https://repl.it/@johnosullivan1/ink-quicksearch-input)
-
+Forked from [@aicioara](https://github.com/aicioara)'s original [`ink-quicksearch`](https://github.com/aicioara/ink-quicksearch) component to upgrade it to Ink 2. Big thanks to him for laying out the original logic in v1! If you are looking for a component that works with Ink v1, that's where to go. This re-write uses modern React (e.g. function components and hooks), and it is also in Typescript, improving the developer experience. The only behavioral difference is that this component always filters out items which do not match the query. Note that the demo runs a good bit slower than it does in an actual terminal; there's some uncanny-valley lag which is not present during normal use.
## Install
```
-$ npm install ink-quicksearch
+$ npm install ink-quicksearch-input
```
## Quickstart
+If you'd like to get a feel for how the component works, you can see the examples in action by running:
+
```bash
npm install
npm start
@@ -20,13 +22,18 @@ npm start
## Usage
```jsx
-const {h, render, Component} = require('ink');
-const QuickSearch = require('ink-quicksearch');
-
-class Demo extends Component {
- render() {
- const props = {
- items: [
+import React, { useState } from 'react';
+import { render, Text } from 'ink';
+import { QuickSearchInput } from 'ink-quicksearch-input';
+
+const Demo = (props) => {
+ const [result, setResult] = useState('');
+ return (
+ <>
+ The user selected {result}.
+ {'\n'}
+ {
- // `item` = { label: 'First', value: 'first' }
- };,
- };
-
- return
- }
+ // ...
+ ]}
+ onSelect={(item) => setResult(item.label)} />
+ >
+ )
}
render();
@@ -60,10 +64,11 @@ render();
| forceMatchingQuery | `bool` | `false` | If set to true, queries that return no results are not allowed. In particular, if previous query `X` returns at least one result and `X + new_character` would not, query will not update to `X + new_character`.
| clearQueryChars | `Array(char)` | `['\u0015', '\u0017']`
(Ctrl + u, Ctrl + w) | Key Combinations that will clear the query.
`ch` follows the `keypress` API `process.stdin.on('keypress', (ch, key) => {})`.
| initialSelectionIndex | `int` | `0` | Selection index when the component is initially rendered or when `props.items` changes. Can be set together with new `props.items` to automatically select an option.
+| label | `string` | | Optionally provide a label which will appear before the current query.
| indicatorComponent | Component | | Custom component to override the default indicator component (default - arrow).
| itemComponent | Component | | Custom component to override the default item style (default - selection coloring).
| highlightComponent | Component | | Custom component to override the default highlight style (default - background highlight).
-| statusComponent | Component | | Custom component to override the status component (default - current query).
+| statusComponent | Component | | Custom component to override the status component (default - current query, optional value label).
## Component Props
@@ -106,6 +111,7 @@ Props:
- `hasMatch`: `boolean`
- `children`: `any`
+- `label`: Optional `string`
diff --git a/bin/release.sh b/bin/release.sh
deleted file mode 100755
index c9734ea..0000000
--- a/bin/release.sh
+++ /dev/null
@@ -1,7 +0,0 @@
-#!/usr/bin/env bash
-
-THIS_DIR=$(dirname "$0")
-cd ${THIS_DIR}
-cd ..
-
-
diff --git a/dist/index.js b/dist/index.js
deleted file mode 100644
index b296b40..0000000
--- a/dist/index.js
+++ /dev/null
@@ -1,280 +0,0 @@
-const { h, Component, Color } = require('ink');
-const hasAnsi = require('has-ansi');
-const isEqual = require('lodash.isequal');
-
-const defaultValue = { label: '' }; // Used as return for empty array
-
-
-// For the following four, whitespace is important
-const IndicatorComponent = ({ isSelected }) => {
- return h(
- Color,
- { hex: '#00FF00' },
- isSelected ? '>' : ' ',
- ' '
- );
-};
-
-const ItemComponent = ({ isSelected, children }) => h(
- Color,
- { hex: isSelected ? '#00FF00' : '' },
- children
-);
-
-const HighlightComponent = ({ children }) => h(
- Color,
- { bgHex: '#6C71C4' },
- children
-);
-
-const StatusComponent = ({ hasMatch, children }) => h(
- Color,
- { hex: hasMatch ? '#00FF00' : '#FF0000' },
- children
-);
-
-class QuickSearch extends Component {
- constructor(props) {
- super(props);
- this.state = QuickSearch.initialState;
- this.state.selectionIndex = this.props.initialSelectionIndex;
- this.handleKeyPress = this.handleKeyPress.bind(this);
- }
-
- render() {
- // Cannot have these starting with lowercases
- const HighlightComponent_ = this.props.highlightComponent;
- const ItemComponent_ = this.props.itemComponent;
- const IndicatorComponent_ = this.props.indicatorComponent;
- const StatusComponent_ = this.props.statusComponent;
-
- const begin = this.state.startIndex;
- let end = this.props.items.length;
- if (this.props.limit !== 0) {
- end = Math.min(begin + this.props.limit, this.props.items.length);
- }
- const items = this.props.items.slice(begin, end);
-
- const rows = items.map((item, index) => {
- const isLast = index === items.length - 1;
- const isSelected = index + this.state.startIndex === this.state.selectionIndex;
- const isHighlighted = undefined;
-
- const itemProps = { isSelected, isHighlighted, item };
-
- const label = item.label;
- const queryPosition = this.getMatchPosition(label, this.state.query);
-
- let labelComponent = '';
- if (queryPosition === -1) {
- itemProps.isHighlighted = false;
- labelComponent = h(
- 'span',
- null,
- label
- );
- } else {
- itemProps.isHighlighted = true;
- const start = queryPosition;
- const end = start + this.state.query.length;
-
- const first = label.slice(0, start);
- const second = label.slice(start, end);
- const third = label.slice(end);
-
- labelComponent = h(
- 'span',
- null,
- first,
- h(
- HighlightComponent_,
- itemProps,
- second
- ),
- third
- );
- }
-
- return h(
- ItemComponent_,
- Object.assign({ key: item.value }, itemProps),
- h(IndicatorComponent_, itemProps),
- labelComponent,
- !isLast && h('br', null)
- );
- });
-
- return h(
- 'span',
- null,
- rows,
- h(
- StatusComponent_,
- { hasMatch: this.state.hasMatch },
- h('br', null),
- this.state.query
- )
- );
- }
-
- componentDidMount() {
- process.stdin.on('keypress', this.handleKeyPress);
- }
-
- componentWillUnmount() {
- process.stdin.removeListener('keypress', this.handleKeyPress);
- }
-
- componentWillReceiveProps(nextProps) {
- if (!isEqual(this.props.items, nextProps.items)) {
- this.setState(QuickSearch.initialState);
- if (nextProps.initialSelectionIndex != null) {
- this._updateSelectionIndex(nextProps.initialSelectionIndex, nextProps);
- }
- }
- }
-
- handleKeyPress(ch, key) {
- if (!this.props.focus) {
- return;
- }
-
- if (this.props.clearQueryChars.indexOf(ch) !== -1) {
- this.setState({ query: '' });
- } else if (key.name === 'return') {
- this.props.onSelect(this.getValue());
- } else if (key.name === 'backspace') {
- this._updateQuery(this.state.query.slice(0, -1));
- } else if (key.name === 'up') {
- this._changeSelection(-1);
- } else if (key.name === 'down') {
- this._changeSelection(1);
- } else if (key.name === 'tab') {
- if (key.shift === false) {
- this._changeSelection(1);
- } else {
- this._changeSelection(-1);
- }
- } else if (key.name === 'pageup' || key.name === 'pagedown') {
- this._handlePageChange(key.name);
- } else if (hasAnsi(key.sequence)) {
- // No-op
- } else {
- this._updateQuery(this.state.query + ch);
- }
- }
-
- _updateQuery(query) {
- let selectionIndex = this.state.selectionIndex;
- let hasMatch = false;
- if (query.trim() === '' || this.getMatchPosition(this.getValue().label, query) !== -1) {
- hasMatch = true;
- } else {
- for (var i = 0; i < this.props.items.length; i++) {
- if (this.getMatchPosition(this.props.items[i].label, query) !== -1) {
- selectionIndex = i;
- hasMatch = true;
- break;
- }
- }
- }
-
- if (!hasMatch && this.props.forceMatchingQuery) {
- return;
- }
-
- this._updateSelectionIndex(selectionIndex);
- this.setState({ query, hasMatch });
- }
-
- _changeSelection(delta) {
- for (let selectionIndex = this.state.selectionIndex + delta; 0 <= selectionIndex && selectionIndex < this.props.items.length; selectionIndex += delta) {
- if (!this.state.hasMatch) {
- this._updateSelectionIndex(selectionIndex);
- break;
- }
-
- if (this.getMatchPosition(this.props.items[selectionIndex].label, this.state.query) !== -1) {
- this._updateSelectionIndex(selectionIndex);
- break;
- }
- }
- }
-
- _updateSelectionIndex(selectionIndex, props) {
- if (props == undefined) {
- props = this.props;
- }
- this.setState({ selectionIndex });
- if (props.limit === 0) {
- return;
- }
- const begin = this.state.startIndex;
- const end = Math.min(begin + props.limit, props.items.length);
- if (begin <= selectionIndex && selectionIndex < end) {
- return;
- } else if (selectionIndex >= end) {
- if (selectionIndex >= props.items.length) {
- throw Error(`Error: selection index (${selectionIndex}) outside items range (${props.items.length}).`);
- }
- const startIndex = selectionIndex - props.limit + 1;
- this.setState({ startIndex });
- } else {
- // if (selectionIndex < begin)
- this.setState({ startIndex: selectionIndex });
- }
- }
-
- _handlePageChange(keyName) {
- if (this.state.query.trim() !== '') {
- return; // Do not page when selecting
- }
- if (this.props.limit === 0) {
- return; // Nothing to page
- }
- let newIndex = this.state.selectionIndex;
- if (keyName === 'pageup') {
- newIndex = Math.max(this.state.selectionIndex - this.props.limit + 1, 0);
- } else if (keyName === 'pagedown') {
- newIndex = Math.min(this.state.selectionIndex + this.props.limit - 1, this.props.items.length - 1);
- }
- this._changeSelection(newIndex - this.state.selectionIndex);
- }
-
- getMatchPosition(label, query) {
- if (this.props.caseSensitive) {
- return label.indexOf(query);
- } else {
- return label.toLowerCase().indexOf(query.toLowerCase());
- }
- }
-
- getValue() {
- return this.props.items[this.state.selectionIndex] || defaultValue;
- }
-}
-
-QuickSearch.initialState = {
- query: '',
- hasMatch: true,
- selectionIndex: 0,
- startIndex: 0
-};
-
-QuickSearch.defaultProps = {
- items: [],
- onSelect: () => {}, // no-op
- focus: true,
- caseSensitive: false,
- limit: 0,
- forceMatchingQuery: false,
- clearQueryChars: ['\u0015', // Ctrl + U
- '\u0017'],
- initialSelectionIndex: 0,
- indicatorComponent: IndicatorComponent,
- itemComponent: ItemComponent,
- highlightComponent: HighlightComponent,
- statusComponent: StatusComponent
-};
-
-module.exports = QuickSearch;
diff --git a/examples/example1.js b/examples/example1.js
deleted file mode 100644
index 0a23ab1..0000000
--- a/examples/example1.js
+++ /dev/null
@@ -1 +0,0 @@
-require('import-jsx')('./example1.jsx');
diff --git a/examples/example1.jsx b/examples/example1.jsx
deleted file mode 100644
index fd87ee6..0000000
--- a/examples/example1.jsx
+++ /dev/null
@@ -1,32 +0,0 @@
-/**
- * This is the basic usage example, including all defaults
- */
-
-const {h, render, Color, Component} = require('ink');
-
-const QuickSearch = require('import-jsx')('../src/QuickSearch.jsx');
-
-class Example1 extends Component {
- render() {
- const props = {
- items: [
- {value: 1, label: 'Animal'},
- {value: 3, label: 'Antilope'},
- {value: 2, label: 'Animation'},
- {value: 0, label: 'Animate'},
- {value: 4, label: 'Arizona'},
- {value: 5, label: 'Aria'},
- {value: 6, label: 'Arid'},
- ],
- onSelect: d => console.log('You selected', d),
- };
-
- return
- Example 1
-
-
- ;
- }
-}
-
-render();
diff --git a/examples/example2.js b/examples/example2.js
deleted file mode 100644
index 2644218..0000000
--- a/examples/example2.js
+++ /dev/null
@@ -1 +0,0 @@
-require('import-jsx')('./example2.jsx');
diff --git a/examples/example2.jsx b/examples/example2.jsx
deleted file mode 100644
index b81ad97..0000000
--- a/examples/example2.jsx
+++ /dev/null
@@ -1,39 +0,0 @@
-/**
- * This is a example that does not feature a search box and
- * is case sensitive
- */
-
-const {h, render, Color, Component} = require('ink');
-
-const QuickSearch = require('import-jsx')('../src/QuickSearch.jsx');
-
-
-const StatusComponent = () => ; // No-op
-
-
-class Example2 extends Component {
- render() {
- const props = {
- items: [
- {label: 'Animal'},
- {label: 'Antilope'},
- {label: 'Animation'},
- {label: 'Animate'},
- {label: 'Arizona'},
- {label: 'Aria'},
- {label: 'Arid'},
- ],
- onSelect: d => console.log(d),
- caseSensitive: true,
- statusComponent: StatusComponent,
- };
-
- return
- Example 2
-
-
- ;
- }
-}
-
-render();
diff --git a/examples/example3.js b/examples/example3.js
deleted file mode 100644
index 1ecb56e..0000000
--- a/examples/example3.js
+++ /dev/null
@@ -1 +0,0 @@
-require('import-jsx')('./example3.jsx');
diff --git a/examples/example3.jsx b/examples/example3.jsx
deleted file mode 100644
index 5988c58..0000000
--- a/examples/example3.jsx
+++ /dev/null
@@ -1,45 +0,0 @@
-/**
- * Example with disappearing options
- */
-
-const {h, render, Color, Component} = require('ink');
-
-const QuickSearch = require('import-jsx')('../src/QuickSearch.jsx');
-
-
-const ItemComponent = ({isHighlighted, isSelected, children}) => {
- if (!isHighlighted) {
- return ;
- }
- return {children};
-};
-
-const StatusComponent = () => ; // No-op
-
-
-class Example3 extends Component {
- render() {
- const props = {
- items: [
- {label: 'Animal'},
- {label: 'Antilope'},
- {label: 'Animation'},
- {label: 'Animate'},
- {label: 'Arizona'},
- {label: 'Aria'},
- {label: 'Arid'},
- ],
- onSelect: d => console.log(d),
- itemComponent: ItemComponent,
- statusComponent: StatusComponent,
- };
-
- return
- Example 3
-
-
- ;
- }
-}
-
-render();
diff --git a/examples/example4.js b/examples/example4.js
deleted file mode 100644
index 402cd7f..0000000
--- a/examples/example4.js
+++ /dev/null
@@ -1 +0,0 @@
-require('import-jsx')('./example4.jsx');
diff --git a/examples/example5.js b/examples/example5.js
deleted file mode 100644
index fda2f17..0000000
--- a/examples/example5.js
+++ /dev/null
@@ -1 +0,0 @@
-require('import-jsx')('./example5.jsx');
diff --git a/examples/example5.jsx b/examples/example5.jsx
deleted file mode 100644
index 325c94f..0000000
--- a/examples/example5.jsx
+++ /dev/null
@@ -1,48 +0,0 @@
-/**
- * Using initialSelectionIndex. Press Enter to cycle between states
- */
-
-const {h, render, Color, Component} = require('ink');
-
-const QuickSearch = require('import-jsx')('../src/QuickSearch.jsx');
-
-class Example1 extends Component {
- constructor(props) {
- super(props);
- this.state = {setIndex: 0};
- }
-
- render() {
- const sets = [
- [
- {label: 'Aardvark'},
- {label: 'Abyssinian'},
- {label: 'Adelie Penguin'},
- {label: 'Affenpinscher'}, // Selected
- {label: 'Afghan Hound'},
- ],
- [
- {label: 'Baboon'},
- {label: 'Bactrian Camel'}, // Selected
- {label: 'Badger'},
- {label: 'Balinese'},
- {label: 'Banded Palm Civet'},
- ]
- ];
- const selection = [3, 1];
-
- const props = {
- items: sets[this.state.setIndex],
- onSelect: () => this.setState({setIndex: this.state.setIndex ^ 1}),
- initialSelectionIndex: selection[this.state.setIndex],
- };
-
- return
- Example 5
-
-
- ;
- }
-}
-
-render();
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 0000000..9c206a4
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,591 @@
+{
+ "name": "ink-quicksearch-input",
+ "version": "1.0.0",
+ "lockfileVersion": 1,
+ "requires": true,
+ "dependencies": {
+ "@types/color-name": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz",
+ "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==",
+ "dev": true
+ },
+ "@types/has-ansi": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/@types/has-ansi/-/has-ansi-3.0.0.tgz",
+ "integrity": "sha512-H3vFOwfLlFEC0MOOrcSkus8PCnMCzz4N0EqUbdJZCdDhBTfkAu86aRYA+MTxjKW6jCpUvxcn4715US8g+28BMA=="
+ },
+ "@types/ink": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/@types/ink/-/ink-2.0.3.tgz",
+ "integrity": "sha512-DYKIKEJqhsGfQ/jgX0t9BzfHmBJ/9dBBT2MDsHAQRAfOPhEe7LZm5QeNBx1J34/e108StCPuJ3r4bh1y38kCJA==",
+ "dev": true,
+ "requires": {
+ "ink": "*"
+ }
+ },
+ "@types/lodash": {
+ "version": "4.14.149",
+ "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.149.tgz",
+ "integrity": "sha512-ijGqzZt/b7BfzcK9vTrS6MFljQRPn5BFWOx8oE0GYxribu6uV+aA9zZuXI1zc/etK9E8nrgdoF2+LgUw7+9tJQ=="
+ },
+ "@types/lodash.isequal": {
+ "version": "4.5.5",
+ "resolved": "https://registry.npmjs.org/@types/lodash.isequal/-/lodash.isequal-4.5.5.tgz",
+ "integrity": "sha512-4IKbinG7MGP131wRfceK6W4E/Qt3qssEFLF30LnJbjYiSfHGGRU/Io8YxXrZX109ir+iDETC8hw8QsDijukUVg==",
+ "requires": {
+ "@types/lodash": "*"
+ }
+ },
+ "@types/node": {
+ "version": "12.12.14",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.14.tgz",
+ "integrity": "sha512-u/SJDyXwuihpwjXy7hOOghagLEV1KdAST6syfnOk6QZAMzZuWZqXy5aYYZbh8Jdpd4escVFP0MvftHNDb9pruA==",
+ "dev": true
+ },
+ "@types/prop-types": {
+ "version": "15.7.3",
+ "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.3.tgz",
+ "integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==",
+ "dev": true
+ },
+ "@types/react": {
+ "version": "16.9.15",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.15.tgz",
+ "integrity": "sha512-WsmM1b6xQn1tG3X2Hx4F3bZwc2E82pJXt5OPs2YJgg71IzvUoKOSSSYOvLXYCg1ttipM+UuA4Lj3sfvqjVxyZw==",
+ "dev": true,
+ "requires": {
+ "@types/prop-types": "*",
+ "csstype": "^2.2.0"
+ }
+ },
+ "ansi-escapes": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.0.tgz",
+ "integrity": "sha512-EiYhwo0v255HUL6eDyuLrXEkTi7WwVCLAw+SeOQ7M7qdun1z1pum4DEm/nuqIVbPvi9RPPc9k9LbyBv6H0DwVg==",
+ "dev": true,
+ "requires": {
+ "type-fest": "^0.8.1"
+ }
+ },
+ "ansi-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
+ "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg="
+ },
+ "ansi-styles": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.0.tgz",
+ "integrity": "sha512-7kFQgnEaMdRtwf6uSfUnVr9gSGC7faurn+J/Mv90/W+iTtN0405/nLdopfMWwchyxhbGYl6TC4Sccn9TUkGAgg==",
+ "dev": true,
+ "requires": {
+ "@types/color-name": "^1.1.1",
+ "color-convert": "^2.0.1"
+ }
+ },
+ "arrify": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz",
+ "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==",
+ "dev": true
+ },
+ "astral-regex": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz",
+ "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==",
+ "dev": true
+ },
+ "auto-bind": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/auto-bind/-/auto-bind-3.0.0.tgz",
+ "integrity": "sha512-v0A231a/lfOo6kxQtmEkdBfTApvC21aJYukA8pkKnoTvVqh3Wmm7/Rwy4GBCHTTHVoLVA5qsBDDvf1XY1nIV2g==",
+ "dev": true
+ },
+ "chalk": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz",
+ "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ }
+ },
+ "ci-info": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz",
+ "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==",
+ "dev": true
+ },
+ "cli-cursor": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
+ "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==",
+ "dev": true,
+ "requires": {
+ "restore-cursor": "^3.1.0"
+ }
+ },
+ "cli-truncate": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz",
+ "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==",
+ "dev": true,
+ "requires": {
+ "slice-ansi": "^3.0.0",
+ "string-width": "^4.2.0"
+ }
+ },
+ "color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "requires": {
+ "color-name": "~1.1.4"
+ }
+ },
+ "color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "csstype": {
+ "version": "2.6.7",
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.7.tgz",
+ "integrity": "sha512-9Mcn9sFbGBAdmimWb2gLVDtFJzeKtDGIr76TUqmjZrw9LFXBMSU70lcs+C0/7fyCd6iBDqmksUcCOUIkisPHsQ==",
+ "dev": true
+ },
+ "emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true
+ },
+ "has-ansi": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-3.0.0.tgz",
+ "integrity": "sha1-Ngd+8dFfMzSEqn+neihgbxxlWzc=",
+ "requires": {
+ "ansi-regex": "^3.0.0"
+ }
+ },
+ "has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true
+ },
+ "ink": {
+ "version": "2.6.0",
+ "resolved": "https://registry.npmjs.org/ink/-/ink-2.6.0.tgz",
+ "integrity": "sha512-nD/wlSuB6WnFsFB0nUcOJdy28YvvDer3eo+gezjvZqojGA4Rx5sQpacvN//Aai83DRgwrRTyKBl5aciOcfP3zQ==",
+ "dev": true,
+ "requires": {
+ "ansi-escapes": "^4.2.1",
+ "arrify": "^2.0.1",
+ "auto-bind": "^3.0.0",
+ "chalk": "^3.0.0",
+ "cli-cursor": "^3.1.0",
+ "cli-truncate": "^2.0.0",
+ "is-ci": "^2.0.0",
+ "lodash.throttle": "^4.1.1",
+ "log-update": "^3.0.0",
+ "prop-types": "^15.6.2",
+ "react-reconciler": "^0.24.0",
+ "scheduler": "^0.18.0",
+ "signal-exit": "^3.0.2",
+ "slice-ansi": "^3.0.0",
+ "string-length": "^3.1.0",
+ "widest-line": "^3.1.0",
+ "wrap-ansi": "^6.2.0",
+ "yoga-layout-prebuilt": "^1.9.3"
+ }
+ },
+ "is-ci": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz",
+ "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==",
+ "dev": true,
+ "requires": {
+ "ci-info": "^2.0.0"
+ }
+ },
+ "is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "dev": true
+ },
+ "js-tokens": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+ "dev": true
+ },
+ "keypress": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/keypress/-/keypress-0.2.1.tgz",
+ "integrity": "sha1-HoBFQlABjbrUw/6USX1uZ7YmnHc="
+ },
+ "lodash.isequal": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
+ "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA="
+ },
+ "lodash.throttle": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz",
+ "integrity": "sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ=",
+ "dev": true
+ },
+ "log-update": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/log-update/-/log-update-3.3.0.tgz",
+ "integrity": "sha512-YSKm5n+YjZoGZT5lfmOqasVH1fIH9xQA9A81Y48nZ99PxAP62vdCCtua+Gcu6oTn0nqtZd/LwRV+Vflo53ZDWA==",
+ "dev": true,
+ "requires": {
+ "ansi-escapes": "^3.2.0",
+ "cli-cursor": "^2.1.0",
+ "wrap-ansi": "^5.0.0"
+ },
+ "dependencies": {
+ "ansi-escapes": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz",
+ "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==",
+ "dev": true
+ },
+ "ansi-regex": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
+ "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
+ "dev": true
+ },
+ "ansi-styles": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^1.9.0"
+ }
+ },
+ "cli-cursor": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz",
+ "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=",
+ "dev": true,
+ "requires": {
+ "restore-cursor": "^2.0.0"
+ }
+ },
+ "color-convert": {
+ "version": "1.9.3",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+ "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+ "dev": true,
+ "requires": {
+ "color-name": "1.1.3"
+ }
+ },
+ "color-name": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+ "dev": true
+ },
+ "emoji-regex": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
+ "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
+ "dev": true
+ },
+ "is-fullwidth-code-point": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
+ "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
+ "dev": true
+ },
+ "mimic-fn": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz",
+ "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==",
+ "dev": true
+ },
+ "onetime": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz",
+ "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=",
+ "dev": true,
+ "requires": {
+ "mimic-fn": "^1.0.0"
+ }
+ },
+ "restore-cursor": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz",
+ "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=",
+ "dev": true,
+ "requires": {
+ "onetime": "^2.0.0",
+ "signal-exit": "^3.0.2"
+ }
+ },
+ "string-width": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
+ "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
+ "dev": true,
+ "requires": {
+ "emoji-regex": "^7.0.1",
+ "is-fullwidth-code-point": "^2.0.0",
+ "strip-ansi": "^5.1.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+ "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^4.1.0"
+ }
+ },
+ "wrap-ansi": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz",
+ "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^3.2.0",
+ "string-width": "^3.0.0",
+ "strip-ansi": "^5.0.0"
+ }
+ }
+ }
+ },
+ "loose-envify": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
+ "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
+ "dev": true,
+ "requires": {
+ "js-tokens": "^3.0.0 || ^4.0.0"
+ }
+ },
+ "mimic-fn": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
+ "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
+ "dev": true
+ },
+ "object-assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+ "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
+ "dev": true
+ },
+ "onetime": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz",
+ "integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==",
+ "dev": true,
+ "requires": {
+ "mimic-fn": "^2.1.0"
+ }
+ },
+ "prop-types": {
+ "version": "15.7.2",
+ "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz",
+ "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==",
+ "dev": true,
+ "requires": {
+ "loose-envify": "^1.4.0",
+ "object-assign": "^4.1.1",
+ "react-is": "^16.8.1"
+ }
+ },
+ "react": {
+ "version": "16.12.0",
+ "resolved": "https://registry.npmjs.org/react/-/react-16.12.0.tgz",
+ "integrity": "sha512-fglqy3k5E+81pA8s+7K0/T3DBCF0ZDOher1elBFzF7O6arXJgzyu/FW+COxFvAWXJoJN9KIZbT2LXlukwphYTA==",
+ "dev": true,
+ "requires": {
+ "loose-envify": "^1.1.0",
+ "object-assign": "^4.1.1",
+ "prop-types": "^15.6.2"
+ }
+ },
+ "react-is": {
+ "version": "16.12.0",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.12.0.tgz",
+ "integrity": "sha512-rPCkf/mWBtKc97aLL9/txD8DZdemK0vkA3JMLShjlJB3Pj3s+lpf1KaBzMfQrAmhMQB0n1cU/SUGgKKBCe837Q==",
+ "dev": true
+ },
+ "react-reconciler": {
+ "version": "0.24.0",
+ "resolved": "https://registry.npmjs.org/react-reconciler/-/react-reconciler-0.24.0.tgz",
+ "integrity": "sha512-gAGnwWkf+NOTig9oOowqid9O0HjTDC+XVGBCAmJYYJ2A2cN/O4gDdIuuUQjv8A4v6GDwVfJkagpBBLW5OW9HSw==",
+ "dev": true,
+ "requires": {
+ "loose-envify": "^1.1.0",
+ "object-assign": "^4.1.1",
+ "prop-types": "^15.6.2",
+ "scheduler": "^0.18.0"
+ }
+ },
+ "restore-cursor": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz",
+ "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==",
+ "dev": true,
+ "requires": {
+ "onetime": "^5.1.0",
+ "signal-exit": "^3.0.2"
+ }
+ },
+ "scheduler": {
+ "version": "0.18.0",
+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.18.0.tgz",
+ "integrity": "sha512-agTSHR1Nbfi6ulI0kYNK0203joW2Y5W4po4l+v03tOoiJKpTBbxpNhWDvqc/4IcOw+KLmSiQLTasZ4cab2/UWQ==",
+ "dev": true,
+ "requires": {
+ "loose-envify": "^1.1.0",
+ "object-assign": "^4.1.1"
+ }
+ },
+ "signal-exit": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
+ "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=",
+ "dev": true
+ },
+ "slice-ansi": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz",
+ "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^4.0.0",
+ "astral-regex": "^2.0.0",
+ "is-fullwidth-code-point": "^3.0.0"
+ }
+ },
+ "string-length": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/string-length/-/string-length-3.1.0.tgz",
+ "integrity": "sha512-Ttp5YvkGm5v9Ijagtaz1BnN+k9ObpvS0eIBblPMp2YWL8FBmi9qblQ9fexc2k/CXFgrTIteU3jAw3payCnwSTA==",
+ "dev": true,
+ "requires": {
+ "astral-regex": "^1.0.0",
+ "strip-ansi": "^5.2.0"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
+ "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
+ "dev": true
+ },
+ "astral-regex": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz",
+ "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==",
+ "dev": true
+ },
+ "strip-ansi": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+ "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^4.1.0"
+ }
+ }
+ }
+ },
+ "string-width": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz",
+ "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==",
+ "dev": true,
+ "requires": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
+ "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^5.0.0"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
+ "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
+ "dev": true
+ }
+ }
+ },
+ "supports-color": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz",
+ "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^4.0.0"
+ }
+ },
+ "term-size": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/term-size/-/term-size-2.1.0.tgz",
+ "integrity": "sha512-I42EWhJ+2aeNQawGx1VtpO0DFI9YcfuvAMNIdKyf/6sRbHJ4P+ZQ/zIT87tE+ln1ymAGcCJds4dolfSAS0AcNg==",
+ "dev": true
+ },
+ "type-fest": {
+ "version": "0.8.1",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz",
+ "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==",
+ "dev": true
+ },
+ "typescript": {
+ "version": "3.7.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.7.3.tgz",
+ "integrity": "sha512-Mcr/Qk7hXqFBXMN7p7Lusj1ktCBydylfQM/FZCk5glCNQJrCUKPkMHdo9R0MTFWsC/4kPFvDS0fDPvukfCkFsw==",
+ "dev": true
+ },
+ "widest-line": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz",
+ "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==",
+ "dev": true,
+ "requires": {
+ "string-width": "^4.0.0"
+ }
+ },
+ "wrap-ansi": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
+ "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ }
+ },
+ "yoga-layout-prebuilt": {
+ "version": "1.9.3",
+ "resolved": "https://registry.npmjs.org/yoga-layout-prebuilt/-/yoga-layout-prebuilt-1.9.3.tgz",
+ "integrity": "sha512-9SNQpwuEh2NucU83i2KMZnONVudZ86YNcFk9tq74YaqrQfgJWO3yB9uzH1tAg8iqh5c9F5j0wuyJ2z72wcum2w==",
+ "dev": true
+ }
+ }
+}
diff --git a/package.json b/package.json
index ce5926d..8fe212c 100644
--- a/package.json
+++ b/package.json
@@ -1,58 +1,53 @@
{
- "name": "ink-quicksearch",
- "version": "0.3.2",
- "description": "Quicksearch Input Component for Ink",
- "main": "dist/index.js",
+ "name": "ink-quicksearch-input",
+ "version": "1.0.0",
+ "description": "Quicksearch Input Component for Ink 2",
+ "main": "build/QuickSearchInput.js",
+ "types": "src/QuickSearchInput.tsx",
"author": {
- "name": "Andrei Cioara",
- "email": "github@andreicioara.com",
- "url": "http://andrei.cioara.me"
+ "name": "John O'Sullivan "
},
"license": "MIT",
"scripts": {
- "build": "babel src/QuickSearch.jsx --out-file=dist/index.js",
- "eslint": "eslint src/**/*.jsx examples/**/*.jsx",
- "eslint-fix": "eslint --fix src/**/*.jsx examples/**/*.jsx",
- "start": "node examples/example1.js",
- "test": "echo \"Error: no test specified\" && exit 1"
+ "prepare": "npm run build",
+ "build": "tsc",
+ "dev": "tsc --watch",
+ "start": "node build/examples/ExampleDirectory.js"
},
+ "files": [
+ "src/QuickSearchInput.tsx",
+ "build/QuickSearchInput.js",
+ "tsconfig.json"
+ ],
"repository": {
"type": "git",
- "url": "git+https://github.com/aicioara/ink-quicksearch.git"
+ "url": "git+https://github.com/Eximchain/ink-quicksearch-input.git"
},
"keywords": [
"ink",
"ink-component"
],
"bugs": {
- "url": "https://github.com/aicioara/ink-quicksearch/issues"
+ "url": "https://github.com/Eximchain/ink-quicksearch-input/issues"
},
- "homepage": "https://github.com/aicioara/ink-quicksearch#readme",
+ "homepage": "https://github.com/Eximchain/ink-quicksearch-input#readme",
"dependencies": {
+ "@types/has-ansi": "^3.0.0",
+ "@types/lodash.isequal": "^4.5.5",
"has-ansi": "3.0.0",
+ "keypress": "^0.2.1",
"lodash.isequal": "4.5.0"
},
"devDependencies": {
- "babel-cli": "6.24.1",
- "babel-plugin-transform-react-jsx": "6.24.1",
- "eslint": "4.19.1",
- "eslint-plugin-react": "7.8.2",
- "import-jsx": "1.3.0",
- "ink": "0.5.0",
- "term-size": "1.2.0"
+ "@types/ink": "^2.0.3",
+ "@types/node": "^12.12.14",
+ "@types/react": "^16.9.15",
+ "ink": "^2.6.0",
+ "react": "^16.12.0",
+ "term-size": "^2.1.0",
+ "typescript": "^3.7.3"
},
"peerDependencies": {
- "ink": "0.5.x"
- },
- "babel": {
- "plugins": [
- [
- "transform-react-jsx",
- {
- "pragma": "h",
- "useBuiltIns": true
- }
- ]
- ]
+ "ink": "2.6.x"
}
}
diff --git a/src/QuickSearch.jsx b/src/QuickSearch.jsx
deleted file mode 100644
index 9579b96..0000000
--- a/src/QuickSearch.jsx
+++ /dev/null
@@ -1,259 +0,0 @@
-const {h, Component, Color} = require('ink');
-const hasAnsi = require('has-ansi');
-const isEqual = require('lodash.isequal');
-
-const defaultValue = {label:''}; // Used as return for empty array
-
-
-// For the following four, whitespace is important
-const IndicatorComponent = ({isSelected}) => {
- return {isSelected ? '>' : ' '} ;
-};
-
-const ItemComponent = ({isSelected, children}) => (
- {children}
-);
-
-const HighlightComponent = ({children}) => (
- {children}
-);
-
-const StatusComponent = ({hasMatch, children}) => (
- {children}
-);
-
-
-class QuickSearch extends Component {
- constructor(props) {
- super(props);
- this.state = QuickSearch.initialState;
- this.state.selectionIndex = this.props.initialSelectionIndex;
- this.handleKeyPress = this.handleKeyPress.bind(this);
- }
-
- render() {
- // Cannot have these starting with lowercases
- const HighlightComponent_ = this.props.highlightComponent;
- const ItemComponent_ = this.props.itemComponent;
- const IndicatorComponent_ = this.props.indicatorComponent;
- const StatusComponent_ = this.props.statusComponent;
-
- const begin = this.state.startIndex;
- let end = this.props.items.length;
- if (this.props.limit !== 0) {
- end = Math.min(begin + this.props.limit, this.props.items.length);
- }
- const items = this.props.items.slice(begin, end);
-
- const rows = items.map((item, index) => {
- const isLast = (index === items.length - 1);
- const isSelected = (index + this.state.startIndex === this.state.selectionIndex);
- const isHighlighted = undefined;
-
- const itemProps = {isSelected, isHighlighted, item};
-
- const label = item.label;
- const queryPosition = this.getMatchPosition(label, this.state.query);
-
- let labelComponent = '';
- if (queryPosition === -1) {
- itemProps.isHighlighted = false;
- labelComponent = {label};
- } else {
- itemProps.isHighlighted = true;
- const start = queryPosition;
- const end = start + this.state.query.length;
-
- const first = label.slice(0, start);
- const second = label.slice(start, end);
- const third = label.slice(end);
-
- labelComponent =
- {first}
- {second}
- {third}
- ;
- }
-
- return
-
- {labelComponent}
- {!isLast &&
}
- ;
- });
-
- return
- {rows}
-
-
- {this.state.query}
-
- ;
- }
-
- componentDidMount() {
- process.stdin.on('keypress', this.handleKeyPress);
- }
-
- componentWillUnmount() {
- process.stdin.removeListener('keypress', this.handleKeyPress);
- }
-
- componentWillReceiveProps(nextProps) {
- if (!isEqual(this.props.items, nextProps.items)) {
- this.setState(QuickSearch.initialState);
- if (nextProps.initialSelectionIndex != null) {
- this._updateSelectionIndex(nextProps.initialSelectionIndex, nextProps);
- }
- }
- }
-
- handleKeyPress(ch, key) {
- if (!this.props.focus) {
- return;
- }
-
- if (this.props.clearQueryChars.indexOf(ch) !== -1) {
- this.setState({query: ''});
- } else if (key.name === 'return') {
- this.props.onSelect(this.getValue());
- } else if (key.name === 'backspace') {
- this._updateQuery(this.state.query.slice(0, -1));
- } else if (key.name === 'up') {
- this._changeSelection(-1);
- } else if (key.name === 'down') {
- this._changeSelection(1);
- } else if (key.name === 'tab') {
- if (key.shift === false) {
- this._changeSelection(1);
- } else {
- this._changeSelection(-1);
- }
- } else if (key.name === 'pageup' || key.name === 'pagedown') {
- this._handlePageChange(key.name);
- } else if (hasAnsi(key.sequence)) {
- // No-op
- } else {
- this._updateQuery(this.state.query + ch);
- }
- }
-
- _updateQuery(query) {
- let selectionIndex = this.state.selectionIndex;
- let hasMatch = false;
- if (query.trim() === '' || this.getMatchPosition(this.getValue().label, query) !== -1) {
- hasMatch = true;
- } else {
- for (var i = 0; i < this.props.items.length; i++) {
- if (this.getMatchPosition(this.props.items[i].label, query) !== -1) {
- selectionIndex = i;
- hasMatch = true;
- break;
- }
- }
- }
-
- if (!hasMatch && this.props.forceMatchingQuery) {
- return;
- }
-
- this._updateSelectionIndex(selectionIndex);
- this.setState({query, hasMatch});
- }
-
- _changeSelection(delta) {
- for (
- let selectionIndex = this.state.selectionIndex + delta;
- 0 <= selectionIndex && selectionIndex < this.props.items.length;
- selectionIndex += delta
- ) {
- if (!this.state.hasMatch) {
- this._updateSelectionIndex(selectionIndex);
- break;
- }
-
- if (this.getMatchPosition(this.props.items[selectionIndex].label, this.state.query) !== -1) {
- this._updateSelectionIndex(selectionIndex);
- break;
- }
- }
- }
-
- _updateSelectionIndex(selectionIndex, props) {
- if (props == undefined) {
- props = this.props;
- }
- this.setState({selectionIndex});
- if (props.limit === 0) {
- return;
- }
- const begin = this.state.startIndex;
- const end = Math.min(begin + props.limit, props.items.length);
- if (begin <= selectionIndex && selectionIndex < end) {
- return;
- } else if (selectionIndex >= end) {
- if (selectionIndex >= props.items.length) {
- throw Error(`Error: selection index (${selectionIndex}) outside items range (${props.items.length}).`);
- }
- const startIndex = selectionIndex - props.limit + 1;
- this.setState({startIndex});
- } else { // if (selectionIndex < begin)
- this.setState({startIndex: selectionIndex});
- }
- }
-
- _handlePageChange(keyName) {
- if (this.state.query.trim() !== '') {
- return; // Do not page when selecting
- }
- if (this.props.limit === 0) {
- return; // Nothing to page
- }
- let newIndex = this.state.selectionIndex;
- if (keyName === 'pageup') {
- newIndex = Math.max(this.state.selectionIndex - this.props.limit + 1, 0);
- } else if (keyName === 'pagedown') {
- newIndex = Math.min(this.state.selectionIndex + this.props.limit - 1, this.props.items.length - 1);
- }
- this._changeSelection(newIndex - this.state.selectionIndex);
- }
-
- getMatchPosition(label, query) {
- if (this.props.caseSensitive) {
- return label.indexOf(query);
- } else {
- return label.toLowerCase().indexOf(query.toLowerCase());
- }
- }
-
- getValue() {
- return this.props.items[this.state.selectionIndex] || defaultValue;
- }
-}
-
-QuickSearch.initialState = {
- query: '',
- hasMatch: true,
- selectionIndex: 0,
- startIndex: 0,
-};
-
-QuickSearch.defaultProps = {
- items: [],
- onSelect: () => {}, // no-op
- focus: true,
- caseSensitive: false,
- limit: 0,
- forceMatchingQuery: false,
- clearQueryChars: [
- '\u0015', // Ctrl + U
- '\u0017', // Ctrl + W
- ],
- initialSelectionIndex: 0,
- indicatorComponent: IndicatorComponent,
- itemComponent: ItemComponent,
- highlightComponent: HighlightComponent,
- statusComponent: StatusComponent,
-};
-
-module.exports = QuickSearch;
diff --git a/src/QuickSearchInput.tsx b/src/QuickSearchInput.tsx
new file mode 100644
index 0000000..c44d5b2
--- /dev/null
+++ b/src/QuickSearchInput.tsx
@@ -0,0 +1,301 @@
+import React, { Component, FunctionComponent, FC, useState, useEffect, useRef, useMemo, PropsWithChildren } from 'react';
+import { Color, useStdin, Text, Box } from 'ink';
+import hasAnsi from 'has-ansi';
+import isEqual from 'lodash.isequal';
+// @ts-ignore This module makes stdin emit keypress events,
+// that's it. Hasn't been published in six years, no types
+// available.
+import keypress from 'keypress';
+
+const defaultValue = { label: '' }; // Used as return for empty array
+
+
+export type IsSelected = PropsWithChildren<{
+ isSelected: boolean
+}>
+
+export interface ItemProps extends IsSelected {
+ item: Item
+ isHighlighted: boolean | undefined
+}
+// For the following four, whitespace is important
+const IndicatorComponent: FC = ({ isSelected }) => {
+ return {isSelected ? '>' : ' '} ;
+};
+
+const ItemComponent: FC = ({ isSelected, children }) => (
+ {children}
+);
+
+const HighlightComponent: FC = ({ children }) => (
+ {children}
+);
+
+export interface StatusProps {
+ hasMatch: boolean
+ label?: string
+}
+
+const StatusComponent: FC = ({ hasMatch, children, label }) => (
+ {`${label || 'Query'}: `}{children}
+);
+
+export interface Item {
+ label: string
+ value?: string | number
+}
+
+interface KeyPress {
+ name: string
+ sequence: string
+ shift: boolean
+}
+
+export interface QuickSearchProps {
+ onSelect: (item: Item) => void
+ items: Item[]
+ label?: string
+ focus?: boolean
+ caseSensitive?: boolean
+ limit?: number
+ forceMatchingQuery?: boolean
+ clearQueryChars?: string[]
+ initialSelectionIndex?: number
+ indicatorComponent?: FunctionComponent
+ itemComponent?: FunctionComponent
+ highlightComponent?: FunctionComponent
+ statusComponent?: FunctionComponent
+}
+
+export const QuickSearch: FC = (props) => {
+ const {
+ items, onSelect, focus, clearQueryChars, limit,
+ indicatorComponent, itemComponent, highlightComponent,
+ statusComponent, label, forceMatchingQuery
+ } = Object.assign({}, defaultProps, props);
+
+ // Map prop components onto capitalized names, required
+ // for JSX to recognize em
+ const Indicator = indicatorComponent;
+ const Item = itemComponent;
+ const Highlight = highlightComponent;
+ const Status = statusComponent;
+
+ const [windowIndices, setWindowIndices] = useState({
+ selection: 0,
+ start: 0
+ })
+ const [query, setQuery] = useState('');
+
+ const matchingItems = useMemo(() => {
+ return getMatchingItems();
+ }, [items, query]);
+ const usingLimitedView = limit !== 0 && matchingItems.length > limit;
+
+ const inkStdin = useStdin();
+ useEffect(function listenToRawKeyboard() {
+ keypress(inkStdin.stdin);
+ if (inkStdin.isRawModeSupported) inkStdin.setRawMode(true);
+ inkStdin.stdin.addListener('keypress', handleKeyPress)
+ return () => {
+ inkStdin.stdin.removeListener('keypress', handleKeyPress);
+ if (inkStdin.isRawModeSupported) inkStdin.setRawMode(false);
+ }
+ }, [inkStdin, query, items, windowIndices])
+
+ const itemRef = useRef(items);
+ useEffect(function resetForNewItems() {
+ if (!isEqual(items, itemRef.current)) {
+ itemRef.current = items;
+ setWindowIndices({
+ selection: 0, start: 0
+ })
+ setQuery('');
+ }
+ }, [items])
+
+ const getValue = () => {
+ return matchingItems[windowIndices.selection] || defaultValue;
+ }
+
+
+ function getMatchIndex(label: string, query: string) {
+ return props.caseSensitive ?
+ label.indexOf(query) :
+ label.toLowerCase().indexOf(query.toLowerCase())
+ }
+
+ function getMatchingItems(alternateQuery?: string) {
+ const matchQuery = alternateQuery || query;
+ if (matchQuery === '') return items;
+ return items.filter(item => getMatchIndex(item.label, matchQuery) >= 0);
+ }
+
+ function removeCharFromQuery() {
+ setQuery((query) => query.slice(0, -1) as string)
+ }
+
+ function addCharToQuery(newChar: string) {
+ setQuery((query) => {
+ let newQuery = query + newChar;
+ let newMatching = getMatchingItems(newQuery);
+ if (newMatching.length === 0 && forceMatchingQuery) {
+ return query;
+ } else {
+ setWindowIndices({ start: 0, selection: 0 })
+ return newQuery
+ }
+ })
+ }
+
+ function selectUp() {
+ setWindowIndices((windowIndices) => {
+ const { selection, start } = windowIndices;
+ let newSelection = selection;
+ let newStart = start;
+ if (selection === 0) {
+ // Wrap around to the bottom
+ newSelection = matchingItems.length - 1;
+ if (usingLimitedView) {
+ newStart = matchingItems.length - limit;
+ }
+ } else {
+ // Go up, potentially moving up window, unless
+ // it is already 0.
+ newSelection -= 1;
+ if (usingLimitedView) {
+ if (selection - start <= 1 && start > 0) {
+ newStart -= 1;
+ }
+ }
+ }
+ return {
+ selection: newSelection,
+ start: newStart
+ }
+ })
+ }
+
+ function selectDown() {
+ setWindowIndices(({ start, selection }) => {
+ let newStart = start;
+ let newSelection = selection;
+ if (selection === matchingItems.length - 1) {
+ // Wrap around to the top
+ newSelection = 0;
+ if (newStart !== 0) newStart = 0;
+ } else {
+ // Go down, potentially moving window
+ newSelection++;
+ if (limit && matchingItems.length > limit && newSelection - newStart >= limit - 1) {
+ newStart += 1;
+ }
+ }
+ return {
+ start: newStart,
+ selection: newSelection
+ }
+ })
+ }
+
+ function handleKeyPress(ch: string, key: KeyPress) {
+ if (!focus) return;
+ if (!key && parseInt(ch) !== NaN) {
+ addCharToQuery(ch);
+ return;
+ }
+ if (clearQueryChars.indexOf(ch) !== -1) {
+ setQuery('');
+ } else if (key.name === 'return') {
+ onSelect(getValue());
+ } else if (key.name === 'backspace') {
+ removeCharFromQuery();
+ } else if (key.name === 'up') {
+ selectUp();
+ } else if (key.name === 'down') {
+ selectDown();
+ } else if (key.name === 'tab') {
+ if (key.shift === false) {
+ selectDown();
+ } else {
+ selectUp();
+ }
+ } else if (hasAnsi(key.sequence)) {
+ // Ignore fancy Ansi escape codes
+ } else {
+ addCharToQuery(ch);
+ }
+ }
+
+ const begin = windowIndices.start;
+ let end = items.length;
+ if (limit !== 0) end = Math.min(begin + limit, items.length);
+ const visibleItems = matchingItems.slice(begin, end);
+
+ return (
+
+
+ 0}>
+ {query}
+
+
+ {
+ visibleItems.length === 0 ?
+
+ No matches :
+
+ visibleItems.map((item) => {
+ const isSelected = matchingItems.indexOf(item) === windowIndices.selection;
+ const isHighlighted = undefined;
+ const itemProps: ItemProps = { isSelected, isHighlighted, item };
+ const label = item.label;
+
+ const queryStart = getMatchIndex(label, query);
+ const queryEnd = queryStart + query.length;
+ let labelComponent;
+ itemProps.isHighlighted = true;
+ const preMatch = label.slice(0, queryStart);
+ const match = label.slice(queryStart, queryEnd);
+ const postMatch = label.slice(queryEnd);
+ labelComponent = (
+ {preMatch}{match}{postMatch}
+ )
+ return (
+
+ -
+
+ {labelComponent}
+
+
+ )
+ })
+ }
+ {
+ !usingLimitedView ? null : (
+
+ Viewing {begin}-{end} of {matchingItems.length} matching items ({items.length} items overall)
+
+ )
+ }
+
+ )
+}
+
+const defaultProps = {
+ focus: true,
+ caseSensitive: false,
+ limit: 0,
+ forceMatchingQuery: true,
+ clearQueryChars: [
+ '\u0015', // Ctrl + U
+ '\u0017', // Ctrl + W
+ ],
+ initialSelectionIndex: 0,
+ indicatorComponent: IndicatorComponent,
+ itemComponent: ItemComponent,
+ highlightComponent: HighlightComponent,
+ statusComponent: StatusComponent
+};
+
+
+export default QuickSearch;
\ No newline at end of file
diff --git a/src/examples/Example1.tsx b/src/examples/Example1.tsx
new file mode 100644
index 0000000..2faeaa0
--- /dev/null
+++ b/src/examples/Example1.tsx
@@ -0,0 +1,33 @@
+import React, { FC, useState } from 'react';
+import { render, Color, Text, Box, Static } from 'ink';
+
+import QuickSearch, { Item } from '../QuickSearchInput';
+
+export const Example1Name = 'Basic';
+
+export const Example1: FC = () => {
+ const [selectedValue, setSelectedValue] = useState('');
+ return (
+ <>
+ Example 1: {Example1Name}
+ Selected item is {selectedValue}
+ {'\n\n'}
+ setSelectedValue(item.label),
+ }} />
+ >
+ )
+}
+
+if (require.main && require.main.filename === __filename) {
+ render();
+}
\ No newline at end of file
diff --git a/src/examples/Example2.tsx b/src/examples/Example2.tsx
new file mode 100644
index 0000000..161cb7c
--- /dev/null
+++ b/src/examples/Example2.tsx
@@ -0,0 +1,37 @@
+import React, { FC, useState } from 'react';
+import { render, Color, Text, Box, Static } from 'ink';
+
+import QuickSearch from '../QuickSearchInput';
+
+export const Example2Name = 'Case-Sensitive, No Query/Status Element';
+
+export const Example2: FC = () => {
+ const [selectedValue, setSelectedValue] = useState('');
+ return (
+ <>
+ Example 2: {Example2Name}
+ Selected item is {selectedValue}
+ { '\n\n' }
+ setSelectedValue(d.label),
+ caseSensitive: true,
+ // Hide the statusComponent
+ statusComponent: () => <>>,
+ }} />
+ >
+ )
+
+}
+
+if (require.main && require.main.filename === __filename) {
+ render();
+}
\ No newline at end of file
diff --git a/src/examples/Example3.tsx b/src/examples/Example3.tsx
new file mode 100644
index 0000000..de66e89
--- /dev/null
+++ b/src/examples/Example3.tsx
@@ -0,0 +1,49 @@
+import React, { FC, useState } from 'react';
+import { render, Color, Text, Box, Static } from 'ink';
+
+import QuickSearch, { ItemProps } from '../QuickSearchInput';
+
+export const Example3Name = 'Case-Sensitive, Hiding Status & non-selected Items';
+
+// Hide elements which are not selected
+const ItemComponent = ({isHighlighted, isSelected, children}:ItemProps) => {
+ if (!isHighlighted) {
+ return <>>;
+ }
+ return {children};
+};
+
+const StatusComponent = () => <>>; // No-op
+
+export const Example3: FC = () => {
+ const [selectedValue, setSelectedValue] = useState('');
+ return (
+ <>
+ Example 3: {Example3Name}
+ Selected item is {selectedValue}
+ { '\n\n' }
+ setSelectedValue(d.label),
+ caseSensitive: true,
+ // Hide the statusComponent
+ statusComponent: StatusComponent,
+ // Only show items which are selected
+ itemComponent: ItemComponent
+ }} />
+ >
+ )
+
+}
+
+if (require.main && require.main.filename === __filename) {
+ render();
+}
\ No newline at end of file
diff --git a/examples/example4.jsx b/src/examples/Example4.tsx
similarity index 74%
rename from examples/example4.jsx
rename to src/examples/Example4.tsx
index 9f220d7..d0bbb15 100644
--- a/examples/example4.jsx
+++ b/src/examples/Example4.tsx
@@ -1,15 +1,18 @@
-/**
- * Limiting rows to current terminal size
- */
+import React, { FC, useState } from 'react';
+import { render, Color, Text } from 'ink';
+import termSize from 'term-size';
+import QuickSearch from '../QuickSearchInput';
-const {h, render, Component} = require('ink');
-const termSize = require('term-size');
+export const Example4Name = 'Long list limited to terminal size; non-matching queries are not allowed';
-const QuickSearch = require('import-jsx')('../src/QuickSearch.jsx');
-
-class Example3 extends Component {
- render() {
- const props = {
+export const Example4: FC = () => {
+ const [selectedValue, setSelectedValue] = useState('');
+ return (
+ <>
+ Example 4: {Example4Name}
+ Selected item is {selectedValue}
+ { '\n' }
+ {},
- statusComponent: () => ,
+ onSelect: d => setSelectedValue(d.label),
forceMatchingQuery: true,
- limit: termSize().rows - 2, // One for clear screen, one for cursor (Could be 1 more for statusComponent if that exists)
- };
+ limit: termSize().rows - 8, // One for clear screen, one for cursor (Could be 1 more for statusComponent if that exists)
+ }} />
+ >
+ )
- return
-
- ;
- }
}
-console.log('\x1Bc'); // Clear screen
-render();
+if (require.main && require.main.filename === __filename) {
+ render();
+}
\ No newline at end of file
diff --git a/src/examples/ExampleDirectory.tsx b/src/examples/ExampleDirectory.tsx
new file mode 100644
index 0000000..5bbf10d
--- /dev/null
+++ b/src/examples/ExampleDirectory.tsx
@@ -0,0 +1,39 @@
+import React, { FC, useState } from 'react';
+import { render, Color, Text, Box, Static } from 'ink';
+import QuickSearch, { Item } from '../QuickSearchInput';
+import { Example1, Example1Name } from './Example1';
+import { Example2, Example2Name } from './Example2';
+import { Example3, Example3Name } from './Example3';
+import { Example4, Example4Name } from './Example4';
+
+const ExampleDirectory: FC = () => {
+ const [selectedExample, setSelectedExample] = useState('');
+
+ if (selectedExample === Example1Name) {
+ return
+ } else if (selectedExample === Example2Name) {
+ return
+ } else if (selectedExample === Example3Name) {
+ return
+ } else if (selectedExample === Example4Name) {
+ return
+ } else {
+ return (
+ <>
+ Which example would you like to explore?
+ {'\n\n'}
+ setSelectedExample(item.label),
+ }} />
+ >
+ )
+ }
+}
+
+render();
\ No newline at end of file
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..81d939f
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,31 @@
+{
+ "include": [
+ "src/**/*"
+ ],
+ "exclude": ["node_modules", "build"],
+ "compileOnSave": true,
+ "compilerOptions": {
+ "target": "es5", // Compatible with older browsers
+ "module": "commonjs", // Compatible with both Node.js and browser
+ "moduleResolution": "node", // Tell tsc to look in node_modules for modules
+ "sourceMap": false, // Creates *.js.map files
+ "inlineSourceMap": true,
+ "strict": true, // Strict types, eg. prohibits `var x=0; x=null`
+ "alwaysStrict": true, // Enable JavaScript's "use strict" mode
+
+ "outDir": "build",
+ "sourceRoot": "./src",
+ "noImplicitAny": true,
+ "allowSyntheticDefaultImports": true,
+ "esModuleInterop": true,
+ "typeRoots": ["node_modules/@types"],
+ "jsx": "react"
+ },
+ "typedocOptions": {
+ "mode" : "modules",
+ "out" : "docs/build",
+ "categorizeByGroup" : true,
+ "theme" : "docs/themeoverride",
+ "excludeNotExported" : false
+ }
+}
\ No newline at end of file