Files
RolemasterDB/src/RolemasterDb.App/Components/Primitives/SegmentedTabs.razor

85 lines
2.8 KiB
Plaintext

<div class="@BuildCssClass()" role="tablist" aria-label="@AriaLabel">
@foreach (var item in Items)
{
var isSelected = string.Equals(item.Value, SelectedValue, StringComparison.Ordinal);
var tabIndex = isSelected || (!HasSelectedValue && string.Equals(item.Value, FirstEnabledValue, StringComparison.Ordinal)) ? 0 : -1;
<button
type="button"
class="segmented-tabs-button @(isSelected ? "is-selected" : null)"
role="tab"
aria-selected="@isSelected"
tabindex="@tabIndex"
disabled="@item.IsDisabled"
@onclick="() => SelectAsync(item.Value)"
@onkeydown="args => HandleKeyDownAsync(args, item.Value)">
<span>@item.Label</span>
@if (!string.IsNullOrWhiteSpace(item.Badge))
{
<span class="segmented-tabs-badge">@item.Badge</span>
}
</button>
}
</div>
@code {
[Parameter]
public IReadOnlyList<SegmentedTabItem> Items { get; set; } = [];
[Parameter]
public string? SelectedValue { get; set; }
[Parameter]
public EventCallback<string> SelectedValueChanged { get; set; }
[Parameter]
public string AriaLabel { get; set; } = "Options";
[Parameter]
public string? CssClass { get; set; }
private bool HasSelectedValue =>
!string.IsNullOrWhiteSpace(SelectedValue) && Items.Any(item => !item.IsDisabled && string.Equals(item.Value, SelectedValue, StringComparison.Ordinal));
private string? FirstEnabledValue =>
Items.FirstOrDefault(item => !item.IsDisabled)?.Value;
private string BuildCssClass() =>
string.IsNullOrWhiteSpace(CssClass) ? "segmented-tabs" : $"segmented-tabs {CssClass}";
private Task SelectAsync(string value) =>
SelectedValueChanged.InvokeAsync(value);
private async Task HandleKeyDownAsync(KeyboardEventArgs args, string currentValue)
{
var enabledItems = Items.Where(item => !item.IsDisabled).ToList();
if (enabledItems.Count == 0)
{
return;
}
var currentIndex = enabledItems.FindIndex(item => string.Equals(item.Value, currentValue, StringComparison.Ordinal));
if (currentIndex < 0)
{
currentIndex = 0;
}
var nextValue = args.Key switch
{
"ArrowRight" or "ArrowDown" => enabledItems[Math.Min(currentIndex + 1, enabledItems.Count - 1)].Value,
"ArrowLeft" or "ArrowUp" => enabledItems[Math.Max(currentIndex - 1, 0)].Value,
"Home" => enabledItems[0].Value,
"End" => enabledItems[^1].Value,
_ => null
};
if (string.IsNullOrWhiteSpace(nextValue))
{
return;
}
await SelectAsync(nextValue);
}
}