beddu/src/03.prompt/choose.sh
2025-05-09 20:01:43 +02:00

81 lines
1.9 KiB
Bash

#! /usr/bin/env bash
# choose.sh - Choose from a menu of options
# @depends on:
# - pen.sh
# - _symbols.sh
# - cursor.sh
# Print an interactive menu of options and return the selected option
#
# Usage:
# choose outvar text [choices...]
# Example:
# choose color "What is your favorite color?" "Red" "Blue" "Green"
# pen "You chose $color!"
choose() {
local -n outvar="$1"
local prompt
local options=("${@:3}") # Get options from third argument onwards
local current=0
local count=${#options[@]}
# Set prompt with default indicator
prompt=$(
pen -n blue "${_q:-?} "
pen -n "${2}"
pen gray "[↑↓]"
)
# Hide cursor for cleaner UI
hide_cursor
trap 'show_cursor; return' INT TERM
# Display initial prompt
pen "$prompt"
# Main loop
while true; do
local index=0
for item in "${options[@]}"; do
if ((index == current)); then
pen -n blue "${_O:-} "
pen "${item}"
else
pen gray "${_o:-} ${item}"
fi
((index++))
done
# Read a single key press
read -s -r -n1 key
# Handle arrow/enter keys
if [[ $key == $'\e' ]]; then
read -s -r -n2 -t 0.0001 escape
key+="$escape"
fi
case "$key" in
$'\e[A' | 'k') # Up arrow or k
((current--))
[[ $current -lt 0 ]] && current=$((count - 1))
;;
$'\e[B' | 'j') # Down arrow or j
((current++))
[[ $current -ge "$count" ]] && current=0
;;
'') # Enter
break
;;
esac
# Clear screen and repeat
echo -en "\e[${count}A\e[J"
done
# Pass selected option back to caller
# shellcheck disable=SC2034
outvar="${options[$current]}"
}