Files
Moonlight/Moonlight/Shared/Components/Forms/SmartDropdown.razor

141 lines
3.6 KiB
Plaintext

@using Microsoft.AspNetCore.Components.Forms
@typeparam T
@inherits InputBase<T>
<div class="dropdown w-100">
<div class="input-group">
@if (CurrentValue == null)
{
<input class="form-control" type="text" @bind-value="SearchTerm" @bind-value:event="oninput" placeholder="@(Placeholder)">
}
else
{
<input class="form-control" type="text" value="@(DisplayFunc(CurrentValue))">
<button class="btn btn-sm btn-primary" @onclick="() => SelectItem(default(T)!)">
<i class="bx bx-sm bx-x"></i>
</button>
}
</div>
@{
var anyItems = FilteredItems.Any();
}
<div class="dropdown-menu w-100 @(anyItems ? "show" : "")" style="max-height: 200px; overflow-y: auto;">
@if (anyItems)
{
foreach (var item in FilteredItems)
{
<button class="dropdown-item py-2" type="button" @onclick="() => SelectItem(item)">@DisplayFunc(item)</button>
}
}
</div>
</div>
@code {
[Parameter]
public IEnumerable<T> Items { get; set; }
[Parameter]
public Func<T, string> DisplayFunc { get; set; }
[Parameter]
public Func<T, string> SearchProp { get; set; }
[Parameter]
public Func<Task>? OnSelected { get; set; }
[Parameter]
public string Placeholder { get; set; } = "Search...";
private string SearchTerm
{
get => searchTerm;
set
{
searchTerm = value;
FilteredItems = Items.OrderByDescending(x => Matches(SearchProp(x), searchTerm)).Take(30).ToList();
}
}
private string searchTerm = "";
private List<T> FilteredItems = new();
private async void SelectItem(T item)
{
CurrentValue = item;
SearchTerm = "";
FilteredItems.Clear();
if (OnSelected != null)
await OnSelected.Invoke();
}
protected override bool TryParseValueFromString(string? value, out T result, out string? validationErrorMessage)
{
// Check if the value is null or empty
if (string.IsNullOrEmpty(value))
{
result = default(T)!;
validationErrorMessage = "Value cannot be null or empty";
return false;
}
// Try to find an item that matches the search term
var item = FilteredItems.OrderByDescending(x => Matches(SearchProp(x), value)).FirstOrDefault();
if (item != null)
{
result = item;
validationErrorMessage = null;
return true;
}
else
{
result = default(T)!;
validationErrorMessage = $"No item found for search term '{value}'";
return false;
}
}
private float Matches(string input, string search)
{
if (string.IsNullOrEmpty(input) || string.IsNullOrEmpty(search))
return 0;
var cleanedSearch = search
.ToLower()
.Replace(" ", "")
.Replace("-", "");
var cleanedInput = input
.ToLower()
.Replace(" ", "")
.Replace("-", "");
if (cleanedInput == cleanedSearch)
return 10000;
float matches = 0;
int i = 0;
foreach (var c in cleanedInput)
{
if (cleanedSearch.Length > i)
{
if (c == cleanedSearch[i])
matches++;
else
matches--;
}
i++;
}
matches = matches / searchTerm.Length;
return matches;
}
}