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 Exercises { get; set; } = []; [Parameter] public int Value { get; set; } [Parameter] public int ResetVersion { get; set; } [Parameter] public EventCallback ValueChanged { get; set; } [Parameter] public EventCallback 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 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 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("trainingPlanner.isExercisePickerOpen", root)) { var index = activeIndex < 0 ? 0 : activeIndex; if (index < Matches.Count) await ApplyAsync(Matches[index].Id); } } }