107 lines
2.2 KiB
TypeScript
107 lines
2.2 KiB
TypeScript
|
|
/**
|
||
|
|
* Search Controller
|
||
|
|
*
|
||
|
|
* Handles global search functionality and keyboard shortcuts
|
||
|
|
*/
|
||
|
|
|
||
|
|
export class SearchController {
|
||
|
|
private input: HTMLInputElement | null = null;
|
||
|
|
private container: HTMLElement | null = null;
|
||
|
|
|
||
|
|
constructor() {
|
||
|
|
this.input = document.getElementById('globalSearch') as HTMLInputElement | null;
|
||
|
|
this.container = document.querySelector<HTMLElement>('swp-topbar-search');
|
||
|
|
|
||
|
|
this.setupListeners();
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Get current search value
|
||
|
|
*/
|
||
|
|
get value(): string {
|
||
|
|
return this.input?.value ?? '';
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Set search value
|
||
|
|
*/
|
||
|
|
set value(val: string) {
|
||
|
|
if (this.input) {
|
||
|
|
this.input.value = val;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Focus the search input
|
||
|
|
*/
|
||
|
|
focus(): void {
|
||
|
|
this.input?.focus();
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Blur the search input
|
||
|
|
*/
|
||
|
|
blur(): void {
|
||
|
|
this.input?.blur();
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Clear the search input
|
||
|
|
*/
|
||
|
|
clear(): void {
|
||
|
|
this.value = '';
|
||
|
|
}
|
||
|
|
|
||
|
|
private setupListeners(): void {
|
||
|
|
// Keyboard shortcuts
|
||
|
|
document.addEventListener('keydown', (e) => this.handleKeyboard(e));
|
||
|
|
|
||
|
|
// Input handlers
|
||
|
|
if (this.input) {
|
||
|
|
this.input.addEventListener('input', (e) => this.handleInput(e));
|
||
|
|
|
||
|
|
// Prevent form submission if wrapped in form
|
||
|
|
const form = this.input.closest('form');
|
||
|
|
form?.addEventListener('submit', (e) => this.handleSubmit(e));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
private handleKeyboard(e: KeyboardEvent): void {
|
||
|
|
// Cmd/Ctrl + K to focus search
|
||
|
|
if ((e.metaKey || e.ctrlKey) && e.key === 'k') {
|
||
|
|
e.preventDefault();
|
||
|
|
this.focus();
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Escape to blur search when focused
|
||
|
|
if (e.key === 'Escape' && document.activeElement === this.input) {
|
||
|
|
this.blur();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
private handleInput(e: Event): void {
|
||
|
|
const target = e.target as HTMLInputElement;
|
||
|
|
const query = target.value.trim();
|
||
|
|
|
||
|
|
// Emit custom event for search
|
||
|
|
document.dispatchEvent(new CustomEvent('app:search', {
|
||
|
|
detail: { query },
|
||
|
|
bubbles: true
|
||
|
|
}));
|
||
|
|
}
|
||
|
|
|
||
|
|
private handleSubmit(e: Event): void {
|
||
|
|
e.preventDefault();
|
||
|
|
|
||
|
|
const query = this.value.trim();
|
||
|
|
if (!query) return;
|
||
|
|
|
||
|
|
// Emit custom event for search submit
|
||
|
|
document.dispatchEvent(new CustomEvent('app:search-submit', {
|
||
|
|
detail: { query },
|
||
|
|
bubbles: true
|
||
|
|
}));
|
||
|
|
}
|
||
|
|
}
|