Add game header #5
@@ -0,0 +1,65 @@
|
|||||||
|
@using GameIdeas.BlazorApp.Shared.Components.Search
|
||||||
|
@using GameIdeas.BlazorApp.Shared.Components.Select
|
||||||
|
@using GameIdeas.BlazorApp.Shared.Components.Select.Models
|
||||||
|
@using GameIdeas.BlazorApp.Shared.Components.SliderRange
|
||||||
|
@using GameIdeas.Shared.Dto
|
||||||
|
@using GameIdeas.BlazorApp.Pages.Games.Models
|
||||||
|
|
||||||
|
|
||||||
|
<EditForm EditContext="EditContext">
|
||||||
|
<div class="form-filter">
|
||||||
|
<SelectList TItem="Func<GameDto, object>"
|
||||||
|
Headers="SortTypes"
|
||||||
|
Items="GameProperties"
|
||||||
|
@bind-Value=GameFilterParams.SortProperty
|
||||||
|
HeaderChanged=HandleSortTypeChanged
|
||||||
|
Theme="SelectListTheme.Sort">
|
||||||
|
<Button>
|
||||||
|
<div class="square-button">
|
||||||
|
<svg class="sort-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||||
|
<path d="M10,13H22V11H10M10,19H22V17H10M10,7H22V5H10M6,7H8.5L5,3.5L1.5,7H4V17H1.5L5,20.5L8.5,17H6V7Z" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</Button>
|
||||||
|
</SelectList>
|
||||||
|
|
||||||
|
<div class="square-button" @onclick="@(() => HandleDisplayClicked(DisplayType.List))">
|
||||||
|
<svg class="list-icon @(DisplayType == DisplayType.List ? "selected-icon" : "")" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||||
|
<path d="M4,5V7H21V5M4,11H21V9H4M4,19H21V17H4M4,15H21V13H4V15Z" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="square-button" @onclick="@(() => HandleDisplayClicked(DisplayType.Card))">
|
||||||
|
<svg class="card-icon @(DisplayType == DisplayType.Card ? "selected-icon" : "")" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||||
|
<path d="M16,5V11H21V5M10,11H15V5H10M16,18H21V12H16M10,18H15V12H10M4,18H9V12H4M4,11H9V5H4V11Z" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="search-container">
|
||||||
|
<SearchInput @bind-Text=GameFilterParams.SearchName />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="select-container">
|
||||||
|
<MultipleSelectList TItem="string"
|
||||||
|
Items="Plateforms"
|
||||||
|
@bind-Values=GameFilterParams.Plateforms
|
||||||
|
Theme="SelectListTheme.Filter" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="select-container">
|
||||||
|
<MultipleSelectList TItem="string"
|
||||||
|
Items="Genres"
|
||||||
|
@bind-Values=GameFilterParams.Genres
|
||||||
|
Theme="SelectListTheme.Filter" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="slider-container">
|
||||||
|
<SliderRange Params="SliderRangeParams"
|
||||||
|
@bind-Max=GameFilterParams.MaxRating
|
||||||
|
@bind-Min=GameFilterParams.MinRating />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</EditForm>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
using GameIdeas.BlazorApp.Pages.Games.Models;
|
||||||
|
using GameIdeas.BlazorApp.Shared.Components.Select.Models;
|
||||||
|
using GameIdeas.BlazorApp.Shared.Components.SliderRange;
|
||||||
|
using GameIdeas.Shared.Dto;
|
||||||
|
using GameIdeas.Shared.Enum;
|
||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
using Microsoft.AspNetCore.Components.Forms;
|
||||||
|
|
||||||
|
namespace GameIdeas.BlazorApp.Pages.Games.Filter;
|
||||||
|
|
||||||
|
public partial class GameFilter
|
||||||
|
{
|
||||||
|
[Parameter] public GameFilterParams GameFilterParams { get; set; } = new();
|
||||||
|
[Parameter] public EventCallback<GameFilterParams> GameFilterParamsChanged { get; set; }
|
||||||
|
[Parameter] public DisplayType DisplayType { get; set; }
|
||||||
|
[Parameter] public EventCallback<DisplayType> DisplayTypeChanged { get; set; }
|
||||||
|
|
||||||
|
private readonly IEnumerable<SelectElement<Func<GameDto?, object?>>> SortTypes = [
|
||||||
|
new() { Item = _ => SortType.Ascending, Label = "Ascendant", IsSelected = true },
|
||||||
|
new() { Item = _ => SortType.Descending, Label = "Descendant" }
|
||||||
|
];
|
||||||
|
|
||||||
|
private readonly IEnumerable<SelectElement<Func<GameDto?, object?>>> GameProperties = [
|
||||||
|
new() { Item = game => game?.Name, Label = "Nom", IsSelected = true },
|
||||||
|
new() { Item = game => game?.ReleaseDate, Label = "Date de parution" }
|
||||||
|
];
|
||||||
|
|
||||||
|
private readonly IEnumerable<SelectElement<string>> Plateforms = [
|
||||||
|
new() { Item = "Steam", Label = "Steam" },
|
||||||
|
new() { Item = "GOG", Label = "GOG" },
|
||||||
|
new() { Item = "Epic games", Label = "Epic games" },
|
||||||
|
new() { Item = "Ubisoft", Label = "Ubisoft" },
|
||||||
|
];
|
||||||
|
|
||||||
|
private readonly IEnumerable<SelectElement<string>> Genres = [
|
||||||
|
new() { Item = "Rogue Like", Label = "Rogue Like" },
|
||||||
|
new() { Item = "Aventure", Label = "Aventure" },
|
||||||
|
new() { Item = "RPG", Label = "RPG" },
|
||||||
|
new() { Item = "Fast FPS", Label = "Fast FPS" },
|
||||||
|
];
|
||||||
|
|
||||||
|
private EditContext? EditContext;
|
||||||
|
private SliderRangeParams SliderRangeParams =
|
||||||
|
new() { Min = 1, ValueMin = 1, ValueMax = 5, Max = 5 };
|
||||||
|
|
||||||
|
protected override void OnInitialized()
|
||||||
|
{
|
||||||
|
EditContext = new EditContext(GameFilterParams);
|
||||||
|
EditContext.OnFieldChanged += async (s, e) =>
|
||||||
|
{
|
||||||
|
await GameFilterParamsChanged.InvokeAsync(GameFilterParams);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HandleSortTypeChanged(Func<GameDto?, object?> getHeader)
|
||||||
|
{
|
||||||
|
GameFilterParams.SortType = (SortType?)getHeader(null) ?? SortType.Ascending;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task HandleDisplayClicked(DisplayType displayType)
|
||||||
|
{
|
||||||
|
DisplayType = displayType;
|
||||||
|
await DisplayTypeChanged.InvokeAsync(displayType);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
.form-filter {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
gap: 8px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-container {
|
||||||
|
width: 150px;
|
||||||
|
min-width: 150px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slider-container {
|
||||||
|
width: 150px;
|
||||||
|
min-width: 150px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select-container {
|
||||||
|
width: 150px;
|
||||||
|
min-width: 150px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.square-button {
|
||||||
|
height: 28px;
|
||||||
|
min-height: 28px;
|
||||||
|
width: 28px;
|
||||||
|
min-width: 28px;
|
||||||
|
border-radius: var(--small-radius);
|
||||||
|
background: var(--black);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.square-button:first-child {
|
||||||
|
margin-right: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.square-button svg {
|
||||||
|
fill: var(--white);
|
||||||
|
}
|
||||||
|
|
||||||
|
.square-button svg:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
background: var(--low-white);
|
||||||
|
}
|
||||||
|
|
||||||
|
.selected-icon {
|
||||||
|
fill: var(--violet) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sort-icon {
|
||||||
|
padding: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-icon {
|
||||||
|
padding-right: 1px;
|
||||||
|
padding-top: 0.5px;
|
||||||
|
padding-bottom: 0.5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-icon {
|
||||||
|
padding-top: 1px;
|
||||||
|
padding-right: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 1000px) {
|
||||||
|
.select-container {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
using GameIdeas.Shared.Enum;
|
||||||
|
using GameIdeas.Shared.Dto;
|
||||||
|
|
||||||
|
namespace GameIdeas.BlazorApp.Pages.Games.Filter;
|
||||||
|
|
||||||
|
public class GameFilterParams
|
||||||
|
{
|
||||||
|
public SortType? SortType { get; set; }
|
||||||
|
public Func<GameDto?, object?>? SortProperty { get; set; }
|
||||||
|
public string? SearchName { get; set; }
|
||||||
|
public IEnumerable<string>? Plateforms { get; set; }
|
||||||
|
public IEnumerable<string>? Genres { get; set; }
|
||||||
|
public int MaxRating { get; set; }
|
||||||
|
public int MinRating { get; set; }
|
||||||
|
}
|
||||||
@@ -1,15 +1,16 @@
|
|||||||
@page "/Games"
|
@page "/Games"
|
||||||
@using GameIdeas.BlazorApp.Layouts
|
@using GameIdeas.BlazorApp.Layouts
|
||||||
|
@using GameIdeas.BlazorApp.Pages.Games.Filter
|
||||||
@using GameIdeas.BlazorApp.Shared.Components
|
@using GameIdeas.BlazorApp.Shared.Components
|
||||||
@using GameIdeas.BlazorApp.Shared.Headers
|
@using GameIdeas.BlazorApp.Shared.Layouts.Header
|
||||||
@using GameIdeas.Resources
|
@using GameIdeas.Resources
|
||||||
|
|
||||||
@layout MainLayout
|
@layout MainLayout
|
||||||
|
|
||||||
<PageTitle>@ResourcesKey.GamesIdeas</PageTitle>
|
<PageTitle>@ResourcesKey.GamesIdeas</PageTitle>
|
||||||
|
|
||||||
<HeaderBase>
|
<HeaderLayout>
|
||||||
<Body>
|
<Body>
|
||||||
<div>PROUT</div>
|
<GameFilter @bind-DisplayType=DisplayType />
|
||||||
</Body>
|
</Body>
|
||||||
</HeaderBase>
|
</HeaderLayout>
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
|
using GameIdeas.BlazorApp.Pages.Games.Models;
|
||||||
|
|
||||||
namespace GameIdeas.BlazorApp.Pages.Games;
|
namespace GameIdeas.BlazorApp.Pages.Games;
|
||||||
|
|
||||||
public partial class GamesBase ()
|
public partial class GamesBase ()
|
||||||
{
|
{
|
||||||
|
private DisplayType DisplayType = DisplayType.List;
|
||||||
}
|
}
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
namespace GameIdeas.BlazorApp.Pages.Games.Models;
|
|
||||||
|
|
||||||
public enum AccountSetting
|
|
||||||
{
|
|
||||||
Logout
|
|
||||||
}
|
|
||||||
|
|
||||||
public class AccountSettingParams
|
|
||||||
{
|
|
||||||
public string Label { get; set; }
|
|
||||||
public AccountSetting AccountSetting { get; set; }
|
|
||||||
public bool ForConnected { get; set; }
|
|
||||||
|
|
||||||
public AccountSettingParams(string label, AccountSetting accountSetting, bool forConnected)
|
|
||||||
{
|
|
||||||
Label = label;
|
|
||||||
AccountSetting = accountSetting;
|
|
||||||
ForConnected = forConnected;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -5,9 +5,3 @@ public enum AddType
|
|||||||
Manual,
|
Manual,
|
||||||
Auto
|
Auto
|
||||||
}
|
}
|
||||||
|
|
||||||
public class AddTypeParams(AddType addType, string label)
|
|
||||||
{
|
|
||||||
public AddType AddType { get; set; } = addType;
|
|
||||||
public string? Label { get; set; } = label;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
namespace GameIdeas.BlazorApp.Pages.Games.Models;
|
||||||
|
|
||||||
|
public enum DisplayType
|
||||||
|
{
|
||||||
|
Card,
|
||||||
|
List
|
||||||
|
}
|
||||||
@@ -7,6 +7,7 @@
|
|||||||
"launchBrowser": true,
|
"launchBrowser": true,
|
||||||
"inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
|
"inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
|
||||||
"applicationUrl": "http://localhost:5172",
|
"applicationUrl": "http://localhost:5172",
|
||||||
|
"launchUrl": "http://localhost:5172/Games",
|
||||||
"environmentVariables": {
|
"environmentVariables": {
|
||||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
}
|
}
|
||||||
@@ -17,6 +18,7 @@
|
|||||||
"launchBrowser": true,
|
"launchBrowser": true,
|
||||||
"inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
|
"inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
|
||||||
"applicationUrl": "https://localhost:7060;http://localhost:5172",
|
"applicationUrl": "https://localhost:7060;http://localhost:5172",
|
||||||
|
"launchUrl": "http://localhost:7060/Games",
|
||||||
"environmentVariables": {
|
"environmentVariables": {
|
||||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
@using GameIdeas.Resources
|
@using GameIdeas.Resources
|
||||||
@using Blazored.FluentValidation;
|
@using Blazored.FluentValidation;
|
||||||
|
|
||||||
<div class="account-setting-container" tabindex="1001">
|
<div class="account-setting-container" tabindex="1000">
|
||||||
<div class="account-setting-content @(ContentVisile ? string.Empty : "invisible")">
|
<div class="account-setting-content @(ContentVisile ? string.Empty : "invisible")">
|
||||||
@if (!AuthentificationService.IsLogin)
|
@if (!AuthentificationService.IsLogin)
|
||||||
{
|
{
|
||||||
@@ -36,10 +36,13 @@
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
<div class="settings-list">
|
<div class="settings-list">
|
||||||
|
<div class="settings-element">
|
||||||
|
@ResourcesKey.UserManager
|
||||||
|
</div>
|
||||||
|
<span class="line"></span>
|
||||||
<div class="settings-element" @onclick="HandleLogoutClicked">
|
<div class="settings-element" @onclick="HandleLogoutClicked">
|
||||||
@ResourcesKey.Logout
|
@ResourcesKey.Logout
|
||||||
</div>
|
</div>
|
||||||
@* <span class="line"></span> *@
|
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
@@ -2,7 +2,7 @@ using GameIdeas.BlazorApp.Services;
|
|||||||
using GameIdeas.Shared.Dto;
|
using GameIdeas.Shared.Dto;
|
||||||
using Microsoft.AspNetCore.Components.Forms;
|
using Microsoft.AspNetCore.Components.Forms;
|
||||||
|
|
||||||
namespace GameIdeas.BlazorApp.Shared.Headers;
|
namespace GameIdeas.BlazorApp.Shared.Components.Account;
|
||||||
|
|
||||||
public partial class AccountSettings (
|
public partial class AccountSettings (
|
||||||
AuthentificationService AuthentificationService)
|
AuthentificationService AuthentificationService)
|
||||||
@@ -39,17 +39,17 @@ public partial class AccountSettings (
|
|||||||
{
|
{
|
||||||
if (EditContext?.Validate() == false)
|
if (EditContext?.Validate() == false)
|
||||||
{
|
{
|
||||||
LoginDto.Password = string.Empty;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
IsLoading = true;
|
IsLoading = true;
|
||||||
await Task.Delay(TimeSpan.FromSeconds(5));
|
await Task.Delay(TimeSpan.FromSeconds(5));
|
||||||
|
Close();
|
||||||
|
AuthentificationService.Login();
|
||||||
IsLoading = false;
|
IsLoading = false;
|
||||||
|
|
||||||
LoginDto = new();
|
LoginDto = new();
|
||||||
Close();
|
EditContext = new EditContext(LoginDto);
|
||||||
AuthentificationService.Login();
|
|
||||||
}
|
}
|
||||||
private void HandleLogoutClicked()
|
private void HandleLogoutClicked()
|
||||||
{
|
{
|
||||||
@@ -4,8 +4,8 @@
|
|||||||
position: fixed;
|
position: fixed;
|
||||||
animation-name: fade-in;
|
animation-name: fade-in;
|
||||||
animation-duration: 0.4s;
|
animation-duration: 0.4s;
|
||||||
border: 2px solid var(--light-grey);
|
border: 2px solid var(--low-white);
|
||||||
background: var(--black);
|
background: var(--semi-black);
|
||||||
right: 10px;
|
right: 10px;
|
||||||
margin-top: 4px;
|
margin-top: 4px;
|
||||||
}
|
}
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
.login-form {
|
.login-form {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
padding: 20px 6px;
|
padding: 20px 8px;
|
||||||
gap: 20px;
|
gap: 20px;
|
||||||
max-width: 400px;
|
max-width: 400px;
|
||||||
}
|
}
|
||||||
@@ -30,8 +30,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
::deep .input-text {
|
::deep .input-text {
|
||||||
background: var(--light-grey);
|
background: var(--low-white);
|
||||||
border: 2px solid rgb(255, 255, 255, 0.3);
|
border: 2px solid var(--low-white);
|
||||||
border-radius: var(--small-radius);
|
border-radius: var(--small-radius);
|
||||||
padding: 6px;
|
padding: 6px;
|
||||||
color: var(--white);
|
color: var(--white);
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
using FluentValidation;
|
using FluentValidation;
|
||||||
using GameIdeas.Shared.Dto;
|
using GameIdeas.Shared.Dto;
|
||||||
|
|
||||||
namespace GameIdeas.BlazorApp.Shared.Headers;
|
namespace GameIdeas.BlazorApp.Shared.Components.Account;
|
||||||
|
|
||||||
public class LoginValidator : AbstractValidator<LoginDto>
|
public class LoginValidator : AbstractValidator<LoginDto>
|
||||||
{
|
{
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
@typeparam TItem
|
|
||||||
|
|
||||||
<div @ref=Content class="dropdown-container" tabindex="1000"
|
|
||||||
@onfocusout=HandleDropdownAddFocusOut>
|
|
||||||
<div class="dropdown-content @(ContentVisile ? string.Empty : "invisible") @(Enum.GetName(Theme)?.ToLower())">
|
|
||||||
@foreach (var item in Items)
|
|
||||||
{
|
|
||||||
<div class="drowdown-element @(Enum.GetName(Theme)?.ToLower())" @onclick=HandleItemClicked>
|
|
||||||
@(LabelSelector != null ? LabelSelector.Invoke(item) : item?.ToString())
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Components;
|
|
||||||
|
|
||||||
namespace GameIdeas.BlazorApp.Shared.Components.Dropdown;
|
|
||||||
|
|
||||||
public partial class DropdownContent<TItem>
|
|
||||||
{
|
|
||||||
[Parameter] public TItem? Value { get; set; }
|
|
||||||
[Parameter] public EventCallback<TItem?> ValueChanged { get; set; }
|
|
||||||
[Parameter] public IEnumerable<TItem> Items { get; set; } = [];
|
|
||||||
[Parameter] public Func<TItem, string>? LabelSelector { get; set; }
|
|
||||||
[Parameter] public DropdownTheme Theme { get; set; }
|
|
||||||
|
|
||||||
private bool ContentVisile = false;
|
|
||||||
private DateTime ContentLastFocusOut = DateTime.Now;
|
|
||||||
private ElementReference Content;
|
|
||||||
|
|
||||||
public async Task OpenAsync()
|
|
||||||
{
|
|
||||||
if (DateTime.Now - ContentLastFocusOut >= TimeSpan.FromSeconds(0.2))
|
|
||||||
{
|
|
||||||
await Content.FocusAsync();
|
|
||||||
ContentVisile = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Close() => ContentVisile = false;
|
|
||||||
|
|
||||||
private void HandleDropdownAddFocusOut()
|
|
||||||
{
|
|
||||||
ContentLastFocusOut = DateTime.Now;
|
|
||||||
ContentVisile = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task HandleItemClicked() => await ValueChanged.InvokeAsync(Value);
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
.dropdown-content {
|
|
||||||
overflow: hidden;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
border-radius: var(--small-radius);
|
|
||||||
position: fixed;
|
|
||||||
animation-name: fade-in;
|
|
||||||
animation-duration: 0.4s;
|
|
||||||
margin-top: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.drowdown-element {
|
|
||||||
width: fit-content;
|
|
||||||
}
|
|
||||||
|
|
||||||
.drowdown-element:hover {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.invisible {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dropdown-content.navigation {
|
|
||||||
gap: 4px;
|
|
||||||
background: var(--violet);
|
|
||||||
box-shadow: var(--drop-shadow);
|
|
||||||
padding: 4px 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.drowdown-element.navigation:hover {
|
|
||||||
color: var(--light-grey);
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
namespace GameIdeas.BlazorApp.Shared.Components.Dropdown;
|
|
||||||
|
|
||||||
public enum DropdownTheme
|
|
||||||
{
|
|
||||||
Navigation,
|
|
||||||
Account
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
<div class="search-container">
|
||||||
|
<input type="text"
|
||||||
|
class="search-field"
|
||||||
|
@bind=@Text
|
||||||
|
@bind:event="oninput"
|
||||||
|
@bind:after=HandleTextChanged />
|
||||||
|
|
||||||
|
@if (!string.IsNullOrEmpty(Text))
|
||||||
|
{
|
||||||
|
<div class="clear-icon" @onclick=HandleClearClicked>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||||
|
<path d="M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41Z" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
<div class="search-icon @(Enum.GetName(Icon)?.ToLower())" @onclick=HandleSearchClicked>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||||
|
@if (Icon == SearchInputIcon.Search)
|
||||||
|
{
|
||||||
|
<path d="M9.5,3A6.5,6.5 0 0,1 16,9.5C16,11.11 15.41,12.59 14.44,13.73L14.71,14H15.5L20.5,19L19,20.5L14,15.5V14.71L13.73,14.44C12.59,15.41 11.11,16 9.5,16A6.5,6.5 0 0,1 3,9.5A6.5,6.5 0 0,1 9.5,3M9.5,5C7,5 5,7 5,9.5C5,12 7,14 9.5,14C12,14 14,12 14,9.5C14,7 12,5 9.5,5Z" />
|
||||||
|
}
|
||||||
|
else if (Icon == SearchInputIcon.Dropdown)
|
||||||
|
{
|
||||||
|
<path d="M1 3H23L12 22" />
|
||||||
|
}
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
|
||||||
|
namespace GameIdeas.BlazorApp.Shared.Components.Search;
|
||||||
|
|
||||||
|
public partial class SearchInput
|
||||||
|
{
|
||||||
|
[Parameter] public string? Text { get; set; }
|
||||||
|
[Parameter] public EventCallback<string> TextChanged { get; set; }
|
||||||
|
[Parameter] public EventCallback ClearClicked { get; set; }
|
||||||
|
[Parameter] public EventCallback SearchClicked { get; set; }
|
||||||
|
[Parameter] public SearchInputIcon Icon { get; set; }
|
||||||
|
|
||||||
|
protected override void OnInitialized()
|
||||||
|
{
|
||||||
|
Text = string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetText(string str)
|
||||||
|
{
|
||||||
|
Text = str;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task HandleTextChanged()
|
||||||
|
{
|
||||||
|
await TextChanged.InvokeAsync(Text);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task HandleClearClicked()
|
||||||
|
{
|
||||||
|
Text = string.Empty;
|
||||||
|
await TextChanged.InvokeAsync(string.Empty);
|
||||||
|
await ClearClicked.InvokeAsync();
|
||||||
|
}
|
||||||
|
private async Task HandleSearchClicked()
|
||||||
|
{
|
||||||
|
await TextChanged.InvokeAsync(Text);
|
||||||
|
await SearchClicked.InvokeAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
.search-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
gap: 4px;
|
||||||
|
height: 28px;
|
||||||
|
padding-left: 8px;
|
||||||
|
padding-right: 2px;
|
||||||
|
border-radius: var(--small-radius);
|
||||||
|
background: var(--black);
|
||||||
|
overflow: hidden;
|
||||||
|
align-items: center;
|
||||||
|
width: 140px;
|
||||||
|
}
|
||||||
|
|
||||||
|
::deep .search-field {
|
||||||
|
border: none !important;
|
||||||
|
outline: none !important;
|
||||||
|
background: none !important;
|
||||||
|
color: var(--white);
|
||||||
|
height: 100%;
|
||||||
|
padding: 0;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clear-icon {
|
||||||
|
min-height: 18px;
|
||||||
|
min-width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
width: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clear-icon:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clear-icon svg {
|
||||||
|
fill: var(--white);
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-icon {
|
||||||
|
min-height: 24px;
|
||||||
|
min-width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
width: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-icon:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-icon svg {
|
||||||
|
fill: var(--white);
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-icon.dropdown svg {
|
||||||
|
fill: var(--violet);
|
||||||
|
transform: scale(.8, .5);
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
namespace GameIdeas.BlazorApp.Shared.Components.Search;
|
||||||
|
|
||||||
|
public enum SearchInputIcon
|
||||||
|
{
|
||||||
|
Search,
|
||||||
|
Dropdown
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
@typeparam TItem
|
||||||
|
|
||||||
|
<div class="select-element @(Enum.GetName(Theme)?.ToLower())" @onclick=HandleItemClicked>
|
||||||
|
<div class="selected">
|
||||||
|
@if (Value != null && Value.IsSelected)
|
||||||
|
{
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||||
|
<path d="M9,20.42L2.79,14.21L5.62,11.38L9,14.77L18.88,4.88L21.71,7.71L9,20.42Z" />
|
||||||
|
</svg>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
<div class="select-label">
|
||||||
|
@Value?.Label
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
using GameIdeas.BlazorApp.Shared.Components.Select.Models;
|
||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
|
||||||
|
namespace GameIdeas.BlazorApp.Shared.Components.Select.Components;
|
||||||
|
|
||||||
|
public partial class SelectListElement<TItem>
|
||||||
|
{
|
||||||
|
[Parameter] public EventCallback<SelectElement<TItem>?> ValueChanged { get; set; }
|
||||||
|
[Parameter] public SelectElement<TItem>? Value { get; set; }
|
||||||
|
[Parameter] public SelectListTheme Theme { get; set; }
|
||||||
|
private async Task HandleItemClicked()
|
||||||
|
{
|
||||||
|
if (Value == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Value.IsSelected = true;
|
||||||
|
StateHasChanged();
|
||||||
|
await ValueChanged.InvokeAsync(Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
.select-element {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
width: fit-content;
|
||||||
|
width: 100%;
|
||||||
|
gap: 6px;
|
||||||
|
height: 20px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select-element:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selected {
|
||||||
|
width: 12px;
|
||||||
|
min-width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
min-height: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selected svg {
|
||||||
|
display: block;
|
||||||
|
fill: var(--white)
|
||||||
|
}
|
||||||
|
|
||||||
|
/***** Navigation Theme *****/
|
||||||
|
.navigation {
|
||||||
|
padding: 4px 8px;
|
||||||
|
}
|
||||||
|
.navigation:hover {
|
||||||
|
background: var(--violet-selected);
|
||||||
|
}
|
||||||
|
|
||||||
|
.navigation .selected {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***** Sort Theme *****/
|
||||||
|
.sort {
|
||||||
|
padding: 2px 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sort:hover {
|
||||||
|
background: var(--low-white);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sort .select-label {
|
||||||
|
text-wrap: nowrap;
|
||||||
|
margin-right: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***** Filter Theme *****/
|
||||||
|
.filter {
|
||||||
|
padding: 2px 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter:hover {
|
||||||
|
background: var(--low-white);
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter .select-label {
|
||||||
|
text-wrap: nowrap;
|
||||||
|
margin-right: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
namespace GameIdeas.BlazorApp.Shared.Components.Select.Models;
|
||||||
|
|
||||||
|
public class SelectElement<TItem>
|
||||||
|
{
|
||||||
|
public TItem? Item { get; set; }
|
||||||
|
public string? Label { get; set; }
|
||||||
|
public bool IsSelected { get; set; } = false;
|
||||||
|
public bool IsNew { get; set; } = false;
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
namespace GameIdeas.BlazorApp.Shared.Components.Select.Models;
|
||||||
|
|
||||||
|
public enum SelectListTheme
|
||||||
|
{
|
||||||
|
Navigation,
|
||||||
|
Sort,
|
||||||
|
Filter
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
@using GameIdeas.BlazorApp.Shared.Components.Search
|
||||||
|
@using GameIdeas.BlazorApp.Shared.Components.Select.Components
|
||||||
|
@typeparam TItem
|
||||||
|
|
||||||
|
<div class="select-list" tabindex="1001" @ref="BaseElement">
|
||||||
|
<div class="select-button" @onfocusin=HandleFocusIn @onfocusout=HandleFocusOut>
|
||||||
|
<SearchInput @ref=SearchInput
|
||||||
|
Icon="SearchInputIcon.Dropdown"
|
||||||
|
TextChanged="HandleTextChanged"
|
||||||
|
ClearClicked="HandleTextChanged"
|
||||||
|
SearchClicked="HandleSearchClicked" />
|
||||||
|
</div>
|
||||||
|
<div class="select-container @(AlignRight ? "align-right" : "")"
|
||||||
|
tabindex="1000"
|
||||||
|
@ref=ContentElement
|
||||||
|
@onfocusin=HandleContentFocusIn
|
||||||
|
@onfocusout=HandleContentFocusOut>
|
||||||
|
|
||||||
|
@if (IsContentOpen)
|
||||||
|
{
|
||||||
|
<div class="select-content @(Enum.GetName(Theme)?.ToLower())">
|
||||||
|
@foreach (var item in Items)
|
||||||
|
{
|
||||||
|
<SelectListElement TItem="TItem"
|
||||||
|
Value="item"
|
||||||
|
ValueChanged="HandleItemClicked"
|
||||||
|
Theme="Theme" />
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
using GameIdeas.BlazorApp.Shared.Components.Search;
|
||||||
|
using GameIdeas.BlazorApp.Shared.Components.Select.Models;
|
||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
|
||||||
|
namespace GameIdeas.BlazorApp.Shared.Components.Select;
|
||||||
|
|
||||||
|
public partial class MultipleSelectList<TItem>
|
||||||
|
{
|
||||||
|
[Parameter] public IEnumerable<TItem>? Values { get; set; }
|
||||||
|
[Parameter] public EventCallback<IEnumerable<TItem>?> ValuesChanged { get; set; }
|
||||||
|
[Parameter] public IEnumerable<SelectElement<TItem>> Items { get; set; } = [];
|
||||||
|
[Parameter] public SelectListTheme Theme { get; set; }
|
||||||
|
[Parameter] public bool AlignRight { get; set; }
|
||||||
|
|
||||||
|
private bool IsContentOpen
|
||||||
|
{
|
||||||
|
get => InputFocus || ContentFocus;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool InputFocus = false;
|
||||||
|
private bool ContentFocus = false;
|
||||||
|
private SearchInput? SearchInput;
|
||||||
|
private ElementReference ContentElement;
|
||||||
|
private ElementReference BaseElement;
|
||||||
|
|
||||||
|
private async Task HandleItemClicked(SelectElement<TItem> selectedValue)
|
||||||
|
{
|
||||||
|
Values = Items.Where(x => x.IsSelected && x.Item != null).Select(x => x.Item!);
|
||||||
|
SearchInput?.SetText(string.Join(", ", Values));
|
||||||
|
await ValuesChanged.InvokeAsync(Values);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task HandleTextChanged()
|
||||||
|
{
|
||||||
|
await BaseElement.FocusAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HandleFocusIn()
|
||||||
|
{
|
||||||
|
InputFocus = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HandleContentFocusIn()
|
||||||
|
{
|
||||||
|
ContentFocus = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task HandleContentFocusOut()
|
||||||
|
{
|
||||||
|
await Task.Delay(TimeSpan.FromSeconds(0.3));
|
||||||
|
ContentFocus = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task HandleFocusOut()
|
||||||
|
{
|
||||||
|
await Task.Delay(TimeSpan.FromSeconds(0.3));
|
||||||
|
InputFocus = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task HandleSearchClicked()
|
||||||
|
{
|
||||||
|
if (!IsContentOpen)
|
||||||
|
{
|
||||||
|
await ContentElement.FocusAsync();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await BaseElement.FocusAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
.select-list {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select-container {
|
||||||
|
margin-top: 4px;
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
.align-right {
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select-content {
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
border-radius: var(--small-radius);
|
||||||
|
animation-name: fade-in;
|
||||||
|
animation-duration: 0.4s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.line {
|
||||||
|
margin: 2px 6px;
|
||||||
|
border-bottom: 2px solid var(--low-white);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/***** Navigation Theme *****/
|
||||||
|
.select-content.navigation {
|
||||||
|
background: var(--violet);
|
||||||
|
box-shadow: var(--drop-shadow);
|
||||||
|
}
|
||||||
|
|
||||||
|
/***** Sort Theme *****/
|
||||||
|
.select-content.sort {
|
||||||
|
background: var(--semi-black);
|
||||||
|
box-shadow: var(--drop-shadow);
|
||||||
|
padding: 4px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***** Filter Theme *****/
|
||||||
|
.select-content.filter {
|
||||||
|
background: var(--semi-black);
|
||||||
|
box-shadow: var(--drop-shadow);
|
||||||
|
padding: 4px 0;
|
||||||
|
min-width: 150px;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
@using GameIdeas.BlazorApp.Shared.Components.Select.Components
|
||||||
|
@typeparam TItem
|
||||||
|
|
||||||
|
<div class="select-list" @onclick=HandleButtonClicked>
|
||||||
|
<div class="select-button">
|
||||||
|
@Button
|
||||||
|
</div>
|
||||||
|
<div @ref=Container @onfocusout=HandleFocusOut
|
||||||
|
class="select-container @(AlignRight ? "align-right" : "")"
|
||||||
|
tabindex="1000">
|
||||||
|
|
||||||
|
@if (ContentVisile)
|
||||||
|
{
|
||||||
|
<div class="select-content @(Enum.GetName(Theme)?.ToLower())">
|
||||||
|
@foreach (var item in Headers)
|
||||||
|
{
|
||||||
|
<SelectListElement TItem="TItem"
|
||||||
|
Value="item"
|
||||||
|
ValueChanged="HandleHeaderClicked"
|
||||||
|
Theme="Theme" />
|
||||||
|
}
|
||||||
|
@if (Headers.Any())
|
||||||
|
{
|
||||||
|
<span class="line"></span>
|
||||||
|
}
|
||||||
|
@foreach (var item in Items)
|
||||||
|
{
|
||||||
|
<SelectListElement TItem="TItem"
|
||||||
|
Value="item"
|
||||||
|
ValueChanged="HandleItemClicked"
|
||||||
|
Theme="Theme" />
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
using GameIdeas.BlazorApp.Shared.Components.Select.Models;
|
||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
|
||||||
|
namespace GameIdeas.BlazorApp.Shared.Components.Select;
|
||||||
|
|
||||||
|
public partial class SelectList<TItem>
|
||||||
|
{
|
||||||
|
[Parameter] public RenderFragment? Button { get; set; }
|
||||||
|
[Parameter] public TItem? Value { get; set; }
|
||||||
|
[Parameter] public EventCallback<TItem?> ValueChanged { get; set; }
|
||||||
|
[Parameter] public TItem? Header { get; set; }
|
||||||
|
[Parameter] public EventCallback<TItem?> HeaderChanged { get; set; }
|
||||||
|
[Parameter] public IEnumerable<SelectElement<TItem>> Items { get; set; } = [];
|
||||||
|
[Parameter] public IEnumerable<SelectElement<TItem>> Headers { get; set; } = [];
|
||||||
|
[Parameter] public SelectListTheme Theme { get; set; }
|
||||||
|
[Parameter] public bool AlignRight { get; set; }
|
||||||
|
|
||||||
|
private bool ContentVisile = false;
|
||||||
|
private DateTime ContentLastFocusOut = DateTime.Now;
|
||||||
|
private ElementReference Container;
|
||||||
|
|
||||||
|
public async Task OpenAsync()
|
||||||
|
{
|
||||||
|
if (DateTime.Now - ContentLastFocusOut >= TimeSpan.FromSeconds(0.2))
|
||||||
|
{
|
||||||
|
await Container.FocusAsync();
|
||||||
|
ContentVisile = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Close() => ContentVisile = false;
|
||||||
|
|
||||||
|
private async Task HandleButtonClicked() => await OpenAsync();
|
||||||
|
|
||||||
|
private void HandleFocusOut()
|
||||||
|
{
|
||||||
|
ContentLastFocusOut = DateTime.Now;
|
||||||
|
ContentVisile = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task HandleItemClicked(SelectElement<TItem> selectedValue)
|
||||||
|
{
|
||||||
|
foreach (var item in Items)
|
||||||
|
{
|
||||||
|
item.IsSelected = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
selectedValue.IsSelected = true;
|
||||||
|
Value = selectedValue.Item;
|
||||||
|
await ValueChanged.InvokeAsync(Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task HandleHeaderClicked(SelectElement<TItem> selectedValue)
|
||||||
|
{
|
||||||
|
foreach (var header in Headers)
|
||||||
|
{
|
||||||
|
header.IsSelected = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
selectedValue.IsSelected = true;
|
||||||
|
Header = selectedValue.Item;
|
||||||
|
await HeaderChanged.InvokeAsync(Header);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
.select-list {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select-container {
|
||||||
|
margin-top: 4px;
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
.align-right {
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select-content {
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
border-radius: var(--small-radius);
|
||||||
|
animation-name: fade-in;
|
||||||
|
animation-duration: 0.4s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.line {
|
||||||
|
margin: 2px 6px;
|
||||||
|
border-bottom: 2px solid var(--low-white);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/***** Navigation Theme *****/
|
||||||
|
.select-content.navigation {
|
||||||
|
background: var(--violet);
|
||||||
|
box-shadow: var(--drop-shadow);
|
||||||
|
}
|
||||||
|
|
||||||
|
/***** Sort Theme *****/
|
||||||
|
.select-content.sort {
|
||||||
|
background: var(--semi-black);
|
||||||
|
box-shadow: var(--drop-shadow);
|
||||||
|
padding: 4px 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
<div class="container">
|
||||||
|
<div class="slider-track" style="@FillColor()"></div>
|
||||||
|
|
||||||
|
<input type="range" id="min-range" style="@StatusColor(Params.ValueMin)" min="@Params.Min" max="@Params.Max"
|
||||||
|
@bind="@Params.ValueMin" @bind:event="oninput" @bind:after=HandleSlideOnInput />
|
||||||
|
<input type="range" id="max-range" style="@StatusColor(Params.ValueMax)" min="@Params.Min" max="@Params.Max"
|
||||||
|
@bind="@Params.ValueMax" @bind:event="oninput" @bind:after=HandleSlideTwoInput />
|
||||||
|
|
||||||
|
<div class="values">
|
||||||
|
<span class="value">@Params.ValueMin</span>
|
||||||
|
<span class="value">@Params.ValueMax</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace GameIdeas.BlazorApp.Shared.Components.SliderRange;
|
||||||
|
|
||||||
|
public partial class SliderRange
|
||||||
|
{
|
||||||
|
[Parameter] public SliderRangeParams Params { get; set; } = new();
|
||||||
|
[Parameter] public int Max { get; set; }
|
||||||
|
[Parameter] public EventCallback<int> MaxChanged { get; set; }
|
||||||
|
[Parameter] public int Min { get; set; }
|
||||||
|
[Parameter] public EventCallback<int> MinChanged { get; set; }
|
||||||
|
|
||||||
|
private async Task HandleSlideTwoInput()
|
||||||
|
{
|
||||||
|
if (Params.ValueMax - Params.ValueMin <= Params.Gap)
|
||||||
|
{
|
||||||
|
Params.ValueMin = Params.ValueMax - Params.Gap;
|
||||||
|
}
|
||||||
|
|
||||||
|
Max = Params.Max;
|
||||||
|
await MaxChanged.InvokeAsync(Params.Max);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task HandleSlideOnInput()
|
||||||
|
{
|
||||||
|
if (Params.ValueMax - Params.ValueMin <= Params.Gap)
|
||||||
|
{
|
||||||
|
Params.ValueMax = Params.ValueMin + Params.Gap;
|
||||||
|
}
|
||||||
|
|
||||||
|
Min = Params.Min;
|
||||||
|
await MinChanged.InvokeAsync(Params.Min);
|
||||||
|
}
|
||||||
|
|
||||||
|
private string FillColor()
|
||||||
|
{
|
||||||
|
var percent1 = (double)(Params.ValueMin - Params.Min) / (Params.Max - Params.Min) * 100;
|
||||||
|
var percent2 = (double)(Params.ValueMax - Params.Min) / (Params.Max - Params.Min) * 100;
|
||||||
|
return $"background: linear-gradient(to right, var(--light-grey) {percent1}% , var(--violet) {percent1}% , var(--violet) {percent2}%, var(--light-grey) {percent2}%)";
|
||||||
|
}
|
||||||
|
|
||||||
|
private string StatusColor(int value)
|
||||||
|
{
|
||||||
|
string str = "--thumb-color: var({0});";
|
||||||
|
|
||||||
|
int firstTier = (int)Math.Floor(0.33 * Params.Max);
|
||||||
|
int secondTier = (int)Math.Ceiling(0.66 * Params.Max);
|
||||||
|
|
||||||
|
return value switch
|
||||||
|
{
|
||||||
|
int x when x <= firstTier => string.Format(str, "--red"),
|
||||||
|
int x when x >= secondTier => string.Format(str, "--green"),
|
||||||
|
_ => string.Format(str, "--yellow"),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
.container {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="range"] {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
-moz-appearance: none;
|
||||||
|
appearance: none;
|
||||||
|
width: 100%;
|
||||||
|
outline: none;
|
||||||
|
margin: auto;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background-color: transparent;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slider-track {
|
||||||
|
width: 100%;
|
||||||
|
height: 2px;
|
||||||
|
margin: auto;
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="range"]::-webkit-slider-runnable-track {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
height: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="range"]::-moz-range-track {
|
||||||
|
-moz-appearance: none;
|
||||||
|
height: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="range"]::-ms-track {
|
||||||
|
appearance: none;
|
||||||
|
height: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="range"]::-webkit-slider-thumb {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
height: 1em;
|
||||||
|
width: 1em;
|
||||||
|
background-color: var(--thumb-color);
|
||||||
|
cursor: pointer;
|
||||||
|
margin-top: -6px;
|
||||||
|
pointer-events: auto;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="range"]::-moz-range-thumb {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
height: 1em;
|
||||||
|
width: 1em;
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: var(--thumb-color);
|
||||||
|
pointer-events: auto;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="range"]::-ms-thumb {
|
||||||
|
appearance: none;
|
||||||
|
height: 1em;
|
||||||
|
width: 1em;
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: var(--thumb-color);
|
||||||
|
pointer-events: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.values {
|
||||||
|
display: flex;
|
||||||
|
position: absolute;
|
||||||
|
margin-top: 10px;
|
||||||
|
width: 100%;
|
||||||
|
font-weight: bold;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.value {
|
||||||
|
width: 1em;
|
||||||
|
text-align:center;
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
namespace GameIdeas.BlazorApp.Shared.Components.SliderRange;
|
||||||
|
|
||||||
|
public class SliderRangeParams
|
||||||
|
{
|
||||||
|
public int Min{ get; set; }
|
||||||
|
public int Max { get; set; }
|
||||||
|
public int ValueMin { get; set; }
|
||||||
|
public int ValueMax { get; set; }
|
||||||
|
public int Gap { get; set; } = 0;
|
||||||
|
}
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
using GameIdeas.BlazorApp.Pages.Games.Models;
|
|
||||||
using GameIdeas.BlazorApp.Shared.Components.Dropdown;
|
|
||||||
using GameIdeas.Resources;
|
|
||||||
using Microsoft.AspNetCore.Components;
|
|
||||||
|
|
||||||
namespace GameIdeas.BlazorApp.Shared.Headers;
|
|
||||||
|
|
||||||
public partial class HeaderBase
|
|
||||||
{
|
|
||||||
[Parameter] public RenderFragment? Body { get; set; }
|
|
||||||
[Parameter] public EventCallback<AddType> AddTypeChanged { get; set; }
|
|
||||||
|
|
||||||
private DropdownContent<AddTypeParams>? DropdownAdd;
|
|
||||||
private readonly IEnumerable<AddTypeParams> AddTypes =
|
|
||||||
[
|
|
||||||
new AddTypeParams(AddType.Manual, ResourcesKey.ManualAdd),
|
|
||||||
new AddTypeParams(AddType.Auto, ResourcesKey.AutoAdd)
|
|
||||||
];
|
|
||||||
|
|
||||||
private AccountSettings? AccountSettings;
|
|
||||||
|
|
||||||
private void HandleIconClicked()
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task HandleMoreButton()
|
|
||||||
{
|
|
||||||
if (DropdownAdd != null)
|
|
||||||
{
|
|
||||||
await DropdownAdd.OpenAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
AccountSettings?.Close();
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task HandleAddTypeClickedAsync(AddTypeParams value)
|
|
||||||
{
|
|
||||||
await AddTypeChanged.InvokeAsync(value.AddType);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void HandleAccountClicked()
|
|
||||||
{
|
|
||||||
AccountSettings?.Toggle();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +1,15 @@
|
|||||||
@using GameIdeas.BlazorApp.Pages.Games
|
@using GameIdeas.BlazorApp.Pages.Games
|
||||||
@using GameIdeas.BlazorApp.Pages.Games.Models
|
@using GameIdeas.BlazorApp.Pages.Games.Models
|
||||||
@using GameIdeas.BlazorApp.Shared.Components.Dropdown
|
@using GameIdeas.BlazorApp.Shared.Components.Account
|
||||||
|
@using GameIdeas.BlazorApp.Shared.Components.Select
|
||||||
|
@using GameIdeas.BlazorApp.Shared.Components.Select.Models
|
||||||
@using GameIdeas.Resources
|
@using GameIdeas.Resources
|
||||||
|
|
||||||
|
@inherits LayoutComponentBase
|
||||||
|
|
||||||
<div class="header-tab">
|
<div class="header-tab">
|
||||||
<div class="icon-container" @onclick="HandleIconClicked">
|
<div class="icon-container" @onclick="HandleIconClicked">
|
||||||
<img src="icon.png" alt="Game Ideas">
|
<img src="icon.png" alt="Game Ideas">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@Body
|
@Body
|
||||||
@@ -18,21 +22,23 @@
|
|||||||
<path d="M19,13H13V19H11V13H5V11H11V5H13V11H19V13Z" />
|
<path d="M19,13H13V19H11V13H5V11H11V5H13V11H19V13Z" />
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<div class="second-button button" @onclick=HandleMoreButton>
|
<SelectList TItem="AddType"
|
||||||
<svg class="button-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
Items="SelectElements"
|
||||||
<path d="M1 3H23L12 22" />
|
ValueChanged=HandleAddTypeClickedAsync
|
||||||
</svg>
|
Theme="SelectListTheme.Navigation"
|
||||||
</div>
|
AlignRight=true>
|
||||||
|
<Button>
|
||||||
|
<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" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</Button>
|
||||||
|
</SelectList>
|
||||||
</div>
|
</div>
|
||||||
<DropdownContent @ref="DropdownAdd"
|
|
||||||
TItem="AddTypeParams"
|
|
||||||
ValueChanged=HandleAddTypeClickedAsync
|
|
||||||
Items="AddTypes"
|
|
||||||
LabelSelector="(addtype => addtype.Label)"
|
|
||||||
Theme="DropdownTheme.Navigation" />
|
|
||||||
</div>
|
</div>
|
||||||
<div class="account-container">
|
<div class="account-container">
|
||||||
<div class="icon-container">
|
<div class="icon-container" @onclick=HandleAccountClicked>
|
||||||
<svg class="account-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
<svg class="account-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||||
<path d="M12,19.2C9.5,19.2 7.29,17.92 6,16C6.03,14 10,12.9 12,12.9C14,12.9 17.97,14 18,16C16.71,17.92 14.5,19.2 12,19.2M12,5A3,3 0 0,1 15,8A3,3 0 0,1 12,11A3,3 0 0,1 9,8A3,3 0 0,1 12,5M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12C22,6.47 17.5,2 12,2Z" />
|
<path d="M12,19.2C9.5,19.2 7.29,17.92 6,16C6.03,14 10,12.9 12,12.9C14,12.9 17.97,14 18,16C16.71,17.92 14.5,19.2 12,19.2M12,5A3,3 0 0,1 15,8A3,3 0 0,1 12,11A3,3 0 0,1 9,8A3,3 0 0,1 12,5M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12C22,6.47 17.5,2 12,2Z" />
|
||||||
</svg>
|
</svg>
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
using GameIdeas.BlazorApp.Pages.Games.Models;
|
||||||
|
using GameIdeas.BlazorApp.Shared.Components.Account;
|
||||||
|
using GameIdeas.BlazorApp.Shared.Components.Select.Models;
|
||||||
|
using GameIdeas.Resources;
|
||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
|
||||||
|
namespace GameIdeas.BlazorApp.Shared.Layouts.Header;
|
||||||
|
|
||||||
|
public partial class HeaderLayout : LayoutComponentBase
|
||||||
|
{
|
||||||
|
[Parameter] public EventCallback<AddType> AddTypeChanged { get; set; }
|
||||||
|
|
||||||
|
private readonly IEnumerable<SelectElement<AddType>> SelectElements = [
|
||||||
|
new SelectElement<AddType> { Item = AddType.Manual, Label = ResourcesKey.ManualAdd },
|
||||||
|
new SelectElement<AddType> { Item = AddType.Auto, Label = ResourcesKey.AutoAdd }
|
||||||
|
];
|
||||||
|
|
||||||
|
private AccountSettings? AccountSettings;
|
||||||
|
|
||||||
|
private void HandleIconClicked()
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task HandleAddTypeClickedAsync(AddType value)
|
||||||
|
{
|
||||||
|
await AddTypeChanged.InvokeAsync(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HandleAccountClicked()
|
||||||
|
{
|
||||||
|
AccountSettings?.Toggle();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -42,7 +42,7 @@
|
|||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
background: var(--violet);
|
background: var(--violet);
|
||||||
border-radius: var(--small-radius);
|
border-radius: var(--small-radius);
|
||||||
overflow: hidden;
|
overflow: visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
.button {
|
.button {
|
||||||
@@ -8,8 +8,9 @@
|
|||||||
--yellow: #FFC107;
|
--yellow: #FFC107;
|
||||||
--green: #43F8C0;
|
--green: #43F8C0;
|
||||||
--light-grey: #5C5C5E;
|
--light-grey: #5C5C5E;
|
||||||
--black: rgba(44, 43, 46, 0.8);
|
--black: rgba(0, 0, 0, 0.8);
|
||||||
--white: #fff;
|
--white: #fff;
|
||||||
|
--low-white: rgb(255, 255, 255, 0.1);
|
||||||
--semi-black: rgba(0, 0, 0, 0.5);
|
--semi-black: rgba(0, 0, 0, 0.5);
|
||||||
--line-black: rgba(0, 0, 0, 0.2);
|
--line-black: rgba(0, 0, 0, 0.2);
|
||||||
--small-radius: 4px;
|
--small-radius: 4px;
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ public class Translations (TranslationService translationService)
|
|||||||
public string Logout => translationService.Translate(nameof(Logout));
|
public string Logout => translationService.Translate(nameof(Logout));
|
||||||
public string EnterUsername => translationService.Translate(nameof(EnterUsername));
|
public string EnterUsername => translationService.Translate(nameof(EnterUsername));
|
||||||
public string EnterPassword => translationService.Translate(nameof(EnterPassword));
|
public string EnterPassword => translationService.Translate(nameof(EnterPassword));
|
||||||
|
public string UserManager => translationService.Translate(nameof(UserManager));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class ResourcesKey
|
public static class ResourcesKey
|
||||||
@@ -28,4 +29,5 @@ public static class ResourcesKey
|
|||||||
public static string Logout => _instance?.Logout ?? throw new InvalidOperationException("ResourcesKey.Logout is not initialized.");
|
public static string Logout => _instance?.Logout ?? throw new InvalidOperationException("ResourcesKey.Logout is not initialized.");
|
||||||
public static string EnterUsername => _instance?.EnterUsername ?? throw new InvalidOperationException("ResourcesKey.EnterUsername is not initialized.");
|
public static string EnterUsername => _instance?.EnterUsername ?? throw new InvalidOperationException("ResourcesKey.EnterUsername is not initialized.");
|
||||||
public static string EnterPassword => _instance?.EnterPassword ?? throw new InvalidOperationException("ResourcesKey.EnterPassword is not initialized.");
|
public static string EnterPassword => _instance?.EnterPassword ?? throw new InvalidOperationException("ResourcesKey.EnterPassword is not initialized.");
|
||||||
|
public static string UserManager => _instance?.UserManager ?? throw new InvalidOperationException("ResourcesKey.UserManager is not initialized.");
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
namespace GameIdeas.Shared.Constants;
|
||||||
|
|
||||||
|
public class GlobalConstants
|
||||||
|
{
|
||||||
|
public const string EnterKeyCode = "Enter";
|
||||||
|
public const string PadEnterKeyCode = "NumpadEnter";
|
||||||
|
}
|
||||||
7
src/GameIdeas/GameIdeas.Shared/Dto/GameDto.cs
Normal file
7
src/GameIdeas/GameIdeas.Shared/Dto/GameDto.cs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
namespace GameIdeas.Shared.Dto;
|
||||||
|
|
||||||
|
public class GameDto
|
||||||
|
{
|
||||||
|
public string? Name { get; set; }
|
||||||
|
public string? ReleaseDate { get; set; }
|
||||||
|
}
|
||||||
7
src/GameIdeas/GameIdeas.Shared/Enum/SortType.cs
Normal file
7
src/GameIdeas/GameIdeas.Shared/Enum/SortType.cs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
namespace GameIdeas.Shared.Enum;
|
||||||
|
|
||||||
|
public enum SortType
|
||||||
|
{
|
||||||
|
Ascending,
|
||||||
|
Descending
|
||||||
|
}
|
||||||
@@ -5,5 +5,6 @@
|
|||||||
"Login": "Se connecter",
|
"Login": "Se connecter",
|
||||||
"Logout": "Se déconnecter",
|
"Logout": "Se déconnecter",
|
||||||
"EnterUsername": "Nom d'utilisateur",
|
"EnterUsername": "Nom d'utilisateur",
|
||||||
"EnterPassword": "Mot de passe"
|
"EnterPassword": "Mot de passe",
|
||||||
|
"UserManager": "Gestion des utilisateurs"
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user