-
-
Notifications
You must be signed in to change notification settings - Fork 532
Commit
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.ComponentModel; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using Spectre.Console.Rendering; | ||
|
||
namespace Spectre.Console | ||
{ | ||
/// <summary> | ||
/// Represents a prompt in table format. | ||
/// </summary> | ||
/// <typeparam name="T">The prompt result type.</typeparam> | ||
public sealed class TablePrompt<T> : IPrompt<T>, IListPromptStrategy<T> | ||
Check failure on line 14 in src/Spectre.Console/Prompts/TablePrompt.cs
|
||
where T : notnull | ||
{ | ||
private readonly ListPromptTree<T> _tree; | ||
private readonly List<string> _columns; | ||
|
||
/// <summary> | ||
/// Gets or sets the title. | ||
/// </summary> | ||
public string? Title { get; set; } | ||
|
||
/// <summary> | ||
/// Gets or sets the highlight style of the selected choice. | ||
/// </summary> | ||
public Style? HighlightStyle { get; set; } | ||
|
||
/// <summary> | ||
/// Gets or sets the converter to get the display string for a choice and column. | ||
/// By default the corresponding <see cref="TypeConverter"/> is used. | ||
/// </summary> | ||
public Func<T, int, string>? Converter { get; set; } | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="TablePrompt{T}"/> class. | ||
/// </summary> | ||
public TablePrompt() | ||
{ | ||
_tree = new ListPromptTree<T>(EqualityComparer<T>.Default); | ||
_columns = new List<string>(); | ||
} | ||
|
||
/// <summary> | ||
/// Adds a column to the table. | ||
/// </summary> | ||
/// <param name="column">The column to add.</param> | ||
/// <returns>The same instance so that multiple calls can be chained.</returns> | ||
public TablePrompt<T> AddColumn(string column) | ||
{ | ||
_columns.Add(column); | ||
return this; | ||
} | ||
|
||
/// <summary> | ||
/// Adds a choice. | ||
/// </summary> | ||
/// <param name="item">The item to add.</param> | ||
/// <returns>A <see cref="ISelectionItem{T}"/> so that multiple calls can be chained.</returns> | ||
public ISelectionItem<T> AddChoice(T item) | ||
{ | ||
var node = new ListPromptItem<T>(item); | ||
_tree.Add(node); | ||
return node; | ||
} | ||
|
||
/// <inheritdoc/> | ||
public T Show(IAnsiConsole console) | ||
{ | ||
return ShowAsync(console, CancellationToken.None).GetAwaiter().GetResult(); | ||
} | ||
|
||
/// <inheritdoc/> | ||
public async Task<T> ShowAsync(IAnsiConsole console, CancellationToken cancellationToken) | ||
{ | ||
// Create the list prompt | ||
var prompt = new ListPrompt<T>(console, this); | ||
var result = await prompt.Show(_tree, cancellationToken).ConfigureAwait(false); | ||
|
||
// Return the selected item | ||
return result.Items[result.Index].Data; | ||
} | ||
|
||
/// <inheritdoc/> | ||
ListPromptInputResult IListPromptStrategy<T>.HandleInput(ConsoleKeyInfo key, ListPromptState<T> state) | ||
{ | ||
if (key.Key == ConsoleKey.Enter || key.Key == ConsoleKey.Spacebar) | ||
{ | ||
return ListPromptInputResult.Submit; | ||
} | ||
|
||
return ListPromptInputResult.None; | ||
} | ||
|
||
/// <inheritdoc/> | ||
int IListPromptStrategy<T>.CalculatePageSize(IAnsiConsole console, int totalItemCount, int requestedPageSize) | ||
{ | ||
// Display the entire table | ||
return totalItemCount; | ||
} | ||
|
||
/// <inheritdoc/> | ||
IRenderable IListPromptStrategy<T>.Render(IAnsiConsole console, bool scrollable, int cursorIndex, IEnumerable<(int Index, ListPromptItem<T> Node)> items) | ||
Check failure on line 104 in src/Spectre.Console/Prompts/TablePrompt.cs
|
||
{ | ||
var highlightStyle = HighlightStyle ?? new Style(foreground: Color.Blue); | ||
var table = new Table(); | ||
|
||
if (Title != null) | ||
{ | ||
table.Title = new TableTitle(Title); | ||
} | ||
|
||
foreach (var column in _columns) | ||
{ | ||
table.AddColumn(column); | ||
} | ||
|
||
foreach (var item in items) | ||
{ | ||
var current = item.Index == cursorIndex; | ||
var style = current ? highlightStyle : Style.Plain; | ||
|
||
var columns = new List<IRenderable>(); | ||
|
||
for (var columnIndex = 0; columnIndex < _columns.Count; columnIndex++) | ||
{ | ||
var text = Converter?.Invoke(item.Node.Data, columnIndex) ?? TypeConverterHelper.ConvertToString(item.Node.Data) ?? item.Node.Data.ToString() ?? "?"; | ||
if (current) | ||
{ | ||
text = text.RemoveMarkup().EscapeMarkup(); | ||
} | ||
|
||
columns.Add(new Markup(text, style)); | ||
} | ||
|
||
table.AddRow(columns); | ||
} | ||
|
||
return table; | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
|
||
namespace Spectre.Console | ||
{ | ||
/// <summary> | ||
/// Contains extension methods for <see cref="TablePrompt{T}"/>. | ||
/// </summary> | ||
public static class TablePromptExtensions | ||
{ | ||
/// <summary> | ||
/// Adds multiple columns to the table. | ||
/// </summary> | ||
/// <typeparam name="T">The prompt result type.</typeparam> | ||
/// <param name="obj">The table prompt.</param> | ||
/// <param name="columns">The columns to add.</param> | ||
/// <returns>The same instance so that multiple calls can be chained.</returns> | ||
public static TablePrompt<T> AddColumns<T>(this TablePrompt<T> obj, params string[] columns) | ||
where T : notnull | ||
{ | ||
if (obj is null) | ||
{ | ||
throw new ArgumentNullException(nameof(obj)); | ||
} | ||
|
||
if (columns is null) | ||
{ | ||
throw new ArgumentNullException(nameof(columns)); | ||
} | ||
|
||
foreach (var column in columns) | ||
{ | ||
obj.AddColumn(column); | ||
} | ||
|
||
return obj; | ||
} | ||
|
||
/// <summary> | ||
/// Adds multiple choices. | ||
/// </summary> | ||
/// <typeparam name="T">The prompt result type.</typeparam> | ||
/// <param name="obj">The table prompt.</param> | ||
/// <param name="choices">The choices to add.</param> | ||
/// <returns>The same instance so that multiple calls can be chained.</returns> | ||
public static TablePrompt<T> AddChoices<T>(this TablePrompt<T> obj, params T[] choices) | ||
where T : notnull | ||
{ | ||
if (obj is null) | ||
{ | ||
throw new ArgumentNullException(nameof(obj)); | ||
} | ||
|
||
foreach (var choice in choices) | ||
{ | ||
obj.AddChoice(choice); | ||
} | ||
|
||
return obj; | ||
} | ||
|
||
/// <summary> | ||
/// Adds multiple choices. | ||
/// </summary> | ||
/// <typeparam name="T">The prompt result type.</typeparam> | ||
/// <param name="obj">The table prompt.</param> | ||
/// <param name="choices">The choices to add.</param> | ||
/// <returns>The same instance so that multiple calls can be chained.</returns> | ||
public static TablePrompt<T> AddChoices<T>(this TablePrompt<T> obj, IEnumerable<T> choices) | ||
where T : notnull | ||
{ | ||
if (obj is null) | ||
{ | ||
throw new ArgumentNullException(nameof(obj)); | ||
} | ||
|
||
foreach (var choice in choices) | ||
{ | ||
obj.AddChoice(choice); | ||
} | ||
|
||
return obj; | ||
} | ||
|
||
/// <summary> | ||
/// Sets the title. | ||
/// </summary> | ||
/// <typeparam name="T">The prompt result type.</typeparam> | ||
/// <param name="obj">The table prompt.</param> | ||
/// <param name="title">The title markup text.</param> | ||
/// <returns>The same instance so that multiple calls can be chained.</returns> | ||
public static TablePrompt<T> Title<T>(this TablePrompt<T> obj, string? title) | ||
where T : notnull | ||
{ | ||
if (obj is null) | ||
{ | ||
throw new ArgumentNullException(nameof(obj)); | ||
} | ||
|
||
obj.Title = title; | ||
return obj; | ||
} | ||
|
||
/// <summary> | ||
/// Sets the highlight style of the selected choice. | ||
/// </summary> | ||
/// <typeparam name="T">The prompt result type.</typeparam> | ||
/// <param name="obj">The table prompt.</param> | ||
/// <param name="highlightStyle">The highlight style of the selected choice.</param> | ||
/// <returns>The same instance so that multiple calls can be chained.</returns> | ||
public static TablePrompt<T> HighlightStyle<T>(this TablePrompt<T> obj, Style highlightStyle) | ||
where T : notnull | ||
{ | ||
if (obj is null) | ||
{ | ||
throw new ArgumentNullException(nameof(obj)); | ||
} | ||
|
||
obj.HighlightStyle = highlightStyle; | ||
return obj; | ||
} | ||
|
||
/// <summary> | ||
/// Sets the function to create a display string for a given choice and column. | ||
/// </summary> | ||
/// <typeparam name="T">The prompt type.</typeparam> | ||
/// <param name="obj">The table prompt.</param> | ||
/// <param name="displaySelector">The function to get a display string for a given choice and column.</param> | ||
/// <returns>The same instance so that multiple calls can be chained.</returns> | ||
public static TablePrompt<T> UseConverter<T>(this TablePrompt<T> obj, Func<T, int, string>? displaySelector) | ||
where T : notnull | ||
{ | ||
if (obj is null) | ||
{ | ||
throw new ArgumentNullException(nameof(obj)); | ||
} | ||
|
||
obj.Converter = displaySelector; | ||
return obj; | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
using System; | ||
using Shouldly; | ||
using Xunit; | ||
|
||
namespace Spectre.Console.Tests.Unit | ||
{ | ||
public sealed class TablePromptTests | ||
{ | ||
[Fact] | ||
public void Should_Throw_If_Columns_Are_Null() | ||
{ | ||
// Given | ||
var prompt = new TablePrompt<string>(); | ||
|
||
// When | ||
var result = Record.Exception(() => prompt.AddColumns(null)); | ||
|
||
// Then | ||
result.ShouldBeOfType<ArgumentNullException>() | ||
.ParamName.ShouldBe("columns"); | ||
} | ||
} | ||
} |