Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[HxMultiSelect] filtering and select all #617

Merged
merged 37 commits into from
Oct 22, 2023
Merged
Show file tree
Hide file tree
Changes from 29 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
fcce455
Added HxMultiSelect filtering
Oct 8, 2023
64ff133
Moved filtering box into dropdown
Oct 10, 2023
ad7f2ef
Reverted styling changes
Oct 10, 2023
e725c0c
Changed filter text and auto-focusing
Oct 10, 2023
74e58a5
Added empty template and changed hide/show handlers
Oct 10, 2023
a659d9f
Added default filter predicate
Oct 10, 2023
8632672
Changed filtering enablement property
Oct 10, 2023
3d48a11
Added empty template
Oct 10, 2023
5612d24
Changed default filter to use IsNullOrEmpty
Oct 10, 2023
6769c73
Added select all
Oct 10, 2023
dfb9f91
Fixed default select all text value
Oct 10, 2023
c99868d
Removed unused code
Oct 18, 2023
ec53d47
Renamed function
Oct 18, 2023
3e04982
Added string localisation to select all default text
Oct 18, 2023
1466054
Added select all default text to resources
Oct 18, 2023
5e92a17
Removed the unused shouldToggle option
Oct 18, 2023
1093cc6
Changed default select all text to '-select all-'
Oct 18, 2023
6553958
Empty template now has a default template and text parameter. Fixed l…
Oct 18, 2023
55a4cec
Added AllowFiltering and AllowSelectAll as settings
Oct 18, 2023
1ce4766
Removed OnShown and OnHidden event callbacks
Oct 18, 2023
4b3d6a4
Changed default ClearFilterOnHide to true
Oct 18, 2023
e3fe12e
Added ClearFilterOnHide=false to test example
Oct 18, 2023
c0bdb48
Added documentation
Oct 18, 2023
1b8cf70
HxMultiSelect_Demo_TemplatedFiltering - project coding standards - op…
hakenr Oct 19, 2023
3794959
HxMultiSelect - FilterPredicate doc adjustment
hakenr Oct 19, 2023
48c6b59
Removed all SelectAllChanged event callbacks
Oct 19, 2023
c800ecb
Merge branch 'feature/HxMultiSelect-filtering' of https://github.com/…
Oct 19, 2023
a57f860
Added filter search and clear icons
Oct 19, 2023
159c1d2
Reworked select all item selection callbacks
Oct 20, 2023
b2de43b
Wrapped select all button in a li
Oct 20, 2023
e54ae00
Added li around empty filter result
Oct 20, 2023
bef368a
Added padding to filter input.
Oct 20, 2023
34e7033
Filter input now respects parent input size
Oct 20, 2023
5696023
HxMultiSelect - dispose fix
hakenr Oct 22, 2023
ab61f92
HxMultiSelect - doc & demos tuning
hakenr Oct 22, 2023
1144ed2
HxMultiSelect - code-cleanup
hakenr Oct 22, 2023
3ab65e2
HxMultiSelect - localizations
hakenr Oct 22, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 58 additions & 22 deletions BlazorAppTest/Pages/HxMultiSelectTest.razor
Original file line number Diff line number Diff line change
@@ -1,38 +1,74 @@
@page "/HxMultiSelectTest"
@using System.Globalization

<h1>HxCheckboxList</h1>
<h1>HxMultiSelect</h1>

<HxSwitch Text="Enabled" @bind-Value="@enabled" />

<EditForm Model="@model">
<HxMultiSelect TItem="CultureInfo" TValue="string" Label="Cultures" EmptyText="-- choose here --" TextSelector="@(item => item.EnglishName)" ValueSelector="@(item => item.EnglishName)" Data="@data" @bind-Value="@model.CultureInfos" NullDataText="Loading languages..." Enabled="@enabled" InputSize="InputSize.Small" />

<HxMultiSelect TItem="CultureInfo" TValue="string" Label="Cultures" EmptyText="-- choose here --" TextSelector="@(item => item.EnglishName)" ValueSelector="@(item => item.EnglishName)" Data="@data" @bind-Value="@model.CultureInfos" NullDataText="Loading languages..." Enabled="@enabled" />
<HxMultiSelect TItem="CultureInfo" TValue="string" Label="Cultures" EmptyText="-- choose here --" TextSelector="@(item => item.EnglishName)" ValueSelector="@(item => item.EnglishName)" Data="@data" @bind-Value="@model.CultureInfos" NullDataText="Loading languages..." Enabled="@enabled" InputSize="InputSize.Large" />

<HxMultiSelect TItem="CultureInfo" TValue="string" Label="Cultures" EmptyText="-- choose here --" TextSelector="@(item => item.EnglishName)" ValueSelector="@(item => item.EnglishName)" Data="@data" @bind-Value="@model.CultureInfos" NullDataText="Loading languages..." Enabled="@enabled" InputSize="InputSize.Large" />

<!-- Multi-select with default filtering and select all -->
<HxMultiSelect TItem="CultureInfo"
TValue="string"
Label="Cultures with default filtering and select all enabled"
EmptyText="-- choose here --"
TextSelector="@(item => item.EnglishName)"
ValueSelector="@(item => item.EnglishName)"
Data="@data"
@bind-Value="@model.CultureInfos"
NullDataText="Loading languages..."
AllowFiltering="true"
AllowSelectAll="true"
Enabled="@enabled" />

<!-- Multi-select with custom filtering and select all -->
<HxMultiSelect TItem="CultureInfo"
TValue="string"
Label="Cultures with custom filtering and select all enabled"
EmptyText="-- choose here --"
TextSelector="@(item => item.EnglishName)"
ValueSelector="@(item => item.EnglishName)"
Data="@data"
@bind-Value="@model.CultureInfos"
NullDataText="Loading languages..."
AllowFiltering="true"
AllowSelectAll="true"
SelectAllText="Select all cultures"
ClearFilterOnHide="false"
Enabled="@enabled">
<FilterEmptyResultTemplate>
<span class="p-2">Couldn't find any matching cultures</span>
</FilterEmptyResultTemplate>
</HxMultiSelect>
</EditForm>

<p>Selected values: @String.Join(", ", model.CultureInfos ?? Enumerable.Empty<string>())</p>

@code
{
private bool enabled = true;
private Model model = new Model();
private List<CultureInfo> 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<string> CultureInfos { get; set; }
}
private bool enabled = true;
private Model model = new Model();
private List<CultureInfo> 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<string> CultureInfos { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
@code {
private Task<GridDataProviderResult<CultureInfo>> GetGridData(GridDataProviderRequest<CultureInfo> request)
{
// you usualy pass the data-request to your API/DataLayer and it returns just the few requested items (+ total count)
// you usually pass the data-request to your API/DataLayer and it returns just the few requested items (+ total count)

var cultureInfos = CultureInfo.GetCultures(CultureTypes.AllCultures).ToList();
cultureInfos.Sort(request.Sorting.ToGenericPropertyComparer()); // Just a demo. NEVER use in production code!
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<HxMultiSelect Label="Band members"
TItem="Person"
TValue="string"
Data="@people"
@bind-Value="@selectedPersonsInitials"
TextSelector="@(p => p.Name)"
ValueSelector="@(p => p.Initials)"
NullDataText="Loading band members..."
AllowFiltering="true"
FilterEmptyResultText="No results were found" />

<p class="mt-3">Selected initials: @String.Join(' ', selectedPersonsInitials)</p>

@code {
private List<Person> people;
private List<string> selectedPersonsInitials { get; set; } = new();

protected override async Task OnInitializedAsync()
{
await Task.Delay(1000); // simulates slow server API call

people = new List<Person>
{
new Person("Starr Ringo", "RS"),
new Person("Lennon John", "JL"),
new Person("McCartney Paul", "PMC"),
new Person("Harrison George", "GH")
};
}

private record Person(string Name, string Initials);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<HxMultiSelect Label="Band members"
TItem="Person"
TValue="string"
Data="@people"
@bind-Value="@selectedPersonsInitials"
TextSelector="@(p => p.Name)"
ValueSelector="@(p => p.Initials)"
NullDataText="Loading band members..."
AllowSelectAll="true"
SelectAllText="Select all"/>

<p class="mt-3">Selected initials: @String.Join(' ', selectedPersonsInitials)</p>

@code {
private List<Person> people;
private List<string> selectedPersonsInitials { get; set; } = new();

protected override async Task OnInitializedAsync()
{
await Task.Delay(1000); // simulates slow server API call

people = new List<Person>
{
new Person("Starr Ringo", "RS"),
new Person("Lennon John", "JL"),
new Person("McCartney Paul", "PMC"),
new Person("Harrison George", "GH")
};
}

private record Person(string Name, string Initials);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<HxMultiSelect Label="Band members"
TItem="Person"
TValue="string"
Data="@people"
@bind-Value="@selectedPersonsInitials"
TextSelector="@(p => p.Name)"
ValueSelector="@(p => p.Initials)"
NullDataText="Loading band members..."
AllowFiltering="true"
ClearFilterOnHide="false"
FilterPredicate="IsRecordIncluded">
<FilterEmptyResultTemplate>
<HxIcon Icon="BootstrapIcon.EmojiFrown" /> No items found
</FilterEmptyResultTemplate>
</HxMultiSelect>

<p class="mt-3">Selected initials: @String.Join(' ', selectedPersonsInitials)</p>

@code {
private List<Person> people;
private List<string> selectedPersonsInitials { get; set; } = new();

protected override async Task OnInitializedAsync()
{
await Task.Delay(1000); // simulates slow server API call

people = new List<Person>
{
new Person("Starr Ringo", "RS"),
new Person("Lennon John", "JL"),
new Person("McCartney Paul", "PMC"),
new Person("Harrison George", "GH")
};
}

private bool IsRecordIncluded(Person person, string filter)
{
return string.IsNullOrWhiteSpace(filter)
|| person.Name.Contains(filter, StringComparison.OrdinalIgnoreCase)
|| person.Initials.Contains(filter, StringComparison.OrdinalIgnoreCase);
}

private record Person(string Name, string Initials);
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,16 @@
<MainContent>

<DocHeading Title="Basic usage" />
<Demo Type="typeof(HxMultiSelect_Demo_BasicUsage)" Tabs="false" />
<Demo Type="typeof(HxMultiSelect_Demo_BasicUsage)" />

<DocHeading Title="Basic filtering" />
<Demo Type="typeof(HxMultiSelect_Demo_BasicFiltering)" />

<DocHeading Title="Custom filtering" />
<Demo Type="typeof(HxMultiSelect_Demo_TemplatedFiltering)" />

<DocHeading Title="Select all" />
<Demo Type="typeof(HxMultiSelect_Demo_SelectAll)" />

</MainContent>
<CssVariables>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2701,34 +2701,17 @@
<member name="M:Havit.Blazor.Components.Web.Bootstrap.Internal.HxInputDateRangeInternal.DisposeAsync">
<inheritdoc />
</member>
<member name="P:Havit.Blazor.Components.Web.Bootstrap.Internal.HxMultiSelectInternal`2.InputGroupCssClass">
<summary>
Custom CSS class to render with input-group span.
</summary>
</member>
<member name="P:Havit.Blazor.Components.Web.Bootstrap.Internal.HxMultiSelectInternal`2.InputGroupStartText">
<summary>
Input-group at the beginning of the input.
</summary>
</member>
<member name="P:Havit.Blazor.Components.Web.Bootstrap.Internal.HxMultiSelectInternal`2.InputGroupStartTemplate">
<summary>
Input-group at the beginning of the input.
</summary>
</member>
<member name="P:Havit.Blazor.Components.Web.Bootstrap.Internal.HxMultiSelectInternal`2.InputGroupEndText">
<member name="P:Havit.Blazor.Components.Web.Bootstrap.Internal.HxMultiSelectInternal`2.AdditionalAttributes">
<summary>
Input-group at the end of the input.
Additional attributes to be splatted onto an underlying HTML element.
</summary>
</member>
<member name="P:Havit.Blazor.Components.Web.Bootstrap.Internal.HxMultiSelectInternal`2.InputGroupEndTemplate">
<summary>
Input-group at the end of the input.
</summary>
<member name="M:Havit.Blazor.Components.Web.Bootstrap.Internal.HxMultiSelectInternal`2.OnAfterRenderAsync(System.Boolean)">
<inheritdoc cref="M:Microsoft.AspNetCore.Components.ComponentBase.OnAfterRenderAsync(System.Boolean)" />
</member>
<member name="P:Havit.Blazor.Components.Web.Bootstrap.Internal.HxMultiSelectInternal`2.AdditionalAttributes">
<member name="M:Havit.Blazor.Components.Web.Bootstrap.Internal.HxMultiSelectInternal`2.HandleJsHidden">
<summary>
Additional attributes to be splatted onto an underlying HTML element.
Receives notification from JavaScript when item is hidden.
</summary>
</member>
<member name="T:Havit.Blazor.Components.Web.Bootstrap.Internal.IFormValueComponent">
Expand Down Expand Up @@ -4384,6 +4367,52 @@
Input-group at the end of the input.
</summary>
</member>
<member name="P:Havit.Blazor.Components.Web.Bootstrap.HxMultiSelect`2.AllowFiltering">
<summary>
Enables filtering capabilities.
</summary>
</member>
<member name="P:Havit.Blazor.Components.Web.Bootstrap.HxMultiSelect`2.FilterPredicate">
<summary>
Defines a custom filtering predicate to apply to the list of items.
If not specified, the default behavior filters items based on whether the item text (obtained via TextSelector) contains the filter query string.
</summary>
</member>
<member name="P:Havit.Blazor.Components.Web.Bootstrap.HxMultiSelect`2.ClearFilterOnHide">
<summary>
When enabled the filter will be cleared when the dropdown is closed.
</summary>
</member>
<member name="P:Havit.Blazor.Components.Web.Bootstrap.HxMultiSelect`2.FilterEmptyResultTemplate">
<summary>
Template that defines what should be rendered in case of empty items.
</summary>
</member>
<member name="P:Havit.Blazor.Components.Web.Bootstrap.HxMultiSelect`2.FilterEmptyResultText">
<summary>
Text to display when the filtered results list is empty and when not using <see cref="P:Havit.Blazor.Components.Web.Bootstrap.HxMultiSelect`2.FilterEmptyResultTemplate"/>.
</summary>
</member>
<member name="P:Havit.Blazor.Components.Web.Bootstrap.HxMultiSelect`2.AllowSelectAll">
<summary>
Enables select all capabilities.
</summary>
</member>
<member name="P:Havit.Blazor.Components.Web.Bootstrap.HxMultiSelect`2.SelectAllText">
<summary>
Text to display for the select all dropdown option.
</summary>
</member>
<member name="P:Havit.Blazor.Components.Web.Bootstrap.HxMultiSelect`2.FilterSearchIcon">
<summary>
Icon displayed in filter input for searching the filter.
</summary>
</member>
<member name="P:Havit.Blazor.Components.Web.Bootstrap.HxMultiSelect`2.FilterClearIcon">
<summary>
Icon displayed in filter input for clearing the filter.
</summary>
</member>
<member name="M:Havit.Blazor.Components.Web.Bootstrap.HxMultiSelect`2.FocusAsync">
<inheritdoc cref="M:Havit.Blazor.Components.Web.Bootstrap.HxInputBase`1.FocusAsync"/>
</member>
Expand Down Expand Up @@ -5021,6 +5050,31 @@
Input size.
</summary>
</member>
<member name="P:Havit.Blazor.Components.Web.Bootstrap.MultiSelectSettings.AllowFiltering">
<summary>
Enables filtering capabilities.
</summary>
</member>
<member name="P:Havit.Blazor.Components.Web.Bootstrap.MultiSelectSettings.AllowSelectAll">
<summary>
Enables select all capabilities.
</summary>
</member>
<member name="P:Havit.Blazor.Components.Web.Bootstrap.MultiSelectSettings.ClearFilterOnHide">
<summary>
When enabled the filter will be cleared when the dropdown is closed.
</summary>
</member>
<member name="P:Havit.Blazor.Components.Web.Bootstrap.MultiSelectSettings.FilterSearchIcon">
<summary>
Icon displayed in filter input for searching the filter.
</summary>
</member>
<member name="P:Havit.Blazor.Components.Web.Bootstrap.MultiSelectSettings.FilterClearIcon">
<summary>
Icon displayed in filter input for clearing the filter.
</summary>
</member>
<member name="T:Havit.Blazor.Components.Web.Bootstrap.RadioButtonListSettings">
<summary>
Settings for <see cref="T:Havit.Blazor.Components.Web.Bootstrap.HxRadioButtonListBase`2"/>.
Expand Down
Loading