@inquirer/checkbox
Advanced tools
+3
-4
| { | ||
| "name": "@inquirer/checkbox", | ||
| "version": "5.1.0", | ||
| "version": "5.1.1", | ||
| "description": "Inquirer checkbox prompt", | ||
@@ -71,3 +71,3 @@ "keywords": [ | ||
| "@inquirer/ansi": "^2.0.3", | ||
| "@inquirer/core": "^11.1.5", | ||
| "@inquirer/core": "^11.1.6", | ||
| "@inquirer/figures": "^2.0.3", | ||
@@ -77,3 +77,2 @@ "@inquirer/type": "^4.0.3" | ||
| "devDependencies": { | ||
| "@inquirer/testing": "^3.3.0", | ||
| "typescript": "^5.9.3" | ||
@@ -94,3 +93,3 @@ }, | ||
| "types": "./dist/index.d.ts", | ||
| "gitHead": "526eca2e64853510821ffd457561840ec0cbfb93" | ||
| "gitHead": "1ce03199b82b4a5fb6f7c97ce374c6da5087444f" | ||
| } |
| import { Separator, type Theme, type Keybinding } from '@inquirer/core'; | ||
| import type { PartialDeep } from '@inquirer/type'; | ||
| type CheckboxTheme = { | ||
| icon: { | ||
| checked: string; | ||
| unchecked: string; | ||
| cursor: string; | ||
| disabledChecked: string; | ||
| disabledUnchecked: string; | ||
| }; | ||
| style: { | ||
| disabled: (text: string) => string; | ||
| renderSelectedChoices: <T>(selectedChoices: ReadonlyArray<NormalizedChoice<T>>, allChoices: ReadonlyArray<NormalizedChoice<T> | Separator>) => string; | ||
| description: (text: string) => string; | ||
| keysHelpTip: (keys: [key: string, action: string][]) => string | undefined; | ||
| }; | ||
| i18n: { | ||
| disabledError: string; | ||
| }; | ||
| keybindings: ReadonlyArray<Keybinding>; | ||
| }; | ||
| type CheckboxShortcuts = { | ||
| all?: string | null; | ||
| invert?: string | null; | ||
| }; | ||
| type Choice<Value> = { | ||
| value: Value; | ||
| name?: string; | ||
| checkedName?: string; | ||
| description?: string; | ||
| short?: string; | ||
| disabled?: boolean | string; | ||
| checked?: boolean; | ||
| type?: never; | ||
| }; | ||
| type NormalizedChoice<Value> = { | ||
| value: Value; | ||
| name: string; | ||
| checkedName: string; | ||
| description?: string; | ||
| short: string; | ||
| disabled: boolean | string; | ||
| checked: boolean; | ||
| }; | ||
| declare const _default: <Value>(config: { | ||
| message: string; | ||
| prefix?: string | undefined; | ||
| pageSize?: number | undefined; | ||
| choices: readonly (string | Separator)[] | readonly (Separator | Choice<Value>)[]; | ||
| loop?: boolean | undefined; | ||
| required?: boolean | undefined; | ||
| validate?: ((choices: readonly NormalizedChoice<Value>[]) => boolean | string | Promise<string | boolean>) | undefined; | ||
| theme?: PartialDeep<Theme<CheckboxTheme>> | undefined; | ||
| shortcuts?: CheckboxShortcuts | undefined; | ||
| }, context?: import("@inquirer/type").Context) => Promise<Value[]>; | ||
| export default _default; | ||
| export { Separator } from '@inquirer/core'; |
-210
| import { createPrompt, useState, useKeypress, usePrefix, usePagination, useMemo, makeTheme, isUpKey, isDownKey, isSpaceKey, isNumberKey, isEnterKey, ValidationError, Separator, } from '@inquirer/core'; | ||
| import { cursorHide } from '@inquirer/ansi'; | ||
| import { styleText } from 'node:util'; | ||
| import figures from '@inquirer/figures'; | ||
| const checkboxTheme = { | ||
| icon: { | ||
| checked: styleText('green', figures.circleFilled), | ||
| unchecked: figures.circle, | ||
| cursor: figures.pointer, | ||
| disabledChecked: styleText('green', figures.circleDouble), | ||
| disabledUnchecked: '-', | ||
| }, | ||
| style: { | ||
| disabled: (text) => styleText('dim', text), | ||
| renderSelectedChoices: (selectedChoices) => selectedChoices.map((choice) => choice.short).join(', '), | ||
| description: (text) => styleText('cyan', text), | ||
| keysHelpTip: (keys) => keys | ||
| .map(([key, action]) => `${styleText('bold', key)} ${styleText('dim', action)}`) | ||
| .join(styleText('dim', ' • ')), | ||
| }, | ||
| i18n: { disabledError: 'This option is disabled and cannot be toggled.' }, | ||
| keybindings: [], | ||
| }; | ||
| function isSelectable(item) { | ||
| return !Separator.isSeparator(item) && !item.disabled; | ||
| } | ||
| function isNavigable(item) { | ||
| return !Separator.isSeparator(item); | ||
| } | ||
| function isChecked(item) { | ||
| return !Separator.isSeparator(item) && item.checked; | ||
| } | ||
| function toggle(item) { | ||
| return isSelectable(item) ? { ...item, checked: !item.checked } : item; | ||
| } | ||
| function check(checked) { | ||
| return function (item) { | ||
| return isSelectable(item) ? { ...item, checked } : item; | ||
| }; | ||
| } | ||
| function normalizeChoices(choices) { | ||
| return choices.map((choice) => { | ||
| if (Separator.isSeparator(choice)) | ||
| return choice; | ||
| if (typeof choice === 'string') { | ||
| return { | ||
| value: choice, | ||
| name: choice, | ||
| short: choice, | ||
| checkedName: choice, | ||
| disabled: false, | ||
| checked: false, | ||
| }; | ||
| } | ||
| const name = choice.name ?? String(choice.value); | ||
| const normalizedChoice = { | ||
| value: choice.value, | ||
| name, | ||
| short: choice.short ?? name, | ||
| checkedName: choice.checkedName ?? name, | ||
| disabled: choice.disabled ?? false, | ||
| checked: choice.checked ?? false, | ||
| }; | ||
| if (choice.description) { | ||
| normalizedChoice.description = choice.description; | ||
| } | ||
| return normalizedChoice; | ||
| }); | ||
| } | ||
| export default createPrompt((config, done) => { | ||
| const { pageSize = 7, loop = true, required, validate = () => true } = config; | ||
| const shortcuts = { all: 'a', invert: 'i', ...config.shortcuts }; | ||
| const theme = makeTheme(checkboxTheme, config.theme); | ||
| const { keybindings } = theme; | ||
| const [status, setStatus] = useState('idle'); | ||
| const prefix = usePrefix({ status, theme }); | ||
| const [items, setItems] = useState(normalizeChoices(config.choices)); | ||
| const bounds = useMemo(() => { | ||
| const first = items.findIndex(isNavigable); | ||
| const last = items.findLastIndex(isNavigable); | ||
| if (first === -1) { | ||
| throw new ValidationError('[checkbox prompt] No selectable choices. All choices are disabled.'); | ||
| } | ||
| return { first, last }; | ||
| }, [items]); | ||
| const [active, setActive] = useState(bounds.first); | ||
| const [errorMsg, setError] = useState(); | ||
| useKeypress(async (key) => { | ||
| if (isEnterKey(key)) { | ||
| const selection = items.filter(isChecked); | ||
| const isValid = await validate([...selection]); | ||
| if (required && !selection.length) { | ||
| setError('At least one choice must be selected'); | ||
| } | ||
| else if (isValid === true) { | ||
| setStatus('done'); | ||
| done(selection.map((choice) => choice.value)); | ||
| } | ||
| else { | ||
| setError(isValid || 'You must select a valid value'); | ||
| } | ||
| } | ||
| else if (isUpKey(key, keybindings) || isDownKey(key, keybindings)) { | ||
| if (errorMsg) { | ||
| setError(undefined); | ||
| } | ||
| if (loop || | ||
| (isUpKey(key, keybindings) && active !== bounds.first) || | ||
| (isDownKey(key, keybindings) && active !== bounds.last)) { | ||
| const offset = isUpKey(key, keybindings) ? -1 : 1; | ||
| let next = active; | ||
| do { | ||
| next = (next + offset + items.length) % items.length; | ||
| } while (!isNavigable(items[next])); | ||
| setActive(next); | ||
| } | ||
| } | ||
| else if (isSpaceKey(key)) { | ||
| const activeItem = items[active]; | ||
| if (activeItem && !Separator.isSeparator(activeItem)) { | ||
| if (activeItem.disabled) { | ||
| setError(theme.i18n.disabledError); | ||
| } | ||
| else { | ||
| setError(undefined); | ||
| setItems(items.map((choice, i) => (i === active ? toggle(choice) : choice))); | ||
| } | ||
| } | ||
| } | ||
| else if (key.name === shortcuts.all) { | ||
| const selectAll = items.some((choice) => isSelectable(choice) && !choice.checked); | ||
| setItems(items.map(check(selectAll))); | ||
| } | ||
| else if (key.name === shortcuts.invert) { | ||
| setItems(items.map(toggle)); | ||
| } | ||
| else if (isNumberKey(key)) { | ||
| const selectedIndex = Number(key.name) - 1; | ||
| // Find the nth item (ignoring separators) | ||
| let selectableIndex = -1; | ||
| const position = items.findIndex((item) => { | ||
| if (Separator.isSeparator(item)) | ||
| return false; | ||
| selectableIndex++; | ||
| return selectableIndex === selectedIndex; | ||
| }); | ||
| const selectedItem = items[position]; | ||
| if (selectedItem && isSelectable(selectedItem)) { | ||
| setActive(position); | ||
| setItems(items.map((choice, i) => (i === position ? toggle(choice) : choice))); | ||
| } | ||
| } | ||
| }); | ||
| const message = theme.style.message(config.message, status); | ||
| let description; | ||
| const page = usePagination({ | ||
| items, | ||
| active, | ||
| renderItem({ item, isActive }) { | ||
| if (Separator.isSeparator(item)) { | ||
| return ` ${item.separator}`; | ||
| } | ||
| const cursor = isActive ? theme.icon.cursor : ' '; | ||
| if (item.disabled) { | ||
| const disabledLabel = typeof item.disabled === 'string' ? item.disabled : '(disabled)'; | ||
| const checkbox = item.checked | ||
| ? theme.icon.disabledChecked | ||
| : theme.icon.disabledUnchecked; | ||
| return theme.style.disabled(`${cursor}${checkbox} ${item.name} ${disabledLabel}`); | ||
| } | ||
| if (isActive) { | ||
| description = item.description; | ||
| } | ||
| const checkbox = item.checked ? theme.icon.checked : theme.icon.unchecked; | ||
| const name = item.checked ? item.checkedName : item.name; | ||
| const color = isActive ? theme.style.highlight : (x) => x; | ||
| return color(`${cursor}${checkbox} ${name}`); | ||
| }, | ||
| pageSize, | ||
| loop, | ||
| }); | ||
| if (status === 'done') { | ||
| const selection = items.filter(isChecked); | ||
| const answer = theme.style.answer(theme.style.renderSelectedChoices(selection, items)); | ||
| return [prefix, message, answer].filter(Boolean).join(' '); | ||
| } | ||
| const keys = [ | ||
| ['↑↓', 'navigate'], | ||
| ['space', 'select'], | ||
| ]; | ||
| if (shortcuts.all) | ||
| keys.push([shortcuts.all, 'all']); | ||
| if (shortcuts.invert) | ||
| keys.push([shortcuts.invert, 'invert']); | ||
| keys.push(['⏎', 'submit']); | ||
| const helpLine = theme.style.keysHelpTip(keys); | ||
| const lines = [ | ||
| [prefix, message].filter(Boolean).join(' '), | ||
| page, | ||
| ' ', | ||
| description ? theme.style.description(description) : '', | ||
| errorMsg ? theme.style.error(errorMsg) : '', | ||
| helpLine, | ||
| ] | ||
| .filter(Boolean) | ||
| .join('\n') | ||
| .trimEnd(); | ||
| return `${lines}${cursorHide}`; | ||
| }); | ||
| export { Separator } from '@inquirer/core'; |
Empty package
Supply chain riskPackage does not contain any code. It may be removed, is name squatting, or the result of a faulty package publish.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
1
-50%10130
-50.15%3
-40%0
-100%2
Infinity%Updated