Adds fuzzy search filter system

Implements a fuzzy search filter system using Fuse.js to enhance event searching.

This system allows users to quickly find events by typing partial matches of event titles or descriptions, providing visual feedback by dimming non-matching events. The filter persists during navigation and includes escape key support for quick clearing. It also includes performance optimizations like requestAnimationFrame debouncing.
This commit is contained in:
Janus Knudsen 2025-08-23 00:01:59 +02:00
parent a3ed03ff44
commit 12df6a9b06
9 changed files with 513 additions and 1 deletions

View file

@ -0,0 +1,195 @@
# Fuzzy Search Filter System
## Overview
The calendar includes a powerful fuzzy search filter system that allows users to quickly find events by typing partial matches of event titles or descriptions. The system dims non-matching events while keeping matching events fully visible, providing instant visual feedback.
## Features
- **Real-time fuzzy search** using Fuse.js library
- **Visual filtering** with opacity-based dimming (0.2 for non-matches, 1.0 for matches)
- **Minimum character threshold** (2 characters) handled by Fuse.js configuration
- **Cross-navigation persistence** - filter applies to pre-rendered grids during navigation
- **Escape key support** for quick filter clearing
- **Performance optimized** with requestAnimationFrame debouncing
## Architecture
### Core Components
#### EventFilterManager (`src/managers/EventFilterManager.ts`)
Central component responsible for:
- Managing search input event listeners
- Performing fuzzy search with Fuse.js
- Coordinating visual updates across all event containers
- Emitting filter change events via EventBus
#### CSS-based Visual System
```css
/* Dim all events when filter is active */
swp-events-layer[data-filter-active="true"] swp-event {
opacity: 0.2;
transition: opacity 200ms ease;
}
/* Keep matching events fully visible */
swp-events-layer[data-filter-active="true"] swp-event[data-matches="true"] {
opacity: 1;
}
```
#### Pre-rendered Grid Support
- NavigationManager listens for `FILTER_CHANGED` events
- NavigationRenderer applies filter state to all grid containers
- Ensures filter persists when navigating between time periods
### Event Flow
1. **User Input** → Search field receives input
2. **Debounced Processing** → RequestAnimationFrame prevents excessive filtering
3. **Fuzzy Search** → Fuse.js performs search against event titles/descriptions
4. **DOM Updates** → Matching events marked with `data-matches="true"`
5. **CSS Application** → Non-matching events automatically dimmed via CSS rules
6. **Cross-Grid Sync** → Filter applied to all pre-rendered grids
## Configuration
### Fuse.js Settings
```typescript
const fuseOptions = {
keys: ['title', 'description'], // Search in title and description
threshold: 0.3, // 0 = exact match, 1 = match anything
includeScore: true, // Include relevance scores
minMatchCharLength: 2, // Minimum 2 characters for match
shouldSort: true, // Sort by relevance
ignoreLocation: true // Search anywhere in string
};
```
### CSS Variables
```css
:root {
--filter-dimmed-opacity: 0.2; /* Opacity for non-matching events */
--filter-transition: 200ms ease; /* Smooth opacity transitions */
}
```
## Integration Points
### CalendarManager
```typescript
// EventFilterManager is initialized in CalendarManager constructor
this.eventFilterManager = new EventFilterManager();
```
### EventRenderer
```typescript
// Events emit EVENTS_RENDERED for filter system to track
this.eventBus.emit(CoreEvents.EVENTS_RENDERED, {
events: events,
container: context.container
});
```
### HTML Structure
```html
<swp-search-container>
<input type="search" placeholder="Search events..." />
</swp-search-container>
```
## User Experience
### Search Behavior
- **No input**: All events visible at full opacity
- **1 character**: Filter active, but Fuse.js won't return matches (by design)
- **2+ characters**: Real-time fuzzy matching with visual feedback
- **Empty field**: Filter cleared, all events return to normal
- **Escape key**: Immediate filter clearing
### Visual Feedback
- **Search field styling**: Border color changes when filter is active
- **Event dimming**: Non-matching events fade to 20% opacity
- **Smooth transitions**: 200ms ease transitions for polished feel
## Performance Considerations
### Optimization Strategies
1. **RequestAnimationFrame debouncing** prevents excessive search operations
2. **CSS-based visual updates** leverage browser's rendering pipeline
3. **Event delegation** avoids per-event listener overhead
4. **Efficient DOM queries** using attribute selectors
### Memory Management
- Event listeners properly scoped to component lifecycle
- Fuse.js instance recreated only when event data changes
- Minimal DOM manipulation through attribute-based CSS rules
## Error Handling
### Graceful Degradation
- System continues to function if Fuse.js fails to load
- Search input remains functional for basic text entry
- Console warnings for debugging (removed in production)
### Edge Cases
- **No events to search**: Filter system remains inactive
- **Empty search results**: All events dimmed (expected behavior)
- **DOM timing issues**: Waits for DOM readiness before initialization
## Future Enhancements
### Potential Improvements
- **Search result highlighting** within event text
- **Advanced filters** (date range, event type, etc.)
- **Search history** with dropdown suggestions
- **Keyboard navigation** through search results
- **Search analytics** for usage insights
### Performance Optimizations
- **Virtual scrolling** for large event sets
- **Search index caching** for repeated queries
- **Web Workers** for heavy search operations
## Dependencies
### External Libraries
- **Fuse.js** (v7.x) - Fuzzy search library
- License: Apache 2.0
- File: `wwwroot/js/lib/fuse.min.mjs`
- Import: ES module format
### Internal Dependencies
- **EventBus** - Inter-component communication
- **CoreEvents** - Centralized event type definitions
- **CalendarEvent** - Event data type definitions
## Testing Considerations
### Test Scenarios
1. **Basic search functionality** - Verify fuzzy matching works
2. **Performance testing** - Large event sets (1000+ events)
3. **Cross-navigation** - Filter persistence during navigation
4. **Edge cases** - Empty results, special characters, very long queries
5. **Accessibility** - Screen reader compatibility, keyboard navigation
### Browser Compatibility
- **Modern browsers** with ES modules support
- **CSS custom properties** support required
- **requestAnimationFrame** API required
## Maintenance Notes
### Code Organization
- **Single responsibility** - EventFilterManager handles only filtering
- **Event-driven** - Loose coupling via EventBus
- **CSS-first** - Visual logic in stylesheets, not JavaScript
- **Type-safe** - Full TypeScript implementation
### Common Issues
- **Timing**: Ensure EventFilterManager initializes after DOM ready
- **Event data**: Verify EVENTS_RENDERED events are emitted correctly
- **CSS specificity**: Filter styles must override existing event styles
- **Memory leaks**: Properly clean up event listeners in destroy()
This fuzzy search filter system provides an intuitive and performant way for users to quickly locate specific events within the calendar interface.