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");
}
}