Skip to content

Commit 4a3f145

Browse files
committed
feat: Introduce centralized constants and service factory for AutoComplete component
- Added AutoCompleteConstants class to centralize configuration values such as max search length, debounce intervals, and CSS class names. - Implemented IAutoCompleteServiceFactory interface and AutoCompleteServiceFactory class to facilitate the creation of component-scoped services. - Updated AutoComplete component to utilize centralized constants for default values and improved service initialization. - Refactored input handling and debounce logic to use the new AsyncDebouncer class for better performance and compatibility. - Enhanced documentation in README.md for setup and usage instructions. - Added logging for service fallback scenarios to aid in debugging configuration issues.
1 parent 553ac55 commit 4a3f145

15 files changed

Lines changed: 764 additions & 61 deletions

README.md

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,19 @@ dotnet add package EasyAppDev.Blazor.AutoComplete
2525

2626
## Setup
2727

28-
Add to your `App.razor` or `index.html`:
28+
**1. Register services** in `Program.cs`:
29+
30+
```csharp
31+
using EasyAppDev.Blazor.AutoComplete.Extensions;
32+
33+
builder.Services.AddAutoComplete();
34+
```
35+
36+
This registers:
37+
- `IThemeManager` - Theme CSS generation (singleton)
38+
- `IAutoCompleteServiceFactory` - Creates component services (singleton)
39+
40+
**2. Add styles** to your `App.razor` or `index.html`:
2941

3042
```html
3143
<head>
@@ -34,11 +46,7 @@ Add to your `App.razor` or `index.html`:
3446
</head>
3547
```
3648

37-
**Optional:** Register services for singleton theme management:
38-
39-
```csharp
40-
builder.Services.AddAutoComplete();
41-
```
49+
> **Note:** The component works without `AddAutoComplete()` using fallback instances, but registering services enables proper DI, testability, and singleton behavior.
4250
4351
## Basic Usage
4452

@@ -432,8 +440,9 @@ Configuration in `appsettings.json`:
432440

433441
- **CSS Sanitization** - Theme values validated against allowlists
434442
- **Input Limits** - `MaxSearchLength` prevents memory exhaustion (default 500, max 2000)
435-
- **ReDoS Protection** - Regex patterns use timeouts
443+
- **ReDoS Protection** - Regex patterns use 100ms timeouts
436444
- **API Key Redaction** - Sensitive data removed from error messages
445+
- **Centralized Constants** - All limits defined in `AutoCompleteConstants` for consistency
437446

438447
## Packages
439448

samples/TrimTest.Wasm/Program.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using Microsoft.AspNetCore.Components.Web;
22
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
3+
using EasyAppDev.Blazor.AutoComplete.Extensions;
34
using TrimTest.Wasm;
45

56
var builder = WebAssemblyHostBuilder.CreateDefault(args);
@@ -8,4 +9,7 @@
89

910
builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
1011

12+
// Register AutoComplete services (IThemeManager, IAutoCompleteServiceFactory)
13+
builder.Services.AddAutoComplete();
14+
1115
await builder.Build().RunAsync();
Lines changed: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
1+
namespace EasyAppDev.Blazor.AutoComplete;
2+
3+
/// <summary>
4+
/// Centralized constants used throughout the AutoComplete component library.
5+
/// Provides a single source of truth for magic numbers, timeouts, and CSS class names.
6+
/// </summary>
7+
public static class AutoCompleteConstants
8+
{
9+
#region Security Limits
10+
11+
/// <summary>
12+
/// Absolute maximum search length to prevent memory exhaustion attacks.
13+
/// </summary>
14+
public const int AbsoluteMaxSearchLength = 2000;
15+
16+
/// <summary>
17+
/// Default maximum search length for user input.
18+
/// </summary>
19+
public const int DefaultMaxSearchLength = 500;
20+
21+
/// <summary>
22+
/// Maximum allowed distance for fuzzy matching (Levenshtein distance).
23+
/// </summary>
24+
public const int MaxFuzzyDistance = 2;
25+
26+
/// <summary>
27+
/// Maximum character length per dimension for Levenshtein calculation (~4MB memory limit).
28+
/// </summary>
29+
public const int MaxLevenshteinDimensionLength = 1000;
30+
31+
#endregion
32+
33+
#region Timing
34+
35+
/// <summary>
36+
/// Default debounce interval in milliseconds for the core component.
37+
/// </summary>
38+
public const int DefaultDebounceMs = 300;
39+
40+
/// <summary>
41+
/// Default debounce interval in milliseconds for AI semantic search.
42+
/// </summary>
43+
public const int DefaultAIDebounceMs = 500;
44+
45+
/// <summary>
46+
/// Delay before closing dropdown on focus out (allows click events to register).
47+
/// </summary>
48+
public const int FocusOutDelayMs = 200;
49+
50+
/// <summary>
51+
/// Timeout for CSS regex pattern matching to prevent ReDoS attacks.
52+
/// </summary>
53+
public const int CssRegexTimeoutMs = 100;
54+
55+
#endregion
56+
57+
#region Display Defaults
58+
59+
/// <summary>
60+
/// Default minimum number of characters before search triggers.
61+
/// </summary>
62+
public const int DefaultMinSearchLength = 1;
63+
64+
/// <summary>
65+
/// Default maximum number of items to display in the dropdown.
66+
/// </summary>
67+
public const int DefaultMaxDisplayedItems = 100;
68+
69+
/// <summary>
70+
/// Default item height in pixels for virtualization.
71+
/// </summary>
72+
public const float DefaultItemHeight = 40f;
73+
74+
/// <summary>
75+
/// Default virtualization threshold (minimum items before virtualization enables).
76+
/// </summary>
77+
public const int DefaultVirtualizationThreshold = 100;
78+
79+
#endregion
80+
81+
#region AI/Caching Defaults
82+
83+
/// <summary>
84+
/// Default similarity threshold for semantic search (0.0 to 1.0).
85+
/// </summary>
86+
public const float DefaultSimilarityThreshold = 0.15f;
87+
88+
/// <summary>
89+
/// Default TTL for item embedding cache in minutes.
90+
/// </summary>
91+
public const int DefaultItemCacheTtlMinutes = 60;
92+
93+
/// <summary>
94+
/// Default TTL for query embedding cache in minutes.
95+
/// </summary>
96+
public const int DefaultQueryCacheTtlMinutes = 15;
97+
98+
/// <summary>
99+
/// Default maximum items in embedding cache.
100+
/// </summary>
101+
public const int DefaultMaxCachedItems = 10000;
102+
103+
/// <summary>
104+
/// Default maximum queries in embedding cache.
105+
/// </summary>
106+
public const int DefaultMaxCachedQueries = 1000;
107+
108+
/// <summary>
109+
/// Default memory pressure threshold for cache eviction (0.0 to 1.0).
110+
/// </summary>
111+
public const float DefaultMemoryPressureThreshold = 0.85f;
112+
113+
/// <summary>
114+
/// Cache cleanup interval in minutes.
115+
/// </summary>
116+
public const int CacheCleanupIntervalMinutes = 5;
117+
118+
#endregion
119+
120+
#region CSS Class Prefixes
121+
122+
/// <summary>
123+
/// Base CSS class prefix for all AutoComplete components.
124+
/// </summary>
125+
public const string CssPrefix = "ebd-ac";
126+
127+
/// <summary>
128+
/// CSS class for the container element.
129+
/// </summary>
130+
public const string CssContainer = "ebd-ac-container";
131+
132+
/// <summary>
133+
/// CSS class for the input wrapper element.
134+
/// </summary>
135+
public const string CssInputWrapper = "ebd-ac-input-wrapper";
136+
137+
/// <summary>
138+
/// CSS class for the dropdown element.
139+
/// </summary>
140+
public const string CssDropdown = "ebd-ac-dropdown";
141+
142+
/// <summary>
143+
/// CSS class for the listbox element.
144+
/// </summary>
145+
public const string CssListbox = "ebd-ac-listbox";
146+
147+
/// <summary>
148+
/// CSS class for individual list items.
149+
/// </summary>
150+
public const string CssItem = "ebd-ac-item";
151+
152+
/// <summary>
153+
/// CSS class for selected items.
154+
/// </summary>
155+
public const string CssSelected = "ebd-ac-selected";
156+
157+
/// <summary>
158+
/// CSS class for keyboard-selected items.
159+
/// </summary>
160+
public const string CssKeyboardSelected = "ebd-ac-keyboard-selected";
161+
162+
/// <summary>
163+
/// CSS class for invalid state (validation errors).
164+
/// </summary>
165+
public const string CssInvalid = "ebd-ac-invalid";
166+
167+
/// <summary>
168+
/// CSS class for loading state.
169+
/// </summary>
170+
public const string CssLoading = "ebd-ac-loading";
171+
172+
/// <summary>
173+
/// CSS class for disabled state.
174+
/// </summary>
175+
public const string CssDisabled = "ebd-ac-disabled";
176+
177+
/// <summary>
178+
/// CSS class prefix for theme variants.
179+
/// </summary>
180+
public const string CssThemePrefix = "ebd-ac-theme";
181+
182+
/// <summary>
183+
/// CSS class prefix for Bootstrap theme variants.
184+
/// </summary>
185+
public const string CssBootstrapThemePrefix = "ebd-ac-bs";
186+
187+
/// <summary>
188+
/// CSS class prefix for size variants.
189+
/// </summary>
190+
public const string CssSizePrefix = "ebd-ac-size";
191+
192+
/// <summary>
193+
/// CSS class for theme transitions.
194+
/// </summary>
195+
public const string CssThemeTransitions = "ebd-ac-theme-transitions";
196+
197+
#endregion
198+
199+
#region CSS Custom Property Prefixes
200+
201+
/// <summary>
202+
/// Prefix for all CSS custom properties.
203+
/// </summary>
204+
public const string CssVarPrefix = "--ebd-ac";
205+
206+
/// <summary>
207+
/// CSS custom property for primary color.
208+
/// </summary>
209+
public const string CssVarPrimary = "--ebd-ac-primary";
210+
211+
/// <summary>
212+
/// CSS custom property for background color.
213+
/// </summary>
214+
public const string CssVarBackground = "--ebd-ac-bg";
215+
216+
/// <summary>
217+
/// CSS custom property for text color.
218+
/// </summary>
219+
public const string CssVarText = "--ebd-ac-text";
220+
221+
/// <summary>
222+
/// CSS custom property for border color.
223+
/// </summary>
224+
public const string CssVarBorder = "--ebd-ac-border";
225+
226+
#endregion
227+
228+
#region Default Styles
229+
230+
/// <summary>
231+
/// Default CSS class for badge styling (Bootstrap 5).
232+
/// </summary>
233+
public const string DefaultBadgeClass = "badge bg-primary";
234+
235+
#endregion
236+
}

0 commit comments

Comments
 (0)