141 lines
3.6 KiB
Plaintext
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;
|
|
}
|
|
} |