import { services } from "@/config/services"; import { getServiceIconPath, getServiceUrl, searchServices, } from "@/utils/service"; export class ServiceSearch { private isOpen = false; private query = ""; private results: typeof services = []; private selectedIndex = 0; private container: HTMLDivElement | null = null; private searchButton: HTMLButtonElement | null = null; constructor() { this.init(); } private init() { // Create search button this.searchButton = document.createElement("button"); this.searchButton.className = "fixed bottom-4 right-4 md:hidden bg-primary text-primary-foreground p-3 rounded-full shadow-lg z-40"; this.searchButton.innerHTML = ` `; this.searchButton.addEventListener("click", () => this.open()); document.body.appendChild(this.searchButton); // Create container this.container = document.createElement("div"); this.container.className = "fixed inset-0 bg-black/50 flex items-start justify-center pt-[10vh] md:pt-[20vh] z-50 hidden"; this.container.innerHTML = `
`; document.body.appendChild(this.container); // Get elements const input = this.container.querySelector("input"); const resultsContainer = this.container.querySelector(".max-h-\\[70vh\\]"); const closeButton = this.container.querySelector("button"); if (!input || !resultsContainer || !closeButton) return; // Add event listeners input.addEventListener("input", (e) => { this.query = (e.target as HTMLInputElement).value; this.updateResults(); }); closeButton.addEventListener("click", () => this.close()); document.addEventListener("keydown", this.handleKeyDown.bind(this)); } private handleKeyDown(e: KeyboardEvent) { // Open search with Ctrl+K or Cmd+K if ((e.ctrlKey || e.metaKey) && e.key === "k") { e.preventDefault(); this.open(); } // Close search with Escape if (e.key === "Escape") { this.close(); } // Navigate results with arrow keys if (this.isOpen) { if (e.key === "ArrowDown") { e.preventDefault(); this.selectedIndex = Math.min( this.selectedIndex + 1, this.results.length - 1, ); this.updateResults(); } else if (e.key === "ArrowUp") { e.preventDefault(); this.selectedIndex = Math.max(this.selectedIndex - 1, 0); this.updateResults(); } else if (e.key === "Enter" && this.results[this.selectedIndex]) { e.preventDefault(); const service = this.results[this.selectedIndex]; window.open(getServiceUrl(service), "_blank"); this.close(); } } } private updateResults() { if (!this.container) return; const resultsContainer = this.container.querySelector(".max-h-\\[70vh\\]"); if (!resultsContainer) return; if (this.query.trim()) { this.results = searchServices(services, this.query); } else { this.results = []; } if (this.results.length > 0) { resultsContainer.innerHTML = this.results .map( (service, index) => ` `, ) .join(""); // Add click handlers resultsContainer.querySelectorAll("button").forEach((button, index) => { button.addEventListener("click", () => { const service = this.results[index]; window.open(getServiceUrl(service), "_blank"); this.close(); }); }); } else if (this.query) { resultsContainer.innerHTML = `
No services found
`; } else { resultsContainer.innerHTML = ""; } } private open() { if (!this.container) return; this.isOpen = true; this.container.classList.remove("hidden"); const input = this.container.querySelector("input"); if (input) { input.value = ""; input.focus(); } this.query = ""; this.results = []; this.selectedIndex = 0; this.updateResults(); } private close() { if (!this.container) return; this.isOpen = false; this.container.classList.add("hidden"); } }