Fix dropdown with backdrop filter

This commit is contained in:
2025-04-07 23:26:37 +02:00
parent 558416e7ad
commit de42286cd5
10 changed files with 119 additions and 51 deletions

View File

@@ -0,0 +1,55 @@
@if (IsVisible)
{
<div class="backdrop-filter @Color.ToString().ToLower()" @onclick="HandleBackdropClicked"></div>
}
@code {
[Inject] private IJSRuntime Js { get; set; } = default!;
[Parameter] public EventCallback OnClick { get; set; }
[Parameter] public bool AllowBodyScroll { get; set; }
[Parameter] public BackdropFilterColor Color { get; set; } = BackdropFilterColor.Overlay;
[Parameter] public bool CloseOnClick { get; set; } = true;
[Parameter] public bool IsVisible { get; set; }
public async Task Show()
{
IsVisible = true;
await HandleBodyOverflow();
}
public async Task Hide()
{
IsVisible = false;
await HandleBodyOverflow();
}
private async Task HandleBodyOverflow()
{
try
{
if (AllowBodyScroll) return;
if (IsVisible)
{
await Js.InvokeVoidAsync("setBodyOverflow", "hidden");
}
else
{
await Js.InvokeVoidAsync("setBodyOverflow", "auto");
}
}
catch (Exception)
{
// ignored because js not loaded
}
}
private async Task HandleBackdropClicked()
{
if (!CloseOnClick) return;
await Hide();
await OnClick.InvokeAsync();
}
}

View File

@@ -0,0 +1,13 @@
.backdrop-filter {
position: fixed;
inset: 0;
z-index: var(--index-backdrop);
}
.backdrop-filter.overlay {
background-color: var(--grey-filter);
}
.backdrop-filter.transparent {
background-color: transparent;
}

View File

@@ -0,0 +1,3 @@
window.setBodyOverflow = (overflow) => {
document.getElementsByTagName('html')[0].style.overflow = overflow;
}

View File

@@ -0,0 +1,7 @@
namespace GameIdeas.BlazorApp.Shared.Components.BackdropFilter;
public enum BackdropFilterColor
{
Overlay,
Transparent
}

View File

@@ -1,10 +1,12 @@
<div class="search-container"> <div class="search-container">
<input type="text" <input @ref=InputText
type="text"
class="search-field" class="search-field"
placeholder="@Placeholder" placeholder="@Placeholder"
@bind=@Text @bind=@Text
@bind:event="oninput" @bind:event="oninput"
@bind:after=HandleTextChanged /> @bind:after=HandleTextChanged
@onfocusin=HandleFocusIn/>
@if (!string.IsNullOrEmpty(Text)) @if (!string.IsNullOrEmpty(Text))
{ {

View File

@@ -9,8 +9,11 @@ public partial class SearchInput
[Parameter] public EventCallback<string> TextChanged { get; set; } [Parameter] public EventCallback<string> TextChanged { get; set; }
[Parameter] public EventCallback ClearClicked { get; set; } [Parameter] public EventCallback ClearClicked { get; set; }
[Parameter] public EventCallback SearchClicked { get; set; } [Parameter] public EventCallback SearchClicked { get; set; }
[Parameter] public EventCallback FocusIn { get; set; }
[Parameter] public SearchInputIcon Icon { get; set; } [Parameter] public SearchInputIcon Icon { get; set; }
private ElementReference InputText;
protected override void OnInitialized() protected override void OnInitialized()
{ {
Text = string.Empty; Text = string.Empty;
@@ -36,5 +39,10 @@ public partial class SearchInput
{ {
await TextChanged.InvokeAsync(Text); await TextChanged.InvokeAsync(Text);
await SearchClicked.InvokeAsync(); await SearchClicked.InvokeAsync();
await InputText.FocusAsync();
}
private async Task HandleFocusIn()
{
await FocusIn.InvokeAsync();
} }
} }

View File

@@ -1,21 +1,19 @@
@using GameIdeas.BlazorApp.Shared.Components.Search @using GameIdeas.BlazorApp.Shared.Components.BackdropFilter
@using GameIdeas.BlazorApp.Shared.Components.Search
@using GameIdeas.BlazorApp.Shared.Components.Select.Components @using GameIdeas.BlazorApp.Shared.Components.Select.Components
@typeparam TItem @typeparam TItem
<div class="select-list" tabindex="1001" @ref="BaseElement"> <div class="select-list">
<div class="select-button @(Enum.GetName(Theme)?.ToLower())" @onfocusin=HandleFocusIn @onfocusout=HandleFocusOut> <div class="select-button @(Enum.GetName(Theme)?.ToLower())">
<SearchInput @ref=SearchInput <SearchInput @ref=SearchInput
Icon="SearchInputIcon.Dropdown" Icon="SearchInputIcon.Dropdown"
Placeholder="@Placeholder" Placeholder="@Placeholder"
TextChanged="HandleTextChanged" TextChanged="HandleTextChanged"
ClearClicked="HandleTextChanged" ClearClicked="HandleTextChanged"
SearchClicked="HandleSearchClicked" /> SearchClicked="HandleSearchClicked"
FocusIn="HandleTextFocusIn"/>
</div> </div>
<div class="select-container @(AlignRight ? "align-right" : "")" <div class="select-container @(AlignRight ? "align-right" : "")">
tabindex="1000"
@ref=ContentElement
@onfocusin=HandleContentFocusIn
@onfocusout=HandleContentFocusOut>
@if (IsContentOpen) @if (IsContentOpen)
{ {
@@ -32,3 +30,6 @@
</div> </div>
</div> </div>
<BackdropFilter AllowBodyScroll=true CloseOnClick=true Color="BackdropFilterColor.Transparent"
IsVisible=IsContentOpen OnClick="HandleContentClosed" />

View File

@@ -13,60 +13,34 @@ public partial class MultipleSelectList<TItem>
[Parameter] public bool AlignRight { get; set; } [Parameter] public bool AlignRight { get; set; }
[Parameter] public string? Placeholder { get; set; } [Parameter] public string? Placeholder { get; set; }
private bool IsContentOpen private bool IsContentOpen = false;
{
get => InputFocus || ContentFocus;
}
private bool InputFocus = false;
private bool ContentFocus = false;
private SearchInput? SearchInput; private SearchInput? SearchInput;
private ElementReference ContentElement;
private ElementReference BaseElement;
private async Task HandleItemClicked(SelectElement<TItem> selectedValue) private async Task HandleItemClicked(SelectElement<TItem> selectedValue)
{ {
selectedValue.IsSelected = !selectedValue.IsSelected;
Values = Items.Where(x => x.IsSelected && x.Item != null).Select(x => x.Item!); Values = Items.Where(x => x.IsSelected && x.Item != null).Select(x => x.Item!);
SearchInput?.SetText(string.Join(", ", Values)); SearchInput?.SetText(string.Join(", ", Values));
await ValuesChanged.InvokeAsync(Values); await ValuesChanged.InvokeAsync(Values);
} }
private async Task HandleTextChanged() private void HandleTextChanged()
{ {
await BaseElement.FocusAsync(); IsContentOpen = false;
} }
private void HandleFocusIn() private void HandleSearchClicked()
{ {
InputFocus = true; IsContentOpen = !IsContentOpen;
} }
private void HandleContentClosed()
private void HandleContentFocusIn()
{ {
ContentFocus = true; IsContentOpen = false;
} }
private void HandleTextFocusIn()
private async Task HandleContentFocusOut()
{ {
await Task.Delay(TimeSpan.FromSeconds(0.3)); IsContentOpen = true;
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();
}
} }
} }

View File

@@ -47,5 +47,10 @@
height: 24px; height: 24px;
width: 210px; width: 210px;
border: 2px solid var(--input-selected); border: 2px solid var(--input-selected);
background: var(--input-secondary);
}
::deep .select-button.advancedfilter .search-container input::placeholder {
color: #ddd;
} }

View File

@@ -25,8 +25,8 @@
--index-layout: 100; --index-layout: 100;
--index-component: 300; --index-component: 300;
--index-floating: 500; --index-floating: 500;
--index-dropdown: 700; --index-backdrop: 700;
--index-backdrop: 900; --index-dropdown: 900;
--index-popup: 1000; --index-popup: 1000;
} }