#
Searchable Grouped List
#
SearchableGroupedList
#
Description
SearchableGroupedList is a versatile collection component designed to render a list of items that can be grouped and searched. It provides built-in search functionality to filter items based on input terms and organizes them by their respective groups. Use this component when you need to display a categorized list that supports live searching over a finite set of items. It is part of the Collections group in the UI toolkit.
#
Usage
Create a SearchableGroupedList by providing an array or an observable list of items that implement the ISearchableGroupedItem interface, along with a function to generate a header component for each group. The component includes a search box that filters items in real time, and you can further customize its behavior, such as adding custom content before or after the search box or setting a custom “no results” message.
Below is an example of how to instantiate the component:
using System;
using Tesserae;
using static Tesserae.UI;
using static H5.Core.dom;
public class MySearchableGroupedListSample : IComponent
{
public HTMLElement Render()
{
// Sample items must implement ISearchableGroupedItem.
var items = new SearchableGroupedListItem[]
{
new SearchableGroupedListItem("Apple", "Fruits"),
new SearchableGroupedListItem("Broccoli", "Vegetables"),
new SearchableGroupedListItem("Banana", "Fruits"),
new SearchableGroupedListItem("Carrot", "Vegetables")
};
// Create the list with a header generator for groups.
var groupedList = SearchableGroupedList(items, groupName =>
HorizontalSeparator(TextBlock(groupName).Primary().SemiBold()).Left())
// Optional: add a custom message if no results match the search.
.WithNoResultsMessage(() =>
BackgroundArea(Card(TextBlock("No Results Found").Padding(16.px()))).WidthStretch().HeightStretch().MinHeight(100.px()));
return groupedList.Render();
}
}
public class SearchableGroupedListItem : ISearchableGroupedItem
{
private readonly string _value;
private readonly IComponent _component;
public SearchableGroupedListItem(string value, string group)
{
_value = value;
_component = Card(TextBlock(value).NonSelectable());
Group = group;
}
// Searches both on the item's value and the group name.
public bool IsMatch(string searchTerm) =>
_value.ToLower().Contains(searchTerm.ToLower()) || Group.ToLower().Contains(searchTerm.ToLower());
public string Group { get; }
public IComponent Render() => _component;
}
#
Methods
WithNoResultsMessage(Func
emptyListMessageGenerator)
Allows you to specify a custom component to display when no items match the search criteria.
Parameter:
• emptyListMessageGenerator: A delegate that returns an IComponent for the no-results display.WithGroupOrdering(IComparer
groupComparer)
Sets a custom comparer to order the groups.
Parameter:
• groupComparer: An IComparerto control the order of groups. SearchBox(Action
sb)
Provides access to the internal SearchBox to perform further customization, such as changing its placeholder text or styles.
Parameter:
• sb: An action to configure the SearchBox.CaptureSearchBox(out SearchBox sb)
Retrieves the reference to the internal SearchBox for additional modifications.
Parameter:
• sb: An output parameter that captures the SearchBox instance.BeforeSearchBox(params IComponent[] beforeComponents)
Adds additional UI components before the search box, such as buttons or labels.
Parameter:
• beforeComponents: An array of IComponent to display before the search box.AfterSearchBox(params IComponent[] afterComponents)
Adds additional UI components after the search box for enhanced controls or information.
Parameter:
• afterComponents: An array of IComponent to display after the search box.
#
Properties
ObservableList
Items
A list that holds the current set of rendered components (both headers and item components) as they are filtered and grouped.HTMLElement StylingContainer
Returns the root HTML element used for styling purposes. This container holds the inner elements of the component.bool PropagateToStackItemParent
Indicates whether styling should propagate to the parent stack container of the component. Always returns true.
#
Samples
#
Basic Grouped Searchable List
The following sample demonstrates how to create a searchable grouped list with a custom no-results message. In this example, items are grouped by category, and the search functionality filters items based on the provided search term.
using System;
using Tesserae;
using static Tesserae.UI;
using static H5.Core.dom;
public class BasicSearchableGroupedListSample : IComponent
{
public HTMLElement Render()
{
var items = new SearchableGroupedListItem[]
{
new SearchableGroupedListItem("Alpha", "Group A"),
new SearchableGroupedListItem("Beta", "Group B"),
new SearchableGroupedListItem("Gamma", "Group A"),
new SearchableGroupedListItem("Delta", "Group C"),
};
var searchableGroupedList = SearchableGroupedList(items,
groupName => HorizontalSeparator(TextBlock(groupName).Primary().SemiBold()).Left())
.WithNoResultsMessage(() =>
BackgroundArea(Card(TextBlock("No Results").Padding(16.px()))).WidthStretch().HeightStretch().MinHeight(100.px()));
return searchableGroupedList.Render();
}
}
public class SearchableGroupedListItem : ISearchableGroupedItem
{
private readonly string _value;
private readonly IComponent _component;
public SearchableGroupedListItem(string value, string group)
{
_value = value;
_component = Card(TextBlock(value).NonSelectable());
Group = group;
}
public bool IsMatch(string searchTerm) =>
_value.ToLower().Contains(searchTerm.ToLower()) || Group.ToLower().Contains(searchTerm.ToLower());
public string Group { get; }
public IComponent Render() => _component;
}
#
Advanced Customization with Additional Buttons
This sample shows how to add extra commands before and after the search box to extend the searchable grouped list functionality:
using System;
using Tesserae;
using static Tesserae.UI;
using static H5.Core.dom;
public class AdvancedSearchableGroupedListSample : IComponent
{
public HTMLElement Render()
{
var items = new SearchableGroupedListItem[]
{
new SearchableGroupedListItem("Item One", "Category 1"),
new SearchableGroupedListItem("Item Two", "Category 1"),
new SearchableGroupedListItem("Item Three", "Category 2"),
new SearchableGroupedListItem("Item Four", "Category 2")
};
var searchableGroupedList = SearchableGroupedList(items,
groupName => HorizontalSeparator(TextBlock(groupName).Primary().SemiBold()).Left())
.BeforeSearchBox(Button("Before Button").Link())
.AfterSearchBox(Button("After Button").Primary())
.WithNoResultsMessage(() =>
BackgroundArea(Card(TextBlock("Nothing found").Padding(16.px()))).WidthStretch().HeightStretch().MinHeight(100.px()));
return searchableGroupedList.Render();
}
}
public class SearchableGroupedListItem : ISearchableGroupedItem
{
private readonly string _value;
private readonly IComponent _component;
public SearchableGroupedListItem(string value, string group)
{
_value = value;
_component = Card(TextBlock(value).NonSelectable());
Group = group;
}
public bool IsMatch(string searchTerm) =>
_value.ToLower().Contains(searchTerm.ToLower()) || Group.ToLower().Contains(searchTerm.ToLower());
public string Group { get; }
public IComponent Render() => _component;
}