Skip to main content
Version: 1.0.3

Advanced Search Form

A comprehensive search interface that combines powerful filtering, saved searches, faceted navigation, and export capabilities. Perfect for admin dashboards, user management systems, and data exploration tools. Features include real-time filtering, saved search management, multiple export formats, theme customization, and comprehensive debugging tools.

Installation

ignix add component advanced-search-form

Basic Usage

import { useState, useCallback } from 'react';
import {
AdvancedSearchForm,
SearchFilters,
SearchActions,
SearchSavedSearches,
SearchFacetedHints,
SearchResultsCount,
SearchTextFilter,
SearchSelectFilter,
SearchMultiSelectFilter,
SearchDateRangeFilter,
SearchNumericRangeFilter,
} from '@ignix-ui/advanced-search-form';

const roleOptions = [
{ value: "admin", label: "Admin", count: 5 },
{ value: "manager", label: "Manager", count: 4 },
{ value: "user", label: "User", count: 5 },
{ value: "developer", label: "Developer", count: 4 },
{ value: "designer", label: "Designer", count: 2 },
];

const statusOptions = [
{ value: "active", label: "Active", count: 12 },
{ value: "inactive", label: "Inactive", count: 4 },
{ value: "pending", label: "Pending", count: 3 },
{ value: "suspended", label: "Suspended", count: 1 },
];

const departmentOptions = [
{ value: "engineering", label: "Engineering", count: 7 },
{ value: "sales", label: "Sales", count: 5 },
{ value: "marketing", label: "Marketing", count: 4 },
{ value: "hr", label: "Human Resources", count: 3 },
{ value: "finance", label: "Finance", count: 4 },
];

const locationOptions = [
{ value: "new york", label: "New York", count: 6 },
{ value: "los angeles", label: "Los Angeles", count: 3 },
{ value: "chicago", label: "Chicago", count: 4 },
{ value: "san francisco", label: "San Francisco", count: 4 },
{ value: "seattle", label: "Seattle", count: 3 },
];

const facetedCategories = [
{ id: "role", label: "Role", options: roleOptions },
{ id: "status", label: "Status", options: statusOptions },
{ id: "department", label: "Department", options: departmentOptions },
];

const savedSearches = [
{ id: "1", name: "Active Admins", filters: { role: "admin", status: ["active"] } },
{ id: "2", name: "Sales Team", filters: { department: "sales", status: ["active"] } },
{ id: "3", name: "High Performers", filters: { scoreRange: { min: 85 } } },
{ id: "4", name: "Engineering Team", filters: { department: "engineering", status: ["active"] } },
];

// Mock user data
const mockUsers = [
{ id: 1, name: "John Doe", role: "admin", status: "active", department: "engineering", location: "New York", age: 32, score: 95, salary: 95000, created: "2024-01-15" },
{ id: 2, name: "Jane Smith", role: "user", status: "active", department: "sales", location: "Los Angeles", age: 28, score: 88, salary: 75000, created: "2024-02-20" },
{ id: 3, name: "Bob Johnson", role: "manager", status: "inactive", department: "marketing", location: "Chicago", age: 45, score: 76, salary: 85000, created: "2023-12-10" },
{ id: 4, name: "Alice Brown", role: "admin", status: "active", department: "finance", location: "Boston", age: 35, score: 92, salary: 82000, created: "2024-01-05" },
{ id: 5, name: "Charlie Wilson", role: "developer", status: "pending", department: "engineering", location: "San Francisco", age: 24, score: 67, salary: 70000, created: "2024-02-28" },
{ id: 6, name: "Diana Prince", role: "manager", status: "active", department: "engineering", location: "New York", age: 38, score: 94, salary: 105000, created: "2023-11-20" },
{ id: 7, name: "Eve Adams", role: "developer", status: "active", department: "engineering", location: "Seattle", age: 26, score: 89, salary: 90000, created: "2024-03-01" },
{ id: 8, name: "Frank Castle", role: "designer", status: "active", department: "marketing", location: "Austin", age: 42, score: 71, salary: 72000, created: "2023-10-15" },
];

function App() {
const [activeFilters, setActiveFilters] = useState({});
const [filteredResults, setFilteredResults] = useState(mockUsers);

const handleSearch = () => {
let results = [...mockUsers];

if (activeFilters.name) {
results = results.filter(u =>
u.name.toLowerCase().includes(activeFilters.name.toLowerCase())
);
}
if (activeFilters.role) {
results = results.filter(u =>
u.role.toLowerCase() === activeFilters.role.toLowerCase()
);
}
if (activeFilters.department) {
results = results.filter(u =>
u.department.toLowerCase() === activeFilters.department.toLowerCase()
);
}
if (activeFilters.status && Array.isArray(activeFilters.status)) {
results = results.filter(u =>
activeFilters.status.includes(u.status.toLowerCase())
);
}

setFilteredResults(results);
};

return (
<AdvancedSearchForm variant="dark" layout="stacked">
<div className="space-y-6">
<SearchFilters>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
<SearchSelectFilter
filter={{ id: "role", label: "Role", type: "select", options: roleOptions }}
value={activeFilters.role || ""}
onChange={(val) => setActiveFilters({ ...activeFilters, role: val })}
/>
<SearchSelectFilter
filter={{ id: "department", label: "Department", type: "select", options: departmentOptions }}
value={activeFilters.department || ""}
onChange={(val) => setActiveFilters({ ...activeFilters, department: val })}
/>
<SearchMultiSelectFilter
filter={{ id: "status", label: "Status", type: "multi-select", options: statusOptions }}
value={activeFilters.status || []}
onChange={(val) => setActiveFilters({ ...activeFilters, status: val })}
/>
</div>
</SearchFilters>

<SearchActions
onApply={handleSearch}
onReset={() => setActiveFilters({})}
showApply={true}
showReset={true}
/>

<SearchResultsCount count={filteredResults.length} total={mockUsers.length} />

<div className="space-y-2">
{filteredResults.map(user => (
<div key={user.id} className="p-4 border rounded-lg">
<p className="font-semibold">{user.name}</p>
<p className="text-sm text-gray-600">
{user.role}{user.department}{user.status}
</p>
</div>
))}
</div>
</div>
</AdvancedSearchForm>
);
}

Interfaces

export type FilterType = 'text' | 'select' | 'multi-select' | 'date-range' | 'checkbox' | 'numeric-range';
export type DateRangePreset = 'today' | 'yesterday' | 'last7days' | 'last30days' | 'last90days' | 'thisMonth' | 'lastMonth' | 'custom';
export type ExportFormat = 'csv' | 'excel' | 'json';
export type SearchVariant = 'default' | 'dark' | 'light';
export type LayoutVariant = 'inline' | 'stacked' | 'collapsible';

export interface FilterOption {
/** Option value (stored in filter state) */
value: string;
/** Display label for the option */
label: string;
/** Optional count to show next to label */
count?: number;
}

export interface BaseFilterConfig {
/** Unique identifier for the filter */
id: string;
/** Display label for the filter */
label: string;
/** Type of filter to render */
type: FilterType;
/** Placeholder text for input */
placeholder?: string;
/** Default value for the filter */
defaultValue?: string | string[] | DateRangeValue | NumericRangeValue;
/** Whether the filter is required */
required?: boolean;
/** Whether to take full width on mobile devices */
fullWidthOnMobile?: boolean;
}

export interface TextFilterConfig extends BaseFilterConfig {
type: 'text';
}

export interface SelectFilterConfig extends BaseFilterConfig {
type: 'select';
/** Options for the select dropdown */
options: FilterOption[];
}

export interface MultiSelectFilterConfig extends BaseFilterConfig {
type: 'multi-select';
/** Options for multi-select */
options: FilterOption[];
/** Props to pass to internal Checkbox components */
checkboxProps?: Omit<React.ComponentProps<typeof Checkbox>, 'checked' | 'onChange' | 'label'>;
}

export interface DateRangeFilterConfig extends BaseFilterConfig {
type: 'date-range';
/** Available preset ranges */
presets?: DateRangePreset[];
/** Props to pass to internal DatePicker component */
datePickerProps?: Omit<React.ComponentProps<typeof DatePicker>, 'value' | 'onChange' | 'variant' | 'themeMode'>;
}

export interface CheckboxFilterConfig extends BaseFilterConfig {
type: 'checkbox';
/** Options for checkbox group */
options: FilterOption[];
/** Props to pass to internal Checkbox components */
checkboxProps?: Omit<React.ComponentProps<typeof Checkbox>, 'checked' | 'onChange' | 'label'>;
}

export interface NumericRangeFilterConfig extends BaseFilterConfig {
type: 'numeric-range';
/** Minimum allowed value */
min?: number;
/** Maximum allowed value */
max?: number;
/** Step increment for number inputs */
step?: number;
}

export type FilterConfig =
| TextFilterConfig
| SelectFilterConfig
| MultiSelectFilterConfig
| DateRangeFilterConfig
| CheckboxFilterConfig
| NumericRangeFilterConfig;

export interface SavedSearch {
/** Unique identifier for the saved search */
id: string;
/** Display name of the saved search */
name: string;
/** Filter state to restore */
filters: Record<string, FilterValue>;
/** Optional creation timestamp */
createdAt?: Date;
}

export interface FacetedCategory {
/** Unique identifier for the category */
id: string;
/** Display label for the category */
label: string;
/** Available options for this category */
options: FilterOption[];
}

export interface DateRangeValue {
/** Start date in YYYY-MM-DD format */
start?: string;
/** End date in YYYY-MM-DD format */
end?: string;
/** Selected preset */
preset?: DateRangePreset;
}

export interface NumericRangeValue {
/** Minimum value */
min?: number;
/** Maximum value */
max?: number;
}

export type FilterValue =
| string
| string[]
| DateRangeValue
| NumericRangeValue
| undefined;

Props

AdvancedSearchForm Props

PropTypeDefaultDescription
variant'default' | 'dark' | 'light''default'Visual theme variant
layout'inline' | 'stacked' | 'collapsible''stacked'Layout style for filters
filtersFilterConfig[][]Array of filter configurations
facetedCategoriesFacetedCategory[]Categories for faceted search hints
onSearch(filters: Record<string, FilterValue>) => voidCalled when search is triggered
onExport(filters: Record<string, FilterValue>, format: ExportFormat) => voidCalled when export is triggered
onSaveSearch(search: SavedSearch) => voidCalled when saving a search
onDeleteSearch(searchId: string) => voidCalled when deleting a saved search
initialFiltersRecord<string, FilterValue>{}Initial filter values
savedSearchesSavedSearch[][]Predefined saved searches
resultsCountnumberCurrent number of results
totalResultsnumberTotal number of results
autoApplybooleanfalseApply filters automatically as they change
debounceMsnumber300Debounce delay for auto-apply in milliseconds
showExportbooleantrueShow export button
showSaveSearchbooleantrueShow save search button
showResetbooleantrueShow reset button
showFacetedbooleantrueShow faceted search hints
showResultsCountbooleantrueShow results count
classNamestringAdditional CSS classes
childrenReact.ReactNodeCustom child components
ariaLabelstring'Advanced search form'ARIA label for accessibility
mobileBreakpointnumber768Breakpoint for mobile layout in pixels
tabletBreakpointnumber1024Breakpoint for tablet layout in pixels
defaultCollapsedOnMobilebooleantrueCollapse filters panel on mobile by default

SearchFilters Props

PropTypeDefaultDescription
childrenReact.ReactNodeCustom filter components
classNamestringAdditional CSS classes

SearchActions Props

PropTypeDefaultDescription
onApply() => voidCalled when apply button is clicked
onReset() => voidCalled when reset button is clicked
onSave() => voidCalled when save button is clicked
onExport() => voidCalled when export button is clicked
showApplybooleantrueShow apply button (hidden when autoApply is true)
showResetbooleantrueShow reset button
showSavebooleantrueShow save button
showExportbooleantrueShow export button
classNamestringAdditional CSS classes

SearchSavedSearches Props

PropTypeDefaultDescription
searchesSavedSearch[]Saved searches to display
onApply(search: SavedSearch) => voidCalled when a saved search is applied
onDelete(searchId: string) => voidCalled when a saved search is deleted
classNamestringAdditional CSS classes

SearchFacetedHints Props

PropTypeDefaultDescription
categoriesFacetedCategory[]Faceted categories to display
onSelect(categoryId: string, value: string) => voidCalled when a facet is selected
classNamestringAdditional CSS classes

SearchResultsCount Props

PropTypeDefaultDescription
countnumberCurrent result count
totalnumberTotal result count
classNamestringAdditional CSS classes

Individual Filter Component Props

ComponentPropsDescription
SearchTextFilterfilter: TextFilterConfig, value: string, onChange: (value: string) => void, className?: stringText input filter
SearchSelectFilterfilter: SelectFilterConfig, value: string, onChange: (value: string) => void, className?: stringSingle-select dropdown filter
SearchMultiSelectFilterfilter: MultiSelectFilterConfig, value: string[], onChange: (value: string[]) => void, className?: stringMulti-select dropdown with checkboxes
SearchDateRangeFilterfilter: DateRangeFilterConfig, value: DateRangeValue, onChange: (value: DateRangeValue) => void, className?: stringDate range picker with presets
SearchCheckboxFilterfilter: CheckboxFilterConfig, value: string[], onChange: (value: string[]) => void, className?: stringCheckbox group filter
SearchNumericRangeFilterfilter: NumericRangeFilterConfig, value: NumericRangeValue, onChange: (value: NumericRangeValue) => void, className?: stringMin/max number inputs
SearchFilterRendererfilter: FilterConfig, value: FilterValue, onChange: (value: FilterValue) => void, className?: stringAutomatic renderer based on filter type

Features

  • Multiple filter types (text, select, multi-select, date range, checkbox, numeric range)
  • Responsive (mobile, tablet, desktop)
  • Auto-apply with debounce
  • Saved searches
  • Faceted filtering (quick refine)
  • Export support (CSV, Excel, JSON)
  • Collapsible filters (mobile optimized)
  • Theming (light/dark)
  • Fully composable architecture