Finder
Use static rules to effortlessly sort, filter, search, and group data of any shape.
Objects, classes, strings; whatever dataset you're manipulating, you can use a Rule to connect Finder to manipulate it. You don't need to transform your object to match an external rubric.
Reusable
Rules are object literals that can create reusable searches, filters, sorting, or grouping. Write a Rule once, use it everywhere you're working with that object - catalog, customer orders, monthly dashboards and so on!
Effortless string matching
Extract a string ( or strings ) from your item to search against. Rank searches by closest match to return relevant results faster. The <Finder.SearchTermHaystack /> component will highlight the matching segments of your Item's search term.
Coupled Rules
If you have rules that need to interact with each other, you can add ruleEffects to listen for rule changes. If certain filters are mutually incompatible, or you need to reset the sortBy rules when the active groupBy rule changes, ruleEffects will get it done.
Headless
Finder provides an API for managing rules and displaying matches. It doesn't have any opinions on how matches or controls should be rendered.
Rule Examples
- Search shoes
- Filter birds
- Sort fruits
- Group cars
interface Shoe {
product {
name: string;
}
image: string;
}
const rule = searchRule<Shoe>({
searchFn: (item) => item.product.name
})
return <Finder items={shoes} rules={[rule]} />
interface Avian {
name: string;
territory: string;
}
const rule = filterRule<Avian>({
id: 'territory_filter',
filterFn: (birb, value) => birb.territory === value;
})
return <Finder items={birds} rules={[rule]} />
Habitat: Alberta
Habitat: Yukon
Habitat: Ontario
Habitat: Nunavut
Habitat: Newfoundland and Labrador
Habitat: Ontario
Habitat: Manitoba
Habitat: Nunavut
Habitat: Yukon
Habitat: Ontario
Habitat: Saskatchewan
Habitat: Prince Edward Island
Habitat: Northwest Territories
Habitat: Alberta
Habitat: Saskatchewan
Habitat: Saskatchewan
Habitat: Prince Edward Island
Habitat: Newfoundland and Labrador
Habitat: Saskatchewan
Habitat: Alberta
Habitat: Prince Edward Island
Habitat: Alberta
Habitat: Ontario
Habitat: Nova Scotia
Habitat: Ontario
Habitat: Ontario
Habitat: Saskatchewan
Habitat: Northwest Territories
Habitat: Manitoba
Habitat: Manitoba
Habitat: Prince Edward Island
Habitat: Ontario
Habitat: Alberta
Habitat: Alberta
Habitat: Ontario
Habitat: Ontario
Habitat: New Brunswick
Habitat: Alberta
Habitat: Northwest Territories
Habitat: Nunavut
Habitat: Nunavut
Habitat: Manitoba
Habitat: New Brunswick
Habitat: Prince Edward Island
Habitat: British Columbia
Habitat: Alberta
Habitat: Ontario
Habitat: Ontario
Habitat: Saskatchewan
Habitat: Saskatchewan
Habitat: Yukon
Habitat: New Brunswick
Habitat: Nunavut
Habitat: Northwest Territories
Habitat: Saskatchewan
Habitat: Prince Edward Island
Habitat: Yukon
Habitat: Alberta
Habitat: Nova Scotia
Habitat: British Columbia
Habitat: Newfoundland and Labrador
Habitat: Alberta
Habitat: Yukon
Habitat: British Columbia
Habitat: Newfoundland and Labrador
Habitat: Newfoundland and Labrador
Habitat: Manitoba
Habitat: Saskatchewan
Habitat: British Columbia
Habitat: British Columbia
Habitat: Prince Edward Island
Habitat: Alberta
Habitat: Quebec
Habitat: Newfoundland and Labrador
Habitat: Ontario
Habitat: Saskatchewan
Habitat: New Brunswick
Habitat: New Brunswick
Habitat: Nunavut
Habitat: Yukon
Habitat: Manitoba
Habitat: Yukon
Habitat: Alberta
Habitat: New Brunswick
Habitat: Northwest Territories
Habitat: Manitoba
Habitat: Alberta
Habitat: British Columbia
Habitat: New Brunswick
Habitat: Alberta
Habitat: Ontario
Habitat: Manitoba
Habitat: Yukon
Habitat: Alberta
Habitat: Alberta
Habitat: New Brunswick
Habitat: Prince Edward Island
Habitat: Northwest Territories
Habitat: Nunavut
Habitat: Ontario
interface Fruit {
name: string;
price: number;
expiry_date: Date;
}
const ruleset = [
sortByRule<Fruit>({
id: 'expiry_date',
sortFn: (fruit) => fruit.expiry_date.getTime(),
label: "Expires soon",
defaultSortDirection: 'desc'
}),
sortByRule<Fruit>({
id: 'price',
sortFn: (fruit) => fruit.price,
label: "Price lowest to highest"
}
]);
return <Finder items={fruits} rules={ruleset} />
Best before: Monday, Oct 13
Best before: Monday, Oct 13
Best before: Monday, Oct 13
Best before: Monday, Oct 13
Best before: Tuesday, Oct 14
Best before: Tuesday, Oct 14
Best before: Thursday, Oct 16
Best before: Thursday, Oct 16
Best before: Thursday, Oct 16
Best before: Thursday, Oct 16
Best before: Thursday, Oct 16
Best before: Thursday, Oct 16
Best before: Thursday, Oct 16
Best before: Friday, Oct 17
Best before: Saturday, Oct 18
Best before: Saturday, Oct 18
Best before: Sunday, Oct 19
Best before: Monday, Oct 20
Best before: Monday, Oct 20
Best before: Tuesday, Oct 21
Best before: Tuesday, Oct 21
Best before: Wednesday, Oct 22
Best before: Wednesday, Oct 22
Best before: Thursday, Oct 23
Best before: Thursday, Oct 23
Best before: Thursday, Oct 23
Best before: Thursday, Oct 23
Best before: Thursday, Oct 23
Best before: Friday, Oct 24
Best before: Friday, Oct 24
Best before: Saturday, Oct 25
Best before: Sunday, Oct 26
Best before: Sunday, Oct 26
Best before: Monday, Oct 27
Best before: Monday, Oct 27
Best before: Tuesday, Oct 28
Best before: Wednesday, Oct 29
Best before: Wednesday, Oct 29
Best before: Wednesday, Oct 29
Best before: Wednesday, Oct 29
Best before: Wednesday, Oct 29
Best before: Thursday, Oct 30
Best before: Thursday, Oct 30
Best before: Thursday, Oct 30
Best before: Thursday, Oct 30
Best before: Friday, Oct 31
Best before: Friday, Oct 31
Best before: Saturday, Nov 1
Best before: Saturday, Nov 1
Best before: Saturday, Nov 1
Best before: Saturday, Nov 1
Best before: Sunday, Nov 2
Best before: Sunday, Nov 2
Best before: Sunday, Nov 2
Best before: Sunday, Nov 2
Best before: Sunday, Nov 2
Best before: Monday, Nov 3
Best before: Monday, Nov 3
Best before: Monday, Nov 3
Best before: Monday, Nov 3
Best before: Monday, Nov 3
Best before: Monday, Nov 3
Best before: Tuesday, Nov 4
Best before: Tuesday, Nov 4
Best before: Wednesday, Nov 5
Best before: Wednesday, Nov 5
Best before: Wednesday, Nov 5
Best before: Wednesday, Nov 5
Best before: Wednesday, Nov 5
Best before: Thursday, Nov 6
Best before: Thursday, Nov 6
Best before: Friday, Nov 7
Best before: Friday, Nov 7
Best before: Friday, Nov 7
Best before: Saturday, Nov 8
Best before: Sunday, Nov 9
Best before: Monday, Nov 10
Best before: Monday, Nov 10
Best before: Monday, Nov 10
Best before: Tuesday, Nov 11
Best before: Tuesday, Nov 11
Best before: Wednesday, Nov 12
Best before: Wednesday, Nov 12
Best before: Wednesday, Nov 12
Best before: Thursday, Nov 13
Best before: Friday, Nov 14
Best before: Friday, Nov 14
Best before: Friday, Nov 14
Best before: Friday, Nov 14
Best before: Saturday, Nov 15
Best before: Saturday, Nov 15
Best before: Sunday, Nov 16
Best before: Sunday, Nov 16
Best before: Sunday, Nov 16
Best before: Sunday, Nov 16
Best before: Monday, Nov 17
Best before: Monday, Nov 17
Best before: Monday, Nov 17
Best before: Monday, Nov 17
Best before: Tuesday, Nov 18
interface Vehicle {
name: string;
make: string;
model: string;
melts_in_rain: boolean;
}
const rule = groupByRule<Vehicle>({
id: 'group_by_make',
groupFn: (car) => car.make,
label: "Make",
// want to sticky certain groups to the top or bottom of the list?
// Use the sticky prop to weight them.
sticky: {
header: ['Honda'],
footer: 'Tesla'
}
})
return <Finder items={vehicles} rules={[rule]} requireGroup={true} />
Filtering and sorting data is the easiest thing in the world, and a disproportionate percentage of webdev tasks. It can also be a disproportionate amount of frustrations as client needs change at the last minute, filters get nested and coupled, and object shape changes.
Without good discipline and planning, data manipulation can become a spaghetti mountain of boilerplate. Finder is intended to make data manipulation easy, type-safe, and reusable.