diff --git a/src/Web/Shared/Components/Form/ItemTable.razor.cs b/src/Web/Shared/Components/Form/ItemTable.razor.cs index ae6098cc4..c071fb016 100644 --- a/src/Web/Shared/Components/Form/ItemTable.razor.cs +++ b/src/Web/Shared/Components/Form/ItemTable.razor.cs @@ -11,6 +11,7 @@ namespace MUnique.OpenMU.Web.Shared.Components.Form; using Microsoft.AspNetCore.Components; using MUnique.OpenMU.DataModel.Composition; using MUnique.OpenMU.Persistence; +using MUnique.OpenMU.Web.Shared; /// /// A component which shows a collection of in a table. @@ -99,6 +100,12 @@ private async Task OnCreateClickAsync() var parameters = new ModalParameters(); parameters.Add(nameof(ModalCreateNew.Item), item); parameters.Add(nameof(ModalCreateNew.PersistenceContext), this.PersistenceContext); + var owner = this.GetOwnerFromValueExpression(); + if (owner is not null) + { + parameters.Add(nameof(ModalCreateNew.Owner), owner); + } + var options = new ModalOptions { DisableBackgroundCancel = true, @@ -131,4 +138,17 @@ private async Task OnRemoveClickAsync(TItem item) await this.PersistenceContext.SaveChangesAsync().ConfigureAwait(false); } } + + private object? GetOwnerFromValueExpression() + { + // This handles the common case where the expression is a simple member access on a captured constant, + // e.g. () => character.LearnedSkills, as generated by CreatePropertyExpression. + // More complex expression trees (e.g. nested properties) are not supported and will return null. + if (this.ValueExpression?.Body is MemberExpression { Expression: ConstantExpression { Value: { } owner } }) + { + return owner; + } + + return null; + } } \ No newline at end of file diff --git a/src/Web/Shared/Components/Form/ModalCreateNew.razor b/src/Web/Shared/Components/Form/ModalCreateNew.razor index 2f08327ec..c6fa86816 100644 --- a/src/Web/Shared/Components/Form/ModalCreateNew.razor +++ b/src/Web/Shared/Components/Form/ModalCreateNew.razor @@ -1,4 +1,8 @@ -@using MUnique.OpenMU.Persistence +@* + Licensed under the MIT License. See LICENSE file in the project root for full license information. + *@ + +@using MUnique.OpenMU.Persistence @using MUnique.OpenMU.DataModel.Entities @using MUnique.OpenMU.Web.Shared.Components.ItemEdit @@ -13,6 +17,17 @@ } +else if (typeof(TItem) == typeof(SkillEntry) && this.Owner is Character character) +{ + @* The intermediate cast through object is required because C# does not allow a direct generic-to-concrete cast at compile time. *@ + var skillEntry = (SkillEntry)(object)this.Item!; + + +} else { @@ -46,6 +61,13 @@ else [Parameter] public IContext PersistenceContext { get; set; } = null!; + /// + /// Gets or sets the owner object of the collection containing this item. + /// Used to provide context for creation (e.g., filtering skills by character class). + /// + [Parameter] + public object? Owner { get; set; } + private Task SubmitAsync() { return this.BlazoredModal.CloseAsync(ModalResult.Ok(Item)); diff --git a/src/Web/Shared/Components/Form/SkillEntryCreationForm.razor b/src/Web/Shared/Components/Form/SkillEntryCreationForm.razor new file mode 100644 index 000000000..52928a8a2 --- /dev/null +++ b/src/Web/Shared/Components/Form/SkillEntryCreationForm.razor @@ -0,0 +1,81 @@ +@* + Licensed under the MIT License. See LICENSE file in the project root for full license information. + *@ + +@using MUnique.OpenMU.DataModel.Configuration +@using MUnique.OpenMU.DataModel.Entities +@using MUnique.OpenMU.Persistence +@using MUnique.OpenMU.Web.Shared.Services + +@inject IDataSource GameConfigurationSource + + + + + + + + + + + + +@code { + + private ILookupController? _skillLookupController; + + private CharacterClass? _lastCharacterClass; + + /// + /// Gets or sets the skill entry which is being created. + /// + [Parameter] + public SkillEntry SkillEntry { get; set; } = null!; + + /// + /// Gets or sets the character for which the skill entry is being created. + /// The character's class is used to filter the available skills. + /// + [Parameter] + public Character Character { get; set; } = null!; + + /// + /// Gets or sets the persistence context. + /// + [Parameter] + public IContext PersistenceContext { get; set; } = null!; + + /// + /// Gets or sets the callback invoked when the form is submitted. + /// + [Parameter] + public EventCallback OnSubmit { get; set; } + + /// + /// Gets or sets the callback invoked when the form is cancelled. + /// + [Parameter] + public EventCallback OnCancel { get; set; } + + /// + protected override void OnParametersSet() + { + base.OnParametersSet(); + var characterClass = this.Character?.CharacterClass; + if (characterClass == this._lastCharacterClass && this._skillLookupController is not null) + { + return; + } + + this._lastCharacterClass = characterClass; + var skills = this.GameConfigurationSource.GetAll(); + if (characterClass is not null) + { + skills = skills.Where(s => s.QualifiedCharacters.Contains(characterClass)); + } + + this._skillLookupController = new EnumerableLookupController(skills.OrderBy(s => s.GetName()).ToList()); + } +}