/**
* Arod Search Modal Component
* An expandable search interface with modal overlay
*
* Usage:
*
*
* Events:
* - search: Fired when user performs a search
* - clear: Fired when search is cleared
*/
// ============================================
// AROD-SEARCH - Search Modal Component
// ============================================
class ArodSearch extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
this.isOpen = false;
this.searchDebounce = null;
}
connectedCallback() {
this.render();
this.setupEventListeners();
this.setupKeyboardShortcuts();
}
disconnectedCallback() {
this.removeKeyboardShortcuts();
}
render() {
const placeholder = this.getAttribute('placeholder') || 'Search...';
this.shadowRoot.innerHTML = `
`;
}
setupEventListeners() {
const trigger = this.shadowRoot.querySelector('.search-trigger');
const overlay = this.shadowRoot.querySelector('.modal-overlay');
const close = this.shadowRoot.querySelector('.search-close');
const input = this.shadowRoot.querySelector('.search-input');
trigger.addEventListener('click', () => this.open());
close.addEventListener('click', () => this.close());
overlay.addEventListener('click', (e) => {
if (e.target === overlay) this.close();
});
input.addEventListener('input', (e) => {
clearTimeout(this.searchDebounce);
this.searchDebounce = setTimeout(() => {
this.handleSearch(e.target.value);
}, 200);
});
input.addEventListener('keydown', (e) => {
if (e.key === 'Escape') this.close();
else if (e.key === 'ArrowDown') this.navigateResults(1);
else if (e.key === 'ArrowUp') this.navigateResults(-1);
else if (e.key === 'Enter') this.selectResult();
});
}
setupKeyboardShortcuts() {
this.keyHandler = (e) => {
// Cmd+K or Ctrl+K to open search
if ((e.metaKey || e.ctrlKey) && e.key === 'k') {
e.preventDefault();
this.open();
}
};
document.addEventListener('keydown', this.keyHandler);
}
removeKeyboardShortcuts() {
if (this.keyHandler) {
document.removeEventListener('keydown', this.keyHandler);
}
}
open() {
this.isOpen = true;
const overlay = this.shadowRoot.querySelector('.modal-overlay');
const input = this.shadowRoot.querySelector('.search-input');
overlay.classList.add('open');
setTimeout(() => input.focus(), 100);
}
close() {
this.isOpen = false;
const overlay = this.shadowRoot.querySelector('.modal-overlay');
const input = this.shadowRoot.querySelector('.search-input');
const results = this.shadowRoot.querySelector('.search-results');
overlay.classList.remove('open');
input.value = '';
results.innerHTML = '';
}
handleSearch(query) {
if (!query) {
this.clearResults();
return;
}
// Dispatch search event for external handling
this.dispatchEvent(new CustomEvent('search', {
detail: { query },
bubbles: true
}));
}
// Public method to display search results
displayResults(results) {
const container = this.shadowRoot.querySelector('.search-results');
container.innerHTML = '';
if (!results || results.length === 0) {
container.innerHTML = 'No results found
';
return;
}
results.forEach((result, index) => {
const item = document.createElement('div');
item.className = 'search-result-item';
item.dataset.index = index;
item.innerHTML = `
${result.title}
${result.snippet || ''}
`;
item.addEventListener('click', () => {
if (result.url) window.location.href = result.url;
this.close();
});
container.appendChild(item);
});
}
clearResults() {
const container = this.shadowRoot.querySelector('.search-results');
container.innerHTML = '';
this.dispatchEvent(new CustomEvent('clear', { bubbles: true }));
}
navigateResults(direction) {
const results = this.shadowRoot.querySelectorAll('.search-result-item');
if (!results.length) return;
const current = this.shadowRoot.querySelector('.search-result-item.selected');
let index = current ? parseInt(current.dataset.index) : -1;
index += direction;
if (index < 0) index = results.length - 1;
if (index >= results.length) index = 0;
results.forEach(r => r.classList.remove('selected'));
results[index].classList.add('selected');
results[index].scrollIntoView({ block: 'nearest' });
}
selectResult() {
const selected = this.shadowRoot.querySelector('.search-result-item.selected');
if (selected) selected.click();
}
}
// Register component
customElements.define('arod-search', ArodSearch);