feat: Add reusable MaterialFilter component
Provides shape, type, grade dropdowns and text search for filtering material-based list pages. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
79
CutList.Web/Components/Shared/MaterialFilter.razor
Normal file
79
CutList.Web/Components/Shared/MaterialFilter.razor
Normal file
@@ -0,0 +1,79 @@
|
||||
@using CutList.Web.Data.Entities
|
||||
|
||||
<div class="row g-2 mb-3">
|
||||
<div class="col-auto">
|
||||
<select class="form-select form-select-sm" value="@Value.Shape" @onchange="OnShapeChanged">
|
||||
<option value="">All Shapes</option>
|
||||
@foreach (var shape in Enum.GetValues<MaterialShape>())
|
||||
{
|
||||
<option value="@shape">@shape.GetDisplayName()</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<select class="form-select form-select-sm" value="@Value.Type" @onchange="OnTypeChanged">
|
||||
<option value="">All Types</option>
|
||||
@foreach (var type in Enum.GetValues<MaterialType>())
|
||||
{
|
||||
<option value="@type">@type</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<select class="form-select form-select-sm" value="@(Value.Grade ?? "")" @onchange="OnGradeChanged">
|
||||
<option value="">All Grades</option>
|
||||
@foreach (var grade in AvailableGrades)
|
||||
{
|
||||
<option value="@grade">@grade</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<input type="text" class="form-control form-control-sm" placeholder="Search..." value="@Value.SearchText" @oninput="OnSearchInput" />
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<button class="btn btn-sm btn-outline-secondary" @onclick="OnClear" title="Clear filters">
|
||||
<i class="bi bi-x-lg"></i> Clear
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@code {
|
||||
[Parameter] public IEnumerable<string> AvailableGrades { get; set; } = Enumerable.Empty<string>();
|
||||
[Parameter] public MaterialFilterState Value { get; set; } = new();
|
||||
[Parameter] public EventCallback<MaterialFilterState> ValueChanged { get; set; }
|
||||
|
||||
private async Task OnShapeChanged(ChangeEventArgs e)
|
||||
{
|
||||
Value.Shape = Enum.TryParse<MaterialShape>(e.Value?.ToString(), out var shape) ? shape : null;
|
||||
await ValueChanged.InvokeAsync(Value);
|
||||
}
|
||||
|
||||
private async Task OnTypeChanged(ChangeEventArgs e)
|
||||
{
|
||||
Value.Type = Enum.TryParse<MaterialType>(e.Value?.ToString(), out var type) ? type : null;
|
||||
await ValueChanged.InvokeAsync(Value);
|
||||
}
|
||||
|
||||
private async Task OnGradeChanged(ChangeEventArgs e)
|
||||
{
|
||||
var val = e.Value?.ToString();
|
||||
Value.Grade = string.IsNullOrEmpty(val) ? null : val;
|
||||
await ValueChanged.InvokeAsync(Value);
|
||||
}
|
||||
|
||||
private async Task OnSearchInput(ChangeEventArgs e)
|
||||
{
|
||||
Value.SearchText = e.Value?.ToString();
|
||||
await ValueChanged.InvokeAsync(Value);
|
||||
}
|
||||
|
||||
private async Task OnClear()
|
||||
{
|
||||
Value.Shape = null;
|
||||
Value.Type = null;
|
||||
Value.Grade = null;
|
||||
Value.SearchText = null;
|
||||
await ValueChanged.InvokeAsync(Value);
|
||||
}
|
||||
}
|
||||
11
CutList.Web/Components/Shared/MaterialFilterState.cs
Normal file
11
CutList.Web/Components/Shared/MaterialFilterState.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using CutList.Web.Data.Entities;
|
||||
|
||||
namespace CutList.Web.Components.Shared;
|
||||
|
||||
public class MaterialFilterState
|
||||
{
|
||||
public MaterialShape? Shape { get; set; }
|
||||
public MaterialType? Type { get; set; }
|
||||
public string? Grade { get; set; }
|
||||
public string? SearchText { get; set; }
|
||||
}
|
||||
Reference in New Issue
Block a user