From fcce4555ebe1e708eeb5e00792f40c2dc098edd7 Mon Sep 17 00:00:00 2001 From: Benjamin Holbrook Date: Mon, 9 Oct 2023 01:35:46 +1100 Subject: [PATCH 01/36] Added HxMultiSelect filtering --- BlazorAppTest/Pages/HxMultiSelectTest.razor | 71 ++++-- .../Havit.Blazor.Components.Web.Bootstrap.xml | 54 ++++- .../Forms/HxMultiSelect.cs | 30 ++- .../Internal/HxMultiSelectInternal.razor | 30 +-- .../Internal/HxMultiSelectInternal.razor.cs | 221 +++++++++++++++--- .../wwwroot/HxMultiSelect.js | 50 ++++ 6 files changed, 373 insertions(+), 83 deletions(-) create mode 100644 Havit.Blazor.Components.Web.Bootstrap/wwwroot/HxMultiSelect.js diff --git a/BlazorAppTest/Pages/HxMultiSelectTest.razor b/BlazorAppTest/Pages/HxMultiSelectTest.razor index 70a5645d5..c45e105d3 100644 --- a/BlazorAppTest/Pages/HxMultiSelectTest.razor +++ b/BlazorAppTest/Pages/HxMultiSelectTest.razor @@ -1,38 +1,61 @@ @page "/HxMultiSelectTest" @using System.Globalization -

HxCheckboxList

+

HxMultiSelect

+ +

Selected values: @String.Join(", ", model.CultureInfos ?? Enumerable.Empty())

+ - - -

Selected values: @String.Join(", ", model.CultureInfos ?? Enumerable.Empty())

