@jakubmazanec/cli 0.1.0-6cee770 → 0.1.0-7dbb232
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ui/form/SelectInput.d.ts +2 -2
- package/dist/ui/form/SelectInput.js +19 -28
- package/dist/ui/form/SelectInput.js.map +1 -1
- package/dist/ui/form/TextInput.d.ts +1 -1
- package/dist/ui/form/TextInput.js +4 -7
- package/dist/ui/form/TextInput.js.map +1 -1
- package/package.json +5 -5
- package/src/ui/form/SelectInput.tsx +21 -23
- package/src/ui/form/TextInput.tsx +5 -10
|
@@ -9,7 +9,7 @@ type SelectInputProps = BoxProps & {
|
|
|
9
9
|
label: string;
|
|
10
10
|
/** Items to display in a list. */
|
|
11
11
|
items: SelectInputItem[];
|
|
12
|
-
/** Should component listen to
|
|
12
|
+
/** Should component listen to user's input? Useful in case there are multiple input components at the same time and input must be handled only by a specific component. */
|
|
13
13
|
focus?: boolean | undefined;
|
|
14
14
|
/** Index of initially selected item. */
|
|
15
15
|
initialIndex?: number | undefined;
|
|
@@ -25,5 +25,5 @@ type SelectInputProps = BoxProps & {
|
|
|
25
25
|
/**
|
|
26
26
|
* Select input component.
|
|
27
27
|
*/
|
|
28
|
-
export declare function SelectInput({ label, items, focus, initialIndex, limit:
|
|
28
|
+
export declare function SelectInput({ label, items, focus, initialIndex, limit: userDefinedLimit, onSelect, onHighlight, isComplete, ...boxProps }: SelectInputProps): JSX.Element;
|
|
29
29
|
export {};
|
|
@@ -1,38 +1,38 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Box, Text, useInput } from 'ink';
|
|
3
3
|
import _ from 'lodash';
|
|
4
|
-
import { useState, useEffect, useRef
|
|
4
|
+
import { useState, useEffect, useRef } from 'react';
|
|
5
5
|
import * as colors from '../colors.js';
|
|
6
6
|
import * as icons from '../icons.js';
|
|
7
7
|
import { rotate } from '../internals.js';
|
|
8
8
|
/**
|
|
9
9
|
* Select input component.
|
|
10
|
-
*/ export function SelectInput({ label, items = [], focus = true, initialIndex = 0, limit:
|
|
10
|
+
*/ export function SelectInput({ label, items = [], focus = true, initialIndex = 0, limit: userDefinedLimit, onSelect, onHighlight, isComplete, ...boxProps }) {
|
|
11
11
|
var _items_selectedIndex, _items_selectedIndex1;
|
|
12
|
-
let [
|
|
12
|
+
let [rotatedIndex, setRotatedIndex] = useState(0);
|
|
13
13
|
let [selectedIndex, setSelectedIndex] = useState(initialIndex);
|
|
14
|
-
let hasLimit = typeof
|
|
15
|
-
let limit = hasLimit ? Math.min(
|
|
14
|
+
let hasLimit = typeof userDefinedLimit === 'number' && items.length > userDefinedLimit;
|
|
15
|
+
let limit = hasLimit ? Math.min(userDefinedLimit, items.length) : items.length;
|
|
16
16
|
let previousItems = useRef(items);
|
|
17
17
|
useEffect(()=>{
|
|
18
18
|
if (!_.isEqual(previousItems.current.map((item)=>item.value), items.map((item)=>item.value))) {
|
|
19
|
-
|
|
19
|
+
setRotatedIndex(0);
|
|
20
20
|
setSelectedIndex(0);
|
|
21
21
|
}
|
|
22
22
|
previousItems.current = items;
|
|
23
23
|
}, [
|
|
24
24
|
items
|
|
25
25
|
]);
|
|
26
|
-
|
|
26
|
+
useInput((input, key)=>{
|
|
27
27
|
if (input === 'k' || key.upArrow) {
|
|
28
28
|
let lastIndex = (hasLimit ? limit : items.length) - 1;
|
|
29
|
-
let
|
|
29
|
+
let isAtFirstIndex = selectedIndex === 0;
|
|
30
30
|
let nextIndex = hasLimit ? selectedIndex : lastIndex;
|
|
31
|
-
let
|
|
32
|
-
let nextSelectedIndex =
|
|
33
|
-
|
|
31
|
+
let nextRotatedIndex = isAtFirstIndex ? rotatedIndex + 1 : rotatedIndex;
|
|
32
|
+
let nextSelectedIndex = isAtFirstIndex ? nextIndex : selectedIndex - 1;
|
|
33
|
+
setRotatedIndex(nextRotatedIndex);
|
|
34
34
|
setSelectedIndex(nextSelectedIndex);
|
|
35
|
-
let slicedItems = hasLimit ? rotate(items,
|
|
35
|
+
let slicedItems = hasLimit ? rotate(items, nextRotatedIndex).slice(0, limit) : items;
|
|
36
36
|
if (typeof onHighlight === 'function') {
|
|
37
37
|
onHighlight(slicedItems[nextSelectedIndex]);
|
|
38
38
|
}
|
|
@@ -40,34 +40,25 @@ import { rotate } from '../internals.js';
|
|
|
40
40
|
if (input === 'j' || key.downArrow) {
|
|
41
41
|
let isAtLastIndex = selectedIndex === (hasLimit ? limit : items.length) - 1;
|
|
42
42
|
let nextIndex = hasLimit ? selectedIndex : 0;
|
|
43
|
-
let
|
|
43
|
+
let nextRotatedIndex = isAtLastIndex ? rotatedIndex - 1 : rotatedIndex;
|
|
44
44
|
let nextSelectedIndex = isAtLastIndex ? nextIndex : selectedIndex + 1;
|
|
45
|
-
|
|
45
|
+
setRotatedIndex(nextRotatedIndex);
|
|
46
46
|
setSelectedIndex(nextSelectedIndex);
|
|
47
|
-
let slicedItems = hasLimit ? rotate(items,
|
|
47
|
+
let slicedItems = hasLimit ? rotate(items, nextRotatedIndex).slice(0, limit) : items;
|
|
48
48
|
if (typeof onHighlight === 'function') {
|
|
49
49
|
onHighlight(slicedItems[nextSelectedIndex]);
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
52
|
if (key.return) {
|
|
53
|
-
let slicedItems = hasLimit ? rotate(items,
|
|
53
|
+
let slicedItems = hasLimit ? rotate(items, rotatedIndex).slice(0, limit) : items;
|
|
54
54
|
if (typeof onSelect === 'function') {
|
|
55
55
|
onSelect(slicedItems[selectedIndex]);
|
|
56
56
|
}
|
|
57
57
|
}
|
|
58
|
-
},
|
|
59
|
-
hasLimit,
|
|
60
|
-
limit,
|
|
61
|
-
rotateIndex,
|
|
62
|
-
selectedIndex,
|
|
63
|
-
items,
|
|
64
|
-
onSelect,
|
|
65
|
-
onHighlight
|
|
66
|
-
]);
|
|
67
|
-
useInput(inputHandler, {
|
|
58
|
+
}, {
|
|
68
59
|
isActive: focus
|
|
69
60
|
});
|
|
70
|
-
let
|
|
61
|
+
let visibleItems = hasLimit ? rotate(items, rotatedIndex).slice(0, limit) : items;
|
|
71
62
|
return /*#__PURE__*/ _jsxs(Box, {
|
|
72
63
|
...boxProps,
|
|
73
64
|
children: [
|
|
@@ -92,7 +83,7 @@ import { rotate } from '../internals.js';
|
|
|
92
83
|
children: ((_items_selectedIndex = items[selectedIndex]) === null || _items_selectedIndex === void 0 ? void 0 : _items_selectedIndex.label) ?? String((_items_selectedIndex1 = items[selectedIndex]) === null || _items_selectedIndex1 === void 0 ? void 0 : _items_selectedIndex1.value)
|
|
93
84
|
}) : /*#__PURE__*/ _jsx(Box, {
|
|
94
85
|
flexDirection: "column",
|
|
95
|
-
children:
|
|
86
|
+
children: visibleItems.map((item, index)=>{
|
|
96
87
|
let isSelected = index === selectedIndex;
|
|
97
88
|
return /*#__PURE__*/ _jsxs(Box, {
|
|
98
89
|
children: [
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/ui/form/SelectInput.tsx"],"sourcesContent":["import {Box, type BoxProps, Text, useInput, type Key} from 'ink';\nimport _ from 'lodash';\nimport {useState, useEffect, useRef
|
|
1
|
+
{"version":3,"sources":["../../../src/ui/form/SelectInput.tsx"],"sourcesContent":["import {Box, type BoxProps, Text, useInput, type Key} from 'ink';\nimport _ from 'lodash';\nimport {useState, useEffect, useRef} from 'react';\n\nimport {type SelectInputItem} from './SelectInputItem.js';\nimport * as colors from '../colors.js';\nimport * as icons from '../icons.js';\nimport {rotate} from '../internals.js';\n\n/**\n * Select input component props.\n */\ntype SelectInputProps = BoxProps & {\n /** Text to display as a label. */\n label: string;\n\n /** Items to display in a list. */\n items: SelectInputItem[];\n\n /** Should component listen to user's input? Useful in case there are multiple input components at the same time and input must be handled only by a specific component. */\n focus?: boolean | undefined;\n\n /** Index of initially selected item. */\n initialIndex?: number | undefined;\n\n /** Number of items to display. */\n limit?: number | undefined;\n\n /** Handler function that is called when item is selected. */\n onSelect?: ((item: SelectInputItem) => void) | undefined;\n\n /** Handler function that is called when item is highlighted. */\n onHighlight?: ((item: SelectInputItem) => void) | undefined;\n\n /** Indicates whether value has been accepted. Component then doesn't accept more input. */\n isComplete?: boolean | undefined;\n};\n\n/**\n * Select input component.\n */\nexport function SelectInput({\n label,\n items = [],\n focus = true,\n initialIndex = 0,\n limit: userDefinedLimit,\n onSelect,\n onHighlight,\n isComplete,\n ...boxProps\n}: SelectInputProps): JSX.Element {\n let [rotatedIndex, setRotatedIndex] = useState(0);\n let [selectedIndex, setSelectedIndex] = useState(initialIndex);\n let hasLimit = typeof userDefinedLimit === 'number' && items.length > userDefinedLimit;\n let limit = hasLimit ? Math.min(userDefinedLimit as number, items.length) : items.length;\n let previousItems = useRef<SelectInputItem[]>(items);\n\n useEffect(() => {\n if (\n !_.isEqual(\n previousItems.current.map((item) => item.value),\n items.map((item) => item.value),\n )\n ) {\n setRotatedIndex(0);\n setSelectedIndex(0);\n }\n\n previousItems.current = items;\n }, [items]);\n\n useInput(\n (input: string, key: Key) => {\n if (input === 'k' || key.upArrow) {\n let lastIndex = (hasLimit ? limit : items.length) - 1;\n let isAtFirstIndex = selectedIndex === 0;\n let nextIndex = hasLimit ? selectedIndex : lastIndex;\n let nextRotatedIndex = isAtFirstIndex ? rotatedIndex + 1 : rotatedIndex;\n let nextSelectedIndex = isAtFirstIndex ? nextIndex : selectedIndex - 1;\n\n setRotatedIndex(nextRotatedIndex);\n setSelectedIndex(nextSelectedIndex);\n\n let slicedItems = hasLimit ? rotate(items, nextRotatedIndex).slice(0, limit) : items;\n\n if (typeof onHighlight === 'function') {\n onHighlight(slicedItems[nextSelectedIndex] as SelectInputItem);\n }\n }\n\n if (input === 'j' || key.downArrow) {\n let isAtLastIndex = selectedIndex === (hasLimit ? limit : items.length) - 1;\n let nextIndex = hasLimit ? selectedIndex : 0;\n let nextRotatedIndex = isAtLastIndex ? rotatedIndex - 1 : rotatedIndex;\n let nextSelectedIndex = isAtLastIndex ? nextIndex : selectedIndex + 1;\n\n setRotatedIndex(nextRotatedIndex);\n setSelectedIndex(nextSelectedIndex);\n\n let slicedItems = hasLimit ? rotate(items, nextRotatedIndex).slice(0, limit) : items;\n\n if (typeof onHighlight === 'function') {\n onHighlight(slicedItems[nextSelectedIndex] as SelectInputItem);\n }\n }\n\n if (key.return) {\n let slicedItems = hasLimit ? rotate(items, rotatedIndex).slice(0, limit) : items;\n\n if (typeof onSelect === 'function') {\n onSelect(slicedItems[selectedIndex] as SelectInputItem);\n }\n }\n },\n {isActive: focus},\n );\n\n let visibleItems: SelectInputItem[] = hasLimit\n ? rotate(items, rotatedIndex).slice(0, limit)\n : items;\n\n return (\n <Box {...boxProps}>\n <Box marginRight={1}>\n <Text>{isComplete ? icons.success : icons.question}</Text>\n </Box>\n\n <Box flexDirection=\"column\">\n <Box marginRight={1}>\n <Text color={colors.highlighted}>{label}</Text>\n </Box>\n {isComplete ? (\n <Text color={colors.body}>\n {items[selectedIndex]?.label ?? String(items[selectedIndex]?.value)}\n </Text>\n ) : (\n <Box flexDirection=\"column\">\n {visibleItems.map((item, index) => {\n let isSelected = index === selectedIndex;\n\n return (\n <Box key={item.value}>\n <Box marginRight={1}>\n {isSelected ? (\n <Text color={colors.selected}>{icons.selectedCircle}</Text>\n ) : (\n <Text color={colors.unselected}>{icons.unselectedCircle}</Text>\n )}\n </Box>\n <Text color={isSelected ? colors.highlighted : colors.muted}>{item.label}</Text>\n </Box>\n );\n })}\n </Box>\n )}\n </Box>\n </Box>\n );\n}\n"],"names":["Box","Text","useInput","_","useState","useEffect","useRef","colors","icons","rotate","SelectInput","label","items","focus","initialIndex","limit","userDefinedLimit","onSelect","onHighlight","isComplete","boxProps","rotatedIndex","setRotatedIndex","selectedIndex","setSelectedIndex","hasLimit","length","Math","min","previousItems","isEqual","current","map","item","value","input","key","upArrow","lastIndex","isAtFirstIndex","nextIndex","nextRotatedIndex","nextSelectedIndex","slicedItems","slice","downArrow","isAtLastIndex","return","isActive","visibleItems","marginRight","success","question","flexDirection","color","highlighted","body","String","index","isSelected","selected","selectedCircle","unselected","unselectedCircle","muted"],"mappings":"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"}
|
|
@@ -9,7 +9,7 @@ export type TextInputProps = BoxProps & {
|
|
|
9
9
|
placeholder?: string | undefined;
|
|
10
10
|
/** Value. */
|
|
11
11
|
value: string;
|
|
12
|
-
/** Should component listen to Listen to user's input? Useful in case there are multiple input components at the same time and input must be
|
|
12
|
+
/** Should component listen to Listen to user's input? Useful in case there are multiple input components at the same time and input must be handled only by a specific component. */
|
|
13
13
|
focus?: boolean | undefined;
|
|
14
14
|
/** Replace all chars and mask the value. Useful for password inputs. */
|
|
15
15
|
mask?: string | undefined;
|
|
@@ -35,18 +35,15 @@ import * as icons from '../icons.js';
|
|
|
35
35
|
let value = mask ? mask.repeat(originalValue.length) : originalValue;
|
|
36
36
|
let renderedValue = value;
|
|
37
37
|
let renderedPlaceholder = placeholder ? chalk[colors.muted](placeholder) : undefined;
|
|
38
|
-
// fake mouse cursor; dealing with with actual cursor and ANSI escape codes is inconvinient
|
|
39
38
|
if (showCursor && focus) {
|
|
40
39
|
renderedPlaceholder = placeholder.length > 0 ? chalk.inverse(placeholder[0]) + chalk[colors.muted](placeholder.slice(1)) : chalk.inverse(' ');
|
|
41
40
|
renderedValue = value.length > 0 ? '' : chalk.inverse(' ');
|
|
42
|
-
let i
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
renderedValue += chalk.inverse(char);
|
|
41
|
+
for (let [i, character] of Object.entries(value)){
|
|
42
|
+
if (Number(i) >= state.cursorOffset && Number(i) <= state.cursorOffset) {
|
|
43
|
+
renderedValue += chalk.inverse(character);
|
|
46
44
|
} else {
|
|
47
|
-
renderedValue +=
|
|
45
|
+
renderedValue += character;
|
|
48
46
|
}
|
|
49
|
-
i += 1;
|
|
50
47
|
}
|
|
51
48
|
if (value.length > 0 && state.cursorOffset === value.length) {
|
|
52
49
|
renderedValue += chalk.inverse(' ');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/ui/form/TextInput.tsx"],"sourcesContent":["// TODO: resolve\n/* eslint-disable complexity -- TODO */\nimport chalk from 'chalk';\nimport {Box, type BoxProps, Text, useInput} from 'ink';\nimport {useEffect, useState} from 'react';\n\nimport * as colors from '../colors.js';\nimport * as icons from '../icons.js';\n\n/**\n * Text input component props.\n */\nexport type TextInputProps = BoxProps & {\n /** Text to display as a label. */\n label: string;\n\n /** Text to display when `value` is empty. */\n placeholder?: string | undefined;\n\n /** Value. */\n value: string;\n\n /** Should component listen to Listen to user's input? Useful in case there are multiple input components at the same time and input must be \"routed\" to a specific component. */\n focus?: boolean | undefined;\n\n /** Replace all chars and mask the value. Useful for password inputs. */\n mask?: string | undefined;\n\n /** Show cursor and allow navigation using arrow keys. */\n showCursor?: boolean | undefined;\n\n /** Validation function that is called when value changes. If this function throws an error*/\n validate?: ((value: string) => void) | undefined;\n\n /** Handler function that is called when value changes. */\n onChange: (value: string) => void;\n\n /** Handler function that is called when enter is pressed. */\n onSubmit?: ((value: string) => void) | undefined;\n\n /** Indicates whether value has been accepted. Component then doesn't accept more input. */\n isComplete?: boolean | undefined;\n};\n\n/**\n * Text input component.\n */\nexport function TextInput({\n label,\n validate,\n isComplete,\n value: originalValue,\n placeholder = '',\n focus = true,\n mask,\n showCursor = true,\n onChange,\n onSubmit,\n ...boxProps\n}: TextInputProps) {\n let [validationError, setValidationError] = useState('');\n let [state, setState] = useState({\n cursorOffset: (originalValue || '').length,\n cursorWidth: 0,\n });\n\n useEffect(() => {\n setState((previousState) => {\n if (!focus || !showCursor) {\n return previousState;\n }\n\n let newValue = originalValue || '';\n\n if (previousState.cursorOffset > newValue.length - 1) {\n return {\n cursorOffset: newValue.length,\n cursorWidth: 0,\n };\n }\n\n return previousState;\n });\n }, [originalValue, focus, showCursor]);\n\n let value = mask ? mask.repeat(originalValue.length) : originalValue;\n let renderedValue = value;\n let renderedPlaceholder = placeholder ? chalk[colors.muted](placeholder) : undefined;\n\n // fake mouse cursor; dealing with with actual cursor and ANSI escape codes is inconvinient\n if (showCursor && focus) {\n renderedPlaceholder =\n placeholder.length > 0\n ? chalk.inverse(placeholder[0]) + chalk[colors.muted](placeholder.slice(1))\n : chalk.inverse(' ');\n\n renderedValue = value.length > 0 ? '' : chalk.inverse(' ');\n\n let i = 0;\n\n for (let char of value) {\n if (i >= state.cursorOffset && i <= state.cursorOffset) {\n renderedValue += chalk.inverse(char);\n } else {\n renderedValue += char;\n }\n\n i += 1;\n }\n\n if (value.length > 0 && state.cursorOffset === value.length) {\n renderedValue += chalk.inverse(' ');\n }\n }\n\n useInput(\n (input, key) => {\n if (\n key.upArrow ||\n key.downArrow ||\n (key.ctrl && input === 'c') ||\n (key.shift && key.tab) ||\n key.tab\n ) {\n return;\n }\n\n if (key.return) {\n if (onSubmit) {\n if (validate) {\n try {\n validate(originalValue);\n } catch (error: unknown) {\n setValidationError((error as Error).message);\n\n return;\n }\n }\n\n if (validationError) {\n setValidationError('');\n } else {\n onSubmit(originalValue);\n }\n }\n\n return;\n }\n\n let nextCursorOffset = state.cursorOffset;\n let nextValue = originalValue;\n let nextCursorWidth = 0;\n\n if (key.leftArrow) {\n if (showCursor) {\n nextCursorOffset -= 1;\n }\n } else if (key.rightArrow) {\n if (showCursor) {\n nextCursorOffset += 1;\n }\n } else if (key.backspace || key.delete) {\n if (state.cursorOffset > 0) {\n nextValue =\n originalValue.slice(0, state.cursorOffset - 1) +\n originalValue.slice(state.cursorOffset, originalValue.length);\n\n nextCursorOffset -= 1;\n }\n } else {\n nextValue =\n originalValue.slice(0, state.cursorOffset) +\n input +\n originalValue.slice(state.cursorOffset, originalValue.length);\n\n nextCursorOffset += input.length;\n\n if (input.length > 1) {\n nextCursorWidth = input.length;\n }\n }\n\n if (state.cursorOffset < 0) {\n nextCursorOffset = 0;\n }\n\n if (state.cursorOffset > originalValue.length) {\n nextCursorOffset = originalValue.length;\n }\n\n setState({\n cursorOffset: nextCursorOffset,\n cursorWidth: nextCursorWidth,\n });\n\n if (nextValue !== originalValue) {\n onChange(nextValue);\n }\n },\n {isActive: focus},\n );\n\n return (\n <Box {...boxProps}>\n <Box marginRight={1}>\n <Text>{isComplete ? icons.success : icons.question}</Text>\n </Box>\n\n <Box flexDirection=\"column\">\n <Box marginRight={1}>\n <Text color={colors.highlighted}>{label}</Text>\n </Box>\n {isComplete ? (\n <Text color={colors.body}>{value}</Text>\n ) : (\n <Text color={colors.highlighted}>\n {placeholder ? (value.length > 0 ? renderedValue : renderedPlaceholder) : renderedValue}\n </Text>\n )}\n {validationError ? (\n <Box marginTop={0}>\n <Text color={colors.warning}>{validationError}</Text>\n </Box>\n ) : null}\n </Box>\n </Box>\n );\n}\n"],"names":["chalk","Box","Text","useInput","useEffect","useState","colors","icons","TextInput","label","validate","isComplete","value","originalValue","placeholder","focus","mask","showCursor","onChange","onSubmit","boxProps","validationError","setValidationError","state","setState","cursorOffset","length","cursorWidth","previousState","newValue","repeat","renderedValue","renderedPlaceholder","muted","undefined","inverse","slice","i","char","input","key","upArrow","downArrow","ctrl","shift","tab","return","error","message","nextCursorOffset","nextValue","nextCursorWidth","leftArrow","rightArrow","backspace","delete","isActive","marginRight","success","question","flexDirection","color","highlighted","body","marginTop","warning"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../src/ui/form/TextInput.tsx"],"sourcesContent":["// TODO: resolve\n/* eslint-disable complexity -- TODO */\nimport chalk from 'chalk';\nimport {Box, type BoxProps, Text, useInput} from 'ink';\nimport {useEffect, useState} from 'react';\n\nimport * as colors from '../colors.js';\nimport * as icons from '../icons.js';\n\n/**\n * Text input component props.\n */\nexport type TextInputProps = BoxProps & {\n /** Text to display as a label. */\n label: string;\n\n /** Text to display when `value` is empty. */\n placeholder?: string | undefined;\n\n /** Value. */\n value: string;\n\n /** Should component listen to Listen to user's input? Useful in case there are multiple input components at the same time and input must be handled only by a specific component. */\n focus?: boolean | undefined;\n\n /** Replace all chars and mask the value. Useful for password inputs. */\n mask?: string | undefined;\n\n /** Show cursor and allow navigation using arrow keys. */\n showCursor?: boolean | undefined;\n\n /** Validation function that is called when value changes. If this function throws an error*/\n validate?: ((value: string) => void) | undefined;\n\n /** Handler function that is called when value changes. */\n onChange: (value: string) => void;\n\n /** Handler function that is called when enter is pressed. */\n onSubmit?: ((value: string) => void) | undefined;\n\n /** Indicates whether value has been accepted. Component then doesn't accept more input. */\n isComplete?: boolean | undefined;\n};\n\n/**\n * Text input component.\n */\nexport function TextInput({\n label,\n validate,\n isComplete,\n value: originalValue,\n placeholder = '',\n focus = true,\n mask,\n showCursor = true,\n onChange,\n onSubmit,\n ...boxProps\n}: TextInputProps) {\n let [validationError, setValidationError] = useState('');\n let [state, setState] = useState({\n cursorOffset: (originalValue || '').length,\n cursorWidth: 0,\n });\n\n useEffect(() => {\n setState((previousState) => {\n if (!focus || !showCursor) {\n return previousState;\n }\n\n let newValue = originalValue || '';\n\n if (previousState.cursorOffset > newValue.length - 1) {\n return {\n cursorOffset: newValue.length,\n cursorWidth: 0,\n };\n }\n\n return previousState;\n });\n }, [originalValue, focus, showCursor]);\n\n let value = mask ? mask.repeat(originalValue.length) : originalValue;\n let renderedValue = value;\n let renderedPlaceholder = placeholder ? chalk[colors.muted](placeholder) : undefined;\n\n if (showCursor && focus) {\n renderedPlaceholder =\n placeholder.length > 0\n ? chalk.inverse(placeholder[0]) + chalk[colors.muted](placeholder.slice(1))\n : chalk.inverse(' ');\n\n renderedValue = value.length > 0 ? '' : chalk.inverse(' ');\n\n for (let [i, character] of Object.entries(value)) {\n if (Number(i) >= state.cursorOffset && Number(i) <= state.cursorOffset) {\n renderedValue += chalk.inverse(character);\n } else {\n renderedValue += character;\n }\n }\n\n if (value.length > 0 && state.cursorOffset === value.length) {\n renderedValue += chalk.inverse(' ');\n }\n }\n\n useInput(\n (input, key) => {\n if (\n key.upArrow ||\n key.downArrow ||\n (key.ctrl && input === 'c') ||\n (key.shift && key.tab) ||\n key.tab\n ) {\n return;\n }\n\n if (key.return) {\n if (onSubmit) {\n if (validate) {\n try {\n validate(originalValue);\n } catch (error: unknown) {\n setValidationError((error as Error).message);\n\n return;\n }\n }\n\n if (validationError) {\n setValidationError('');\n } else {\n onSubmit(originalValue);\n }\n }\n\n return;\n }\n\n let nextCursorOffset = state.cursorOffset;\n let nextValue = originalValue;\n let nextCursorWidth = 0;\n\n if (key.leftArrow) {\n if (showCursor) {\n nextCursorOffset -= 1;\n }\n } else if (key.rightArrow) {\n if (showCursor) {\n nextCursorOffset += 1;\n }\n } else if (key.backspace || key.delete) {\n if (state.cursorOffset > 0) {\n nextValue =\n originalValue.slice(0, state.cursorOffset - 1) +\n originalValue.slice(state.cursorOffset, originalValue.length);\n\n nextCursorOffset -= 1;\n }\n } else {\n nextValue =\n originalValue.slice(0, state.cursorOffset) +\n input +\n originalValue.slice(state.cursorOffset, originalValue.length);\n\n nextCursorOffset += input.length;\n\n if (input.length > 1) {\n nextCursorWidth = input.length;\n }\n }\n\n if (state.cursorOffset < 0) {\n nextCursorOffset = 0;\n }\n\n if (state.cursorOffset > originalValue.length) {\n nextCursorOffset = originalValue.length;\n }\n\n setState({\n cursorOffset: nextCursorOffset,\n cursorWidth: nextCursorWidth,\n });\n\n if (nextValue !== originalValue) {\n onChange(nextValue);\n }\n },\n {isActive: focus},\n );\n\n return (\n <Box {...boxProps}>\n <Box marginRight={1}>\n <Text>{isComplete ? icons.success : icons.question}</Text>\n </Box>\n\n <Box flexDirection=\"column\">\n <Box marginRight={1}>\n <Text color={colors.highlighted}>{label}</Text>\n </Box>\n {isComplete ? (\n <Text color={colors.body}>{value}</Text>\n ) : (\n <Text color={colors.highlighted}>\n {placeholder ? (value.length > 0 ? renderedValue : renderedPlaceholder) : renderedValue}\n </Text>\n )}\n {validationError ? (\n <Box marginTop={0}>\n <Text color={colors.warning}>{validationError}</Text>\n </Box>\n ) : null}\n </Box>\n </Box>\n );\n}\n"],"names":["chalk","Box","Text","useInput","useEffect","useState","colors","icons","TextInput","label","validate","isComplete","value","originalValue","placeholder","focus","mask","showCursor","onChange","onSubmit","boxProps","validationError","setValidationError","state","setState","cursorOffset","length","cursorWidth","previousState","newValue","repeat","renderedValue","renderedPlaceholder","muted","undefined","inverse","slice","i","character","Object","entries","Number","input","key","upArrow","downArrow","ctrl","shift","tab","return","error","message","nextCursorOffset","nextValue","nextCursorWidth","leftArrow","rightArrow","backspace","delete","isActive","marginRight","success","question","flexDirection","color","highlighted","body","marginTop","warning"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jakubmazanec/cli",
|
|
3
|
-
"version": "0.1.0-
|
|
3
|
+
"version": "0.1.0-7dbb232",
|
|
4
4
|
"description": "TypeScript-first library for building CLIs.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -27,10 +27,10 @@
|
|
|
27
27
|
"typecheck": "tsc --project tsconfig.typecheck.json"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@jakubmazanec/args": "1.0.1-
|
|
31
|
-
"@jakubmazanec/error": "2.0.1-
|
|
32
|
-
"@jakubmazanec/ts-utils": "2.1.0-
|
|
33
|
-
"@jakubmazanec/zod-utils": "1.0.0-
|
|
30
|
+
"@jakubmazanec/args": "1.0.1-7dbb232",
|
|
31
|
+
"@jakubmazanec/error": "2.0.1-7dbb232",
|
|
32
|
+
"@jakubmazanec/ts-utils": "2.1.0-7dbb232",
|
|
33
|
+
"@jakubmazanec/zod-utils": "1.0.0-7dbb232",
|
|
34
34
|
"chalk": "^5.2.0",
|
|
35
35
|
"lodash": "^4.17.21",
|
|
36
36
|
"react-error-boundary": "^4.0.7",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {Box, type BoxProps, Text, useInput, type Key} from 'ink';
|
|
2
2
|
import _ from 'lodash';
|
|
3
|
-
import {useState, useEffect, useRef
|
|
3
|
+
import {useState, useEffect, useRef} from 'react';
|
|
4
4
|
|
|
5
5
|
import {type SelectInputItem} from './SelectInputItem.js';
|
|
6
6
|
import * as colors from '../colors.js';
|
|
@@ -17,7 +17,7 @@ type SelectInputProps = BoxProps & {
|
|
|
17
17
|
/** Items to display in a list. */
|
|
18
18
|
items: SelectInputItem[];
|
|
19
19
|
|
|
20
|
-
/** Should component listen to
|
|
20
|
+
/** Should component listen to user's input? Useful in case there are multiple input components at the same time and input must be handled only by a specific component. */
|
|
21
21
|
focus?: boolean | undefined;
|
|
22
22
|
|
|
23
23
|
/** Index of initially selected item. */
|
|
@@ -44,16 +44,16 @@ export function SelectInput({
|
|
|
44
44
|
items = [],
|
|
45
45
|
focus = true,
|
|
46
46
|
initialIndex = 0,
|
|
47
|
-
limit:
|
|
47
|
+
limit: userDefinedLimit,
|
|
48
48
|
onSelect,
|
|
49
49
|
onHighlight,
|
|
50
50
|
isComplete,
|
|
51
51
|
...boxProps
|
|
52
52
|
}: SelectInputProps): JSX.Element {
|
|
53
|
-
let [
|
|
53
|
+
let [rotatedIndex, setRotatedIndex] = useState(0);
|
|
54
54
|
let [selectedIndex, setSelectedIndex] = useState(initialIndex);
|
|
55
|
-
let hasLimit = typeof
|
|
56
|
-
let limit = hasLimit ? Math.min(
|
|
55
|
+
let hasLimit = typeof userDefinedLimit === 'number' && items.length > userDefinedLimit;
|
|
56
|
+
let limit = hasLimit ? Math.min(userDefinedLimit as number, items.length) : items.length;
|
|
57
57
|
let previousItems = useRef<SelectInputItem[]>(items);
|
|
58
58
|
|
|
59
59
|
useEffect(() => {
|
|
@@ -63,26 +63,26 @@ export function SelectInput({
|
|
|
63
63
|
items.map((item) => item.value),
|
|
64
64
|
)
|
|
65
65
|
) {
|
|
66
|
-
|
|
66
|
+
setRotatedIndex(0);
|
|
67
67
|
setSelectedIndex(0);
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
previousItems.current = items;
|
|
71
71
|
}, [items]);
|
|
72
72
|
|
|
73
|
-
|
|
73
|
+
useInput(
|
|
74
74
|
(input: string, key: Key) => {
|
|
75
75
|
if (input === 'k' || key.upArrow) {
|
|
76
76
|
let lastIndex = (hasLimit ? limit : items.length) - 1;
|
|
77
|
-
let
|
|
77
|
+
let isAtFirstIndex = selectedIndex === 0;
|
|
78
78
|
let nextIndex = hasLimit ? selectedIndex : lastIndex;
|
|
79
|
-
let
|
|
80
|
-
let nextSelectedIndex =
|
|
79
|
+
let nextRotatedIndex = isAtFirstIndex ? rotatedIndex + 1 : rotatedIndex;
|
|
80
|
+
let nextSelectedIndex = isAtFirstIndex ? nextIndex : selectedIndex - 1;
|
|
81
81
|
|
|
82
|
-
|
|
82
|
+
setRotatedIndex(nextRotatedIndex);
|
|
83
83
|
setSelectedIndex(nextSelectedIndex);
|
|
84
84
|
|
|
85
|
-
let slicedItems = hasLimit ? rotate(items,
|
|
85
|
+
let slicedItems = hasLimit ? rotate(items, nextRotatedIndex).slice(0, limit) : items;
|
|
86
86
|
|
|
87
87
|
if (typeof onHighlight === 'function') {
|
|
88
88
|
onHighlight(slicedItems[nextSelectedIndex] as SelectInputItem);
|
|
@@ -92,13 +92,13 @@ export function SelectInput({
|
|
|
92
92
|
if (input === 'j' || key.downArrow) {
|
|
93
93
|
let isAtLastIndex = selectedIndex === (hasLimit ? limit : items.length) - 1;
|
|
94
94
|
let nextIndex = hasLimit ? selectedIndex : 0;
|
|
95
|
-
let
|
|
95
|
+
let nextRotatedIndex = isAtLastIndex ? rotatedIndex - 1 : rotatedIndex;
|
|
96
96
|
let nextSelectedIndex = isAtLastIndex ? nextIndex : selectedIndex + 1;
|
|
97
97
|
|
|
98
|
-
|
|
98
|
+
setRotatedIndex(nextRotatedIndex);
|
|
99
99
|
setSelectedIndex(nextSelectedIndex);
|
|
100
100
|
|
|
101
|
-
let slicedItems = hasLimit ? rotate(items,
|
|
101
|
+
let slicedItems = hasLimit ? rotate(items, nextRotatedIndex).slice(0, limit) : items;
|
|
102
102
|
|
|
103
103
|
if (typeof onHighlight === 'function') {
|
|
104
104
|
onHighlight(slicedItems[nextSelectedIndex] as SelectInputItem);
|
|
@@ -106,20 +106,18 @@ export function SelectInput({
|
|
|
106
106
|
}
|
|
107
107
|
|
|
108
108
|
if (key.return) {
|
|
109
|
-
let slicedItems = hasLimit ? rotate(items,
|
|
109
|
+
let slicedItems = hasLimit ? rotate(items, rotatedIndex).slice(0, limit) : items;
|
|
110
110
|
|
|
111
111
|
if (typeof onSelect === 'function') {
|
|
112
112
|
onSelect(slicedItems[selectedIndex] as SelectInputItem);
|
|
113
113
|
}
|
|
114
114
|
}
|
|
115
115
|
},
|
|
116
|
-
|
|
116
|
+
{isActive: focus},
|
|
117
117
|
);
|
|
118
118
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
let visibleItem: SelectInputItem[] = hasLimit
|
|
122
|
-
? rotate(items, rotateIndex).slice(0, limit)
|
|
119
|
+
let visibleItems: SelectInputItem[] = hasLimit
|
|
120
|
+
? rotate(items, rotatedIndex).slice(0, limit)
|
|
123
121
|
: items;
|
|
124
122
|
|
|
125
123
|
return (
|
|
@@ -138,7 +136,7 @@ export function SelectInput({
|
|
|
138
136
|
</Text>
|
|
139
137
|
) : (
|
|
140
138
|
<Box flexDirection="column">
|
|
141
|
-
{
|
|
139
|
+
{visibleItems.map((item, index) => {
|
|
142
140
|
let isSelected = index === selectedIndex;
|
|
143
141
|
|
|
144
142
|
return (
|
|
@@ -20,7 +20,7 @@ export type TextInputProps = BoxProps & {
|
|
|
20
20
|
/** Value. */
|
|
21
21
|
value: string;
|
|
22
22
|
|
|
23
|
-
/** Should component listen to Listen to user's input? Useful in case there are multiple input components at the same time and input must be
|
|
23
|
+
/** Should component listen to Listen to user's input? Useful in case there are multiple input components at the same time and input must be handled only by a specific component. */
|
|
24
24
|
focus?: boolean | undefined;
|
|
25
25
|
|
|
26
26
|
/** Replace all chars and mask the value. Useful for password inputs. */
|
|
@@ -87,7 +87,6 @@ export function TextInput({
|
|
|
87
87
|
let renderedValue = value;
|
|
88
88
|
let renderedPlaceholder = placeholder ? chalk[colors.muted](placeholder) : undefined;
|
|
89
89
|
|
|
90
|
-
// fake mouse cursor; dealing with with actual cursor and ANSI escape codes is inconvinient
|
|
91
90
|
if (showCursor && focus) {
|
|
92
91
|
renderedPlaceholder =
|
|
93
92
|
placeholder.length > 0
|
|
@@ -96,16 +95,12 @@ export function TextInput({
|
|
|
96
95
|
|
|
97
96
|
renderedValue = value.length > 0 ? '' : chalk.inverse(' ');
|
|
98
97
|
|
|
99
|
-
let i
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
if (i >= state.cursorOffset && i <= state.cursorOffset) {
|
|
103
|
-
renderedValue += chalk.inverse(char);
|
|
98
|
+
for (let [i, character] of Object.entries(value)) {
|
|
99
|
+
if (Number(i) >= state.cursorOffset && Number(i) <= state.cursorOffset) {
|
|
100
|
+
renderedValue += chalk.inverse(character);
|
|
104
101
|
} else {
|
|
105
|
-
renderedValue +=
|
|
102
|
+
renderedValue += character;
|
|
106
103
|
}
|
|
107
|
-
|
|
108
|
-
i += 1;
|
|
109
104
|
}
|
|
110
105
|
|
|
111
106
|
if (value.length > 0 && state.cursorOffset === value.length) {
|