import './styles/search.css';
import Mousetrap from 'mousetrap';
document.addEventListener('DOMContentLoaded', () => {
new Search();
});
class Search {
constructor() {
this.currentlySelectedResult = 0;
this.searchContainerElement = document.querySelector('.search-container');
this.#intializeEvents();
this.#initializeQueryContents();
this.#enableKeyboardShortcuts();
this.#enableDocSearchElements();
}
#intializeEvents() {
document.querySelectorAll('.form-search-input').forEach((searchInput) => {
// TODO: fix this; it doesn't work because the 'change' listener is only triggered when the
// input field loses its focus. I asked Ryan but the solution doesn't look clear to me yet
searchInput.addEventListener('change', () => {
this.currentlySelectedResult = 0;
});
});
}
#initializeQueryContents() {
const queryStringParams = new URLSearchParams(window.location.search);
if (!queryStringParams.has('q')) {
return;
}
const query = queryStringParams.get('q');
document.querySelectorAll('.form-search-input').forEach((searchInput) => {
searchInput.value = query;
searchInput.dispatchEvent(new Event('change', { bubbles: true }));
});
}
#enableKeyboardShortcuts() {
Mousetrap.bind(['shift shift', 'command+k', 'ctrl+k'], () => this.#createAndOpenSearchModal());
}
#enableDocSearchElements() {
document.querySelectorAll('.form-search-input-button').forEach((searchElement) => {
searchElement.addEventListener('click', () => this.#createAndOpenSearchModal(), false);
});
}
#createAndOpenSearchModal() {
// needed to close other search results that might exists when opening the modal window
this.#resetSearchResults();
Mousetrap.bind('down', () => this.#selectNextResult());
Mousetrap.bind('up', () => this.#selectPreviousResult());
Mousetrap.bind('enter', (e) => {
return this.#goToSelectedResult();
});
const searchModalBackdrop = document.querySelector('#search-modal-backdrop');
const searchModalContent = document.querySelector('#search-modal-content');
const searchModalCloseButton = document.querySelector('#search-modal-close-button');
const searchInputElement = searchModalContent.querySelector('input[type="search"]');
searchModalBackdrop.classList.add('is-visible');
searchInputElement.focus();
searchInputElement.addEventListener('click', (e) => {
e.stopPropagation();
return false;
});
this.searchContainerElement = searchInputElement.closest('.search-container');
Mousetrap.bind('escape', () => {
this.#closeSearchModal();
});
searchModalCloseButton.addEventListener('click', () => {
this.#closeSearchModal();
});
searchModalBackdrop.addEventListener('click', () => {
this.#closeSearchModal();
});
}
#closeSearchModal() {
Mousetrap.unbind('down');
Mousetrap.unbind('up');
Mousetrap.unbind('enter');
const searchModalBackdrop = document.querySelector('#search-modal-backdrop');
if (searchModalBackdrop !== undefined) {
searchModalBackdrop.classList.remove('is-visible');
}
this.#resetSearchResults();
this.searchContainerElement = document.querySelector('.search-container');
}
#selectNextResult() {
const numberOfResults = document.querySelectorAll('.search-results .search-result').length;
if (numberOfResults === this.currentlySelectedResult) {
return;
}
this.currentlySelectedResult++;
this.#selectResult();
}
#selectPreviousResult() {
if (1 === this.currentlySelectedResult) {
return;
}
this.currentlySelectedResult--;
this.#selectResult();
}
#selectResult() {
this.searchContainerElement.querySelectorAll('.search-result').forEach((searchResultElement, index) => {
const selectedElementIndex = index + 1;
if (selectedElementIndex === this.currentlySelectedResult) {
searchResultElement.classList.add('active');
} else {
searchResultElement.classList.remove('active');
}
});
}
#goToSelectedResult() {
if (0 === this.currentlySelectedResult) {
return false;
}
this.searchContainerElement.querySelector('.search-results .search-result.active a').click();
return false;
}
#resetSearchResults() {
this.searchContainerElement.querySelector('input[type="search"]').value = '';
const searchResultsElement = this.searchContainerElement.querySelector('.search-results');
if (null !== searchResultsElement) {
searchResultsElement.innerHTML = '';
}
this.currentlySelectedResult = 0;
Mousetrap.unbind('escape');
Mousetrap.unbind('down');
Mousetrap.unbind('up');
Mousetrap.unbind('enter');
this.searchContainerElement = document.querySelector('.search-container');
}
}