+ + + + + @code { - private bool enabled = true; - private Model model = new Model(); - private List data; - - protected override async Task OnParametersSetAsync() - { - await base.OnParametersSetAsync(); - await Task.Delay(3000); - - data = CultureInfo.GetCultures(CultureTypes.SpecificCultures) - .OrderBy(item => item.EnglishName) - .Take(100) - .OrderByDescending(i => i.ToString()) // sorting test - .ToList(); - } - - private class Model - { - public List CultureInfos { get; set; } - } + private bool enabled = true; + private Model model = new Model(); + private List data; + + private HxMultiSelect hxMultiSelectFilter; + + protected override async Task OnParametersSetAsync() + { + await base.OnParametersSetAsync(); + await Task.Delay(3000); + + data = CultureInfo.GetCultures(CultureTypes.SpecificCultures) + .OrderBy(item => item.EnglishName) + .Take(100) + .OrderByDescending(i => i.ToString()) // sorting test + .ToList(); + } + + private class Model + { + public List CultureInfos { get; set; } + } + + private bool FilterSelector(CultureInfo item, string filter) + { + return string.IsNullOrWhiteSpace(filter) || item.EnglishName.Contains(filter, StringComparison.OrdinalIgnoreCase); + } } \ No newline at end of file diff --git a/Havit.Blazor.Components.Web.Bootstrap.Documentation/XmlDoc/Havit.Blazor.Components.Web.Bootstrap.xml b/Havit.Blazor.Components.Web.Bootstrap.Documentation/XmlDoc/Havit.Blazor.Components.Web.Bootstrap.xml index 6e31b54ab..50206bac4 100644 --- a/Havit.Blazor.Components.Web.Bootstrap.Documentation/XmlDoc/Havit.Blazor.Components.Web.Bootstrap.xml +++ b/Havit.Blazor.Components.Web.Bootstrap.Documentation/XmlDoc/Havit.Blazor.Components.Web.Bootstrap.xml @@ -2701,14 +2701,24 @@ + + + Additional attributes to be splatted onto an underlying HTML element. + + Custom CSS class to render with input-group span. - + - Input-group at the beginning of the input. + Input-group at the end of the input. + + + + + Input-group at the end of the input. @@ -2716,21 +2726,49 @@ Input-group at the beginning of the input. - + - Input-group at the end of the input. + Input-group at the beginning of the input. - + - Input-group at the end of the input. + This event is fired when a dropdown element has been hidden from the user (will wait for CSS transitions to complete). - + - Additional attributes to be splatted onto an underlying HTML element. + This event is fired when a dropdown element has been made visible to the user (will wait for CSS transitions to complete). + + + + + Receives notification from JavaScript when item is hidden. + + + Collapses the item. + + + + + Expands the item. + + + + + Triggers the event. Allows interception of the event in derived components. + + + + + Triggers the event. Allows interception of the event in derived components. + + + + + Represents properties (and methods) of a component rendering a form value (ie. form inputs). diff --git a/Havit.Blazor.Components.Web.Bootstrap/Forms/HxMultiSelect.cs b/Havit.Blazor.Components.Web.Bootstrap/Forms/HxMultiSelect.cs index 25d5cbc37..8ff8804ed 100644 --- a/Havit.Blazor.Components.Web.Bootstrap/Forms/HxMultiSelect.cs +++ b/Havit.Blazor.Components.Web.Bootstrap/Forms/HxMultiSelect.cs @@ -110,6 +110,11 @@ public class HxMultiSelect : HxInputBase>, IInputWit /// [Parameter] public RenderFragment InputGroupEndTemplate { get; set; } + [Parameter] public bool EnableFiltering { get; set; } + + [Parameter] public Func FilterSelector { get; set; } = (_, _) => true; + + private bool IsShown { get; set; } private List itemsToRender; private HxMultiSelectInternal hxMultiSelectInternalComponent; @@ -152,6 +157,16 @@ private void HandleItemSelectionChanged(bool @checked, TItem item) CurrentValue = newValue; // setter includes ValueChanged + NotifyFieldChanged } + private void HandleOnHiddenChanged() + { + IsShown = false; + } + + private void HandleOnShownChanged() + { + IsShown = true; + } + protected override bool TryParseValueFromString(string value, out List result, out string validationErrorMessage) { throw new NotSupportedException(); @@ -181,13 +196,17 @@ protected override void BuildRenderInput(RenderTreeBuilder builder) builder.AddAttribute(105, nameof(HxMultiSelectInternal.ItemsToRender), itemsToRender); builder.AddAttribute(106, nameof(HxMultiSelectInternal.TextSelector), TextSelector); builder.AddAttribute(107, nameof(HxMultiSelectInternal.ValueSelector), ValueSelector); - builder.AddAttribute(108, nameof(HxMultiSelectInternal.Value), Value); + builder.AddAttribute(108, nameof(HxMultiSelectInternal.SelectedValues), Value); builder.AddAttribute(109, nameof(HxMultiSelectInternal.NullDataText), NullDataText); builder.AddAttribute(110, nameof(HxMultiSelectInternal.ItemSelectionChanged), EventCallback.Factory.Create.SelectionChangedArgs>(this, args => HandleItemSelectionChanged(args.Checked, args.Item))); builder.AddAttribute(111, nameof(HxMultiSelectInternal.InputGroupStartText), InputGroupStartText); builder.AddAttribute(112, nameof(HxMultiSelectInternal.InputGroupStartTemplate), InputGroupStartTemplate); builder.AddAttribute(113, nameof(HxMultiSelectInternal.InputGroupEndText), InputGroupEndText); builder.AddAttribute(114, nameof(HxMultiSelectInternal.InputGroupEndTemplate), InputGroupEndTemplate); + builder.AddAttribute(115, nameof(HxMultiSelectInternal.EnableFiltering), EnableFiltering); + builder.AddAttribute(116, nameof(HxMultiSelectInternal.FilterSelector), FilterSelector); + builder.AddAttribute(117, nameof(HxMultiSelectInternal.OnHidden), EventCallback.Factory.Create(this, HandleOnHiddenChanged)); + builder.AddAttribute(118, nameof(HxMultiSelectInternal.OnShown), EventCallback.Factory.Create(this, HandleOnShownChanged)); builder.AddMultipleAttributes(200, this.AdditionalAttributes); @@ -202,7 +221,14 @@ private string GetInputText() { return InputText; } - else if ((InputTextSelector is null) || (Data is null) || (CurrentValue is null)) + + // If filtering is enabled and the dropdown is visible then we want to display the user-entered filter text instead of the selected values summary + if (EnableFiltering && IsShown) + { + return InputText; + } + + if ((InputTextSelector is null) || (Data is null) || (CurrentValue is null)) { return CurrentValueAsString; } diff --git a/Havit.Blazor.Components.Web.Bootstrap/Forms/Internal/HxMultiSelectInternal.razor b/Havit.Blazor.Components.Web.Bootstrap/Forms/Internal/HxMultiSelectInternal.razor index d34819069..a7f095fd5 100644 --- a/Havit.Blazor.Components.Web.Bootstrap/Forms/Internal/HxMultiSelectInternal.razor +++ b/Havit.Blazor.Components.Web.Bootstrap/Forms/Internal/HxMultiSelectInternal.razor @@ -6,24 +6,23 @@ bool enabled = EnabledEffective && (ItemsToRender != null); } -