import { createPrompt, useState, useKeypress, usePrefix, usePagination, useRef, useMemo, isEnterKey, isUpKey, isDownKey, isNumberKey, Separator, } from '@inquirer/core'; import chalk from 'chalk'; import figures from 'figures'; import ansiEscapes from 'ansi-escapes'; function isSelectable(item) { return !Separator.isSeparator(item) && !item.disabled; } function renderItem({ item, isActive }) { if (Separator.isSeparator(item)) { return ` ${item.separator}`; } const line = item.name || item.value; if (item.disabled) { const disabledLabel = typeof item.disabled === 'string' ? item.disabled : '(disabled)'; return chalk.dim(`- ${line} ${disabledLabel}`); } const color = isActive ? chalk.cyan : (x) => x; const prefix = isActive ? figures.pointer : ` `; return color(`${prefix} ${line}`); } export default createPrompt((config, done) => { const { choices: items, loop = true, pageSize = 7 } = config; const firstRender = useRef(true); const prefix = usePrefix(); const [status, setStatus] = useState('pending'); const bounds = useMemo(() => { const first = items.findIndex(isSelectable); // TODO: Replace with `findLastIndex` when it's available. const last = items.length - 1 - [...items].reverse().findIndex(isSelectable); if (first < 0) throw new Error('[select prompt] No selectable choices. All choices are disabled.'); return { first, last }; }, [items]); const defaultItemIndex = useMemo(() => { if (!('default' in config)) return -1; return items.findIndex((item) => isSelectable(item) && item.value === config.default); }, [config.default, items]); const [active, setActive] = useState(defaultItemIndex === -1 ? bounds.first : defaultItemIndex); // Safe to assume the cursor position always point to a Choice. const selectedChoice = items[active]; useKeypress((key) => { if (isEnterKey(key)) { setStatus('done'); done(selectedChoice.value); } else if (isUpKey(key) || isDownKey(key)) { if (loop || (isUpKey(key) && active !== bounds.first) || (isDownKey(key) && active !== bounds.last)) { const offset = isUpKey(key) ? -1 : 1; let next = active; do { next = (next + offset + items.length) % items.length; } while (!isSelectable(items[next])); setActive(next); } } else if (isNumberKey(key)) { const position = Number(key.name) - 1; const item = items[position]; if (item != null && isSelectable(item)) { setActive(position); } } }); const message = chalk.bold(config.message); let helpTip; if (firstRender.current && items.length <= pageSize) { firstRender.current = false; helpTip = chalk.dim('(Use arrow keys)'); } const page = usePagination({ items, active, renderItem, pageSize, loop, }); if (status === 'done') { return `${prefix} ${message} ${chalk.cyan(selectedChoice.name || selectedChoice.value)}`; } const choiceDescription = selectedChoice.description ? `\n${selectedChoice.description}` : ``; return `${[prefix, message, helpTip].filter(Boolean).join(' ')}\n${page}${choiceDescription}${ansiEscapes.cursorHide}`; }); export { Separator };