Calendar/docs/fuzzy-search-filter-system.md
Janus Knudsen 12df6a9b06 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.
2025-08-23 00:01:59 +02:00

6.7 KiB

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

/* 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

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

:root {
  --filter-dimmed-opacity: 0.2;      /* Opacity for non-matching events */
  --filter-transition: 200ms ease;    /* Smooth opacity transitions */
}

Integration Points

CalendarManager

// EventFilterManager is initialized in CalendarManager constructor
this.eventFilterManager = new EventFilterManager();

EventRenderer

// Events emit EVENTS_RENDERED for filter system to track
this.eventBus.emit(CoreEvents.EVENTS_RENDERED, {
  events: events,
  container: context.container
});

HTML Structure

<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.