Private/Interactive/Show-TBMenu.ps1
|
function Show-TBMenu { <# .SYNOPSIS Renders a numbered menu and reads the user selection. .DESCRIPTION Displays a list of options with numbered indices, reads user input, validates the selection, and returns the chosen index or special value. Supports single-select and multi-select modes. On PS 7+ with an interactive console, uses arrow-key navigation with premium box rendering. On non-interactive hosts, falls back to the classic Read-Host numbered input. .PARAMETER Title The menu title displayed above the options. .PARAMETER Options Array of option display strings. .PARAMETER MultiSelect If specified, allows comma-separated multi-select (e.g., "1,3,5") and an "A" option for all. .PARAMETER IncludeBack If specified, adds a "0. Back" option. .PARAMETER IncludeQuit If specified, adds a "Q. Quit" option. #> [CmdletBinding()] param( [Parameter(Mandatory = $true)] [string]$Title, [Parameter(Mandatory = $true)] [string[]]$Options, [Parameter()] [switch]$MultiSelect, [Parameter()] [switch]$IncludeBack, [Parameter()] [switch]$IncludeQuit ) if (Test-TBArrowKeySupport) { if ($MultiSelect) { return Show-TBMenuArrowMultiSelect -Title $Title -Options $Options -IncludeBack:$IncludeBack } else { return Show-TBMenuArrowSingle -Title $Title -Options $Options -IncludeBack:$IncludeBack -IncludeQuit:$IncludeQuit } } # Classic Read-Host fallback for non-interactive hosts while ($true) { Write-Host '' Write-Host (' {0}' -f $Title) -ForegroundColor Cyan Write-Host (' {0}' -f ('-' * $Title.Length)) -ForegroundColor DarkCyan Write-Host '' for ($i = 0; $i -lt $Options.Count; $i++) { $num = $i + 1 Write-Host (' {0}. {1}' -f $num, $Options[$i]) -ForegroundColor White } Write-Host '' if ($MultiSelect) { Write-Host ' A. Select all' -ForegroundColor DarkYellow } if ($IncludeBack) { Write-Host ' 0. Back' -ForegroundColor DarkGray } if ($IncludeQuit) { Write-Host ' Q. Quit' -ForegroundColor DarkGray } Write-Host '' $prompt = 'Select an option' if ($MultiSelect) { $prompt = 'Select option(s) (comma-separated, or A for all)' } $input_value = Read-Host -Prompt (' {0}' -f $prompt) $input_value = $input_value.Trim() if ($IncludeQuit -and ($input_value -eq 'Q' -or $input_value -eq 'q')) { return 'Quit' } if ($IncludeBack -and $input_value -eq '0') { return 'Back' } if ($MultiSelect -and ($input_value -eq 'A' -or $input_value -eq 'a')) { $allIndices = @() for ($i = 0; $i -lt $Options.Count; $i++) { $allIndices += $i } return $allIndices } if ($MultiSelect) { $parts = $input_value -split ',' | ForEach-Object { $_.Trim() } $valid = $true $seen = @{} $selectedIndices = @() foreach ($part in $parts) { $num = 0 if ([int]::TryParse($part, [ref]$num)) { if ($num -ge 1 -and $num -le $Options.Count) { $idx = $num - 1 if (-not $seen.ContainsKey($idx)) { $seen[$idx] = $true $selectedIndices += $idx } } else { $valid = $false break } } else { $valid = $false break } } if ($valid -and $selectedIndices.Count -gt 0) { return $selectedIndices } } else { $num = 0 if ([int]::TryParse($input_value, [ref]$num)) { if ($num -ge 1 -and $num -le $Options.Count) { return ($num - 1) } } } Write-Host ' Invalid selection. Please try again.' -ForegroundColor Red } } |