Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions src/Web/Shared/ComponentBuilders/SkillListFieldBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// <copyright file="SkillListFieldBuilder.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>

namespace MUnique.OpenMU.Web.Shared.ComponentBuilders;

using System.Reflection;
using Microsoft.AspNetCore.Components.Rendering;
using MUnique.OpenMU.DataModel.Entities;
using MUnique.OpenMU.Web.Shared.Components.Form;
using MUnique.OpenMU.Web.Shared.Services;

/// <summary>
/// A <see cref="IComponentBuilder"/> for <see cref="ICollection{T}"/> of <see cref="SkillEntry"/> fields.
/// It shows a skill list including a master skill tree editor.
/// </summary>
public class SkillListFieldBuilder : BaseComponentBuilder, IComponentBuilder
{
/// <inheritdoc/>
public int BuildComponent(object model, PropertyInfo propertyInfo, RenderTreeBuilder builder, int currentIndex, IChangeNotificationService notificationService)
=> this.BuildField<ICollection<SkillEntry>>(model, typeof(SkillListField), propertyInfo, builder, currentIndex, notificationService);

/// <inheritdoc/>
public bool CanBuildComponent(PropertyInfo propertyInfo)
=> propertyInfo.PropertyType == typeof(ICollection<SkillEntry>);
}
1 change: 1 addition & 0 deletions src/Web/Shared/Components/Form/AutoFields.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ static AutoFields()
Builders.Add(new ItemStorageFieldBuilder());
Builders.Add(new LookupFieldBuilder());
Builders.Add(new EmbeddedFormFieldBuilder());
Builders.Add(new SkillListFieldBuilder());
Builders.Add(new ObjectCollectionFieldBuilder());
Builders.Add(new IntCollectionFieldBuilder());
Builders.Add(new ByteArrayFieldBuilder());
Expand Down
99 changes: 99 additions & 0 deletions src/Web/Shared/Components/Form/SkillListField.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
@* <copyright file="SkillListField.razor" company="MUnique">
Licensed under the MIT License. See LICENSE file in the project root for full license information.
</copyright> *@

@using MUnique.OpenMU.DataModel.Configuration
@using MUnique.OpenMU.DataModel.Entities
@using MUnique.OpenMU.Persistence

@inherits InputBase<ICollection<SkillEntry>>

<div class="skill-list-field">

@* ── Regular Skills ─────────────────────────────────────────────────── *@
<div class="card mb-3">
<h5 class="card-header">
<FieldLabel Text="@Label" ValueExpression="@this.ValueExpression" />
</h5>
<div class="card-body card-text">
<table class="table table-sm">
<tbody>
@foreach (var entry in this.RegularSkills)
{
<tr>
<td>@(entry.Skill?.GetName() ?? "Unknown Skill")</td>
<td>
<button type="button" class="btn-danger btn-sm"
@onclick="@(() => this.OnRemoveSkillClickAsync(entry))">
Remove
</button>
</td>
</tr>
}
</tbody>
<tfoot>
<tr>
<td colspan="2">
<button type="button" class="btn-primary"
@onclick="@this.OnAddRegularSkillClickAsync">
Add Skill
</button>
</td>
</tr>
</tfoot>
</table>
</div>
</div>

@* ── Master Skill Tree ──────────────────────────────────────────────── *@
@if (this.MasterSkillRoots.Count > 0)
{
<div class="card mb-3">
<h5 class="card-header d-flex justify-content-between align-items-center">
<span>Master Skill Tree</span>
<button type="button" class="btn-danger btn-sm"
@onclick="@this.OnResetMasterSkillsClickAsync"
title="Remove all master skill levels">
Reset Master Skills
</button>
</h5>
<div class="card-body">
<div class="master-skill-tree">
@foreach (var root in this.MasterSkillRoots)
{
<div class="master-skill-root">
<div class="master-skill-root-header">@root.Name</div>
@foreach (var rank in this.GetRanksForRoot(root))
{
<div class="master-skill-rank">
@foreach (var skill in this.GetSkillsForRootAndRank(root, rank))
{
var level = this.GetMasterSkillLevel(skill);
var maxLevel = skill.MasterDefinition!.MaximumLevel;
var requiredNames = this.GetRequiredSkillNames(skill);
<div class="master-skill-entry @(level > 0 ? "master-skill-active" : string.Empty)">
<div class="master-skill-name" title="@skill.GetName()">@skill.GetName()</div>
@if (!string.IsNullOrEmpty(requiredNames))
{
<div class="master-skill-requires" title="Requires (level ≥ 10): @requiredNames">
↑ @requiredNames
</div>
}
<input type="number"
class="master-skill-level-input"
value="@level"
min="0"
max="@maxLevel"
title="Level (0–@maxLevel)"
@onchange="@(e => this.OnMasterSkillLevelChangedAsync(skill, e))" />
</div>
}
</div>
}
</div>
}
</div>
</div>
</div>
}
</div>
Loading
Loading