Add items in search celect

This commit is contained in:
Maxime Adler
2025-04-14 14:40:41 +02:00
parent 3d713d5749
commit 63af58f5bb
15 changed files with 115 additions and 44 deletions

View File

@@ -19,13 +19,15 @@
</div>
<div class="input-game">
<div class="label">@ResourcesKey.Developers :</div>
<SelectSearch TItem="DeveloperDto" Theme="Theme" GetLabel="@(i => i.Name)"
Items="Categories?.Developers" @bind-Values=GameDto.Developers />
<SelectSearch TItem="DeveloperDto" Theme="Theme" GetLabel="@(i => i.Name)" QuickAdd=true
Items="Categories?.Developers" @bind-Values=GameDto.Developers
AddItem="@(str => new DeveloperDto() { Name = str })" />
</div>
<div class="input-game">
<div class="label">@ResourcesKey.Publishers :</div>
<SelectSearch TItem="PublisherDto" Theme="Theme" GetLabel="@(i => i.Name)"
Items="Categories?.Publishers" @bind-Values=GameDto.Publishers />
<SelectSearch TItem="PublisherDto" Theme="Theme" GetLabel="@(i => i.Name)" QuickAdd=true
Items="Categories?.Publishers" @bind-Values=GameDto.Publishers
AddItem="@(str => new PublisherDto() { Name = str })" />
</div>
</div>
<div class="container">
@@ -37,19 +39,22 @@
</div>
<div class="input-game">
<div class="label">@ResourcesKey.Properties :</div>
<SelectSearch TItem="PropertyDto" Theme="Theme" GetLabel="@(i => i.Label)"
Items="Categories?.Properties" @bind-Values=GameDto.Properties />
<SelectSearch TItem="PropertyDto" Theme="Theme" GetLabel="@(i => i.Label)" QuickAdd=true
Items="Categories?.Properties" @bind-Values=GameDto.Properties
AddItem="@(str => new PropertyDto() { Label = str })" />
</div>
<div class="input-game">
<div class="label">@ResourcesKey.Tags :</div>
<SelectSearch TItem="TagDto" Theme="Theme" GetLabel="@(i => i.Label)"
Items="Categories?.Tags" @bind-Values=GameDto.Tags />
<SelectSearch TItem="TagDto" Theme="Theme" GetLabel="@(i => i.Label)" QuickAdd=true
Items="Categories?.Tags" @bind-Values=GameDto.Tags
AddItem="@(str => new TagDto() { Label = str })" />
</div>
<div class="input-game">
<div class="label">@ResourcesKey.Platforms :</div>
<SelectSearch TItem="PlatformDto" Theme="Theme" GetLabel="@(i => i.Label)"
Items="Categories?.Platforms" @bind-Values=GameDto.Platforms />
<SelectSearch TItem="PlatformDto" Theme="Theme" GetLabel="@(i => i.Label)" QuickAdd=true
Items="Categories?.Platforms" @bind-Values=GameDto.Platforms
AddItem="@(str => new PlatformDto() { Label = str })" />
</div>
</div>
</div>

View File

@@ -36,10 +36,10 @@ public partial class GameFilter
{
Headers = SortTypes,
GetHeaderLabel = header => header.Label,
DefaultHeader = SortTypes.FirstOrDefault(h => h.SortType == SortType.Ascending),
DefaultHeaders = SortTypes.Where(h => h.SortType == SortType.Ascending).ToList(),
Items = GameProperties,
GetItemLabel = item => item.Label,
DefaultItem = GameProperties.FirstOrDefault(p => p.Label == "Titre")
DefaultItems = GameProperties.Where(p => p.Label == "Titre").ToList()
};
}

View File

@@ -23,8 +23,7 @@
</svg>
</div>
<Select @ref="SelectListAdd" TItem="KeyValuePair<AddType, string>" THeader="object"
ValuesChanged=HandleAddTypeClicked Params=SelectParams
Theme="SelectTheme.Navigation">
ValuesChanged=HandleAddTypeClicked Params=SelectParams Theme="SelectTheme.Navigation">
<div class="second-button button">
<svg class="button-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path d="M1 3H23L12 22" />

View File

@@ -28,6 +28,7 @@ public partial class GameHeader : ComponentBase
{
Items = AddTypes.ToList(),
GetItemLabel = item => item.Value,
DefaultItems = []
};
base.OnInitialized();

View File

@@ -1,5 +1,9 @@
.backdrop-filter {
position: fixed;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
inset: 0;
z-index: var(--index-backdrop);
}

View File

@@ -1,16 +1,12 @@
.popup-wrapper {
display: none;
justify-content: center;
align-items: center;
justify-self: anchor-center;
align-self: anchor-center;
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: var(--index-popup);
}
.popup-content {
position: relative;
background-color: var(--dropdown-content);
padding: 10px;
border-radius: var(--big-radius);

View File

@@ -3,10 +3,11 @@
public class SelectParams<TItem, THeader>
{
public List<TItem> Items { get; set; } = [];
public TItem? DefaultItem { get; set; }
public List<TItem> DefaultItems { get; set; } = [];
public Func<TItem, string> GetItemLabel { get; set; } = _ => string.Empty;
public List<THeader> Headers { get; set; } = [];
public THeader? DefaultHeader { get; set; }
public List<THeader> DefaultHeaders { get; set; } = [];
public Func<THeader, string> GetHeaderLabel { get; set; } = _ => string.Empty;
public Func<string, TItem>? AddItem { get; set; }
}

View File

@@ -14,9 +14,20 @@
@if (IsContentOpen)
{
<div class="content">
@if (QuickAdd)
{
<div class="add-item">
<EditForm EditContext="QuickAddEditContext" OnSubmit="HandleSubmitAdd">
<input type="text" placeholder="@ResourcesKey.PlaceholderAdd" @bind=AddLabel>
</EditForm>
</div>
<span class="line"></span>
}
@if (Params.Headers != null)
{
@foreach (var header in Params.Headers)
@foreach (var header in Params.Headers.Union(HeaderValues ?? []))
{
<SelectRow IsSelected=HeaderValues?.Contains(header)
Label="@Params.GetHeaderLabel(header)" Theme=Theme
@@ -24,14 +35,14 @@
}
}
@if (Params.Headers?.Any() == true)
@if (Params.Headers?.Count != 0)
{
<span class="line"></span>
}
@if (Params.Items != null)
{
@foreach (var item in Params.Items)
@foreach (var item in Params.Items.Union(Values ?? []))
{
<SelectRow IsSelected=Values?.Contains(item)
Label="@Params.GetItemLabel(item)" Theme=Theme

View File

@@ -1,5 +1,9 @@
using GameIdeas.BlazorApp.Shared.Components.Select.Models;
using GameIdeas.Resources;
using GameIdeas.Shared.Constants;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Forms;
using System.Text.RegularExpressions;
namespace GameIdeas.BlazorApp.Shared.Components.Select;
@@ -14,8 +18,11 @@ public partial class Select<TItem, THeader>
[Parameter] public SelectTheme Theme { get; set; }
[Parameter] public SelectType Type { get; set; } = SelectType.Single;
[Parameter] public bool DisableClicked { get; set; } = false;
[Parameter] public bool QuickAdd { get; set; } = false;
private bool IsContentOpen = false;
private string AddLabel = string.Empty;
private EditContext? QuickAddEditContext;
public void Close() =>
IsContentOpen = false;
@@ -23,6 +30,21 @@ public partial class Select<TItem, THeader>
public void Open() =>
IsContentOpen = true;
protected override void OnInitialized()
{
QuickAddEditContext = new EditContext(AddLabel);
if (Params.DefaultItems.Count != 0)
{
Values.AddRange(Params.DefaultItems);
}
if (Params.DefaultHeaders.Count != 0)
{
HeaderValues.AddRange(Params.DefaultHeaders);
}
}
private void HandleButtonClicked()
{
if (!DisableClicked)
@@ -32,19 +54,6 @@ public partial class Select<TItem, THeader>
private void HandleContentClosed() =>
IsContentOpen = false;
protected override void OnInitialized()
{
if (Params.DefaultItem != null)
{
Values.Add(Params.DefaultItem);
}
if (Params.DefaultHeader != null)
{
HeaderValues.Add(Params.DefaultHeader);
}
}
private async Task HandleValueClicked(TItem value)
{
if (Type != SelectType.Multiple || Values == null)
@@ -82,4 +91,17 @@ public partial class Select<TItem, THeader>
await HeaderValuesChanged.InvokeAsync(HeaderValues);
}
private async Task HandleSubmitAdd()
{
if (Regex.IsMatch(AddLabel, GlobalConstants.RegexSelectRow) &&
Params.AddItem != null)
{
Values ??= [];
Values.Add(Params.AddItem(AddLabel));
AddLabel = string.Empty;
await ValuesChanged.InvokeAsync(Values);
}
}
}

View File

@@ -29,6 +29,20 @@
display: none;
}
.add-item {
align-content: center;
height: 24px;
padding: 0 10px;
}
.add-item input {
width: 100%;
color: var(--white);
border: none;
outline: none;
background: transparent;
}
.dropdown::-webkit-scrollbar {
width: 10px;
}
@@ -72,4 +86,16 @@
border-bottom: 2px solid var(--input-selected);
}
/***** Sort Theme *****/
.creation .content {
border-radius: var(--small-radius);
box-sizing: border-box;
border: solid 1px var(--violet);
}
.creation .content .line {
display: block;
margin: 2px 6px;
border-bottom: 2px solid var(--input-selected);
}

View File

@@ -6,7 +6,7 @@
@typeparam TItem
<Select @ref=Select TItem="TItem" THeader="string" Theme="Theme" Type="SelectType.Multiple" DisableClicked=true
Params="SelectParams" Values=Values ValuesChanged="HandleValuesChanged">
Params="SelectParams" Values=Values ValuesChanged="HandleValuesChanged" QuickAdd=QuickAdd>
<div class="@SelectHelper.GetClassFromTheme(Theme)">
<SearchInput @ref=SearchInput Icon="SearchInputIcon.Dropdown" Placeholder="@Placeholder"

View File

@@ -13,16 +13,20 @@ public partial class SelectSearch<TItem>
[Parameter] public List<TItem> Values { get; set; } = [];
[Parameter] public EventCallback<List<TItem>> ValuesChanged { get; set; }
[Parameter] public string Placeholder { get; set; } = string.Empty;
[Parameter] public bool QuickAdd { get; set; } = false;
[Parameter] public Func<string, TItem>? AddItem { get; set; }
private SelectParams<TItem, string> SelectParams = new();
private SearchInput? SearchInput;
private Select<TItem, string>? Select;
protected override void OnParametersSet()
{
SelectParams = new()
{
Items = Items,
GetItemLabel = GetLabel
GetItemLabel = GetLabel,
AddItem = AddItem
};
base.OnParametersSet();

View File

@@ -34,6 +34,7 @@ public class Translations (TranslationService translationService)
public string ErrorWhenFetchingData => translationService.Translate(nameof(ErrorWhenFetchingData));
public string RequestFailedStatusFormat => translationService.Translate(nameof(RequestFailedStatusFormat));
public string ErrorFetchCategories => translationService.Translate(nameof(ErrorFetchCategories));
public string PlaceholderAdd => translationService.Translate(nameof(PlaceholderAdd));
}
public static class ResourcesKey
@@ -76,4 +77,5 @@ public static class ResourcesKey
public static string ErrorWhenFetchingData => _instance?.ErrorWhenFetchingData ?? throw new InvalidOperationException("ResourcesKey.ErrorWhenFetchingData is not initialized.");
public static string RequestFailedStatusFormat => _instance?.RequestFailedStatusFormat ?? throw new InvalidOperationException("ResourcesKey.RequestFailedStatusFormat is not initialized.");
public static string ErrorFetchCategories => _instance?.ErrorFetchCategories ?? throw new InvalidOperationException("ResourcesKey.ErrorFetchCategories is not initialized.");
public static string PlaceholderAdd => _instance?.PlaceholderAdd ?? throw new InvalidOperationException("ResourcesKey.PlaceholderAdd is not initialized.");
}

View File

@@ -2,6 +2,5 @@
public class GlobalConstants
{
public const string EnterKeyCode = "Enter";
public const string PadEnterKeyCode = "NumpadEnter";
public const string RegexSelectRow = @"[a-zA-Z_ \-]*";
}

View File

@@ -29,5 +29,6 @@
"ErrorWhenDeletingData": "Erreur lors de la requête DELETE",
"ErrorWhenFetchingData": "Erreur lors de la requête GET",
"RequestFailedStatusFormat": "Erreur lors de la réponse, code {0}",
"ErrorFetchCategories": "Erreur lors de la récupération des catégories"
"ErrorFetchCategories": "Erreur lors de la récupération des catégories",
"PlaceholderAdd": "Ajouter un nouveau"
}