Files
TrainigPlaner/src/TrainingPlanner.Web/Components/Editor/ExercisePicker.razor.cs
T
2026-09-30 15:05:05 +03:00

101 lines
3.9 KiB
C#

using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.JSInterop;
using TrainingPlanner.Application;
using TrainingPlanner.Domain;
namespace TrainingPlanner.Web.Components.Editor;
public partial class ExercisePicker
{
[Inject] public IJSRuntime JavaScript { get; set; } = null!;
[Parameter, EditorRequired] public string Id { get; set; } = string.Empty;
[Parameter] public List<LibraryExercise> Exercises { get; set; } = [];
[Parameter] public int Value { get; set; }
[Parameter] public int ResetVersion { get; set; }
[Parameter] public EventCallback<int> ValueChanged { get; set; }
[Parameter] public EventCallback<int> OnApply { get; set; }
private ElementReference root;
private string search = string.Empty;
private int groupId = -1;
private int activeIndex = -1;
private bool applying;
private int lastResetVersion;
private bool closeAfterReset;
private List<LibraryExercise> Matches = [];
private LibraryExercise? SelectedExercise => Exercises.SingleOrDefault(exercise => exercise.Id == Value);
private string? ActiveOptionId => activeIndex >= 0 && activeIndex < Matches.Count
? Id + "-option-" + Matches[activeIndex].Id : null;
private List<ExerciseGroup> Groups => Exercises.Where(exercise => exercise.Group != null).Select(exercise => exercise.Group!)
.DistinctBy(group => group.Id).OrderBy(group => group.Name).ToList();
protected override void OnParametersSet()
{
if (lastResetVersion != ResetVersion)
{
lastResetVersion = ResetVersion;
search = string.Empty;
activeIndex = -1;
closeAfterReset = true;
}
if (groupId > 0 && Groups.All(group => group.Id != groupId)) groupId = -1;
UpdateMatches();
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (closeAfterReset)
{
closeAfterReset = false;
await JavaScript.InvokeVoidAsync("trainingPlanner.closeExercisePicker", root);
}
await JavaScript.InvokeVoidAsync("trainingPlanner.positionExercisePicker", root);
}
private void UpdateMatches()
{
Matches = ExerciseSearch.Find(Exercises.Where(exercise => groupId < 0 ||
(groupId == 0 ? exercise.GroupId == null : exercise.GroupId == groupId)), search);
if (activeIndex >= Matches.Count) activeIndex = -1;
}
private async Task SearchChangedAsync()
{
activeIndex = -1;
UpdateMatches();
await ValueChanged.InvokeAsync(0);
await OpenAsync();
}
private async Task OpenAsync() => await JavaScript.InvokeVoidAsync("trainingPlanner.openExercisePicker", root);
private async Task SelectAsync(int id, int index)
{
activeIndex = index;
await ValueChanged.InvokeAsync(id);
}
private async Task ApplyAsync(int id)
{
if (applying || Matches.All(exercise => exercise.Id != id)) return;
applying = true;
try
{
await JavaScript.InvokeVoidAsync("trainingPlanner.closeExercisePicker", root);
await OnApply.InvokeAsync(id);
}
finally { applying = false; }
}
private async Task HandleKeyAsync(KeyboardEventArgs args)
{
if (applying) return;
if (args.Key is "ArrowDown" or "ArrowUp")
{
await OpenAsync();
if (Matches.Count == 0) return;
activeIndex = args.Key == "ArrowDown" ? Math.Min(activeIndex + 1, Matches.Count - 1)
: activeIndex < 0 ? Matches.Count - 1 : Math.Max(activeIndex - 1, 0);
}
else if (args.Key == "Enter" && await JavaScript.InvokeAsync<bool>("trainingPlanner.isExercisePickerOpen", root))
{
var index = activeIndex < 0 ? 0 : activeIndex;
if (index < Matches.Count) await ApplyAsync(Matches[index].Id);
}
}
}