/* global IITC -- eslint */
/**
* Represents the view for displaying search query results in the IITC search module.
*
* @memberof IITC.search
* @class
*/
class QueryResultsView {
/**
* Initializes the query results view, setting up the display elements for the search term.
*
* @constructor
* @param {string} term - The search term.
* @param {boolean} confirmed - Indicates if the search is confirmed (e.g., by pressing Enter).
*/
constructor(term, confirmed) {
this.term = term;
this.confirmed = confirmed;
this.container = this.createContainer();
this.header = this.createHeader();
this.list = this.createList();
this.setupAccordion();
}
/**
* Creates and returns the main container element for the query results.
*
* @memberof IITC.search.QueryResultsView
* @function createContainer
* @returns {HTMLElement} - The container element for the results.
* @private
*/
createContainer() {
const container = document.createElement('div');
container.classList.add('searchquery');
return container;
}
/**
* Creates and appends a header to the container based on the search term.
*
* @memberof IITC.search.QueryResultsView
* @function createHeader
* @returns {HTMLElement} - The header element displaying the search term or a loading message.
* @private
*/
createHeader() {
const header = document.createElement('h3');
let headerText;
if (this.confirmed) {
headerText = this.term;
} else {
if (this.term.length > 16) {
const start = this.term.slice(0, 8);
const end = this.term.slice(-8);
headerText = `${start}…${end} (Return to load more)`;
} else {
headerText = `${this.term} (Return to load more)`;
}
}
header.textContent = headerText;
this.container.appendChild(header);
return header;
}
/**
* Creates and appends an initial list element to display the search results.
*
* @memberof IITC.search.QueryResultsView
* @function createList
* @returns {HTMLElement} - The list element for displaying the results.
* @private
*/
createList() {
const list = document.createElement('ul');
const initialItem = document.createElement('li');
initialItem.textContent = this.confirmed ? 'No local results, searching online...' : 'No local results.';
list.appendChild(initialItem);
this.container.appendChild(list);
return list;
}
/**
* Sets up the accordion functionality for expanding and collapsing results.
*
* @memberof IITC.search.QueryResultsView
* @function setupAccordion
* @private
*/
setupAccordion() {
this.header.addEventListener('click', () => {
this.container.classList.toggle('collapsed');
});
}
/**
* Renders the search results within the list container and sets up event interactions.
*
* @memberof IITC.search.QueryResultsView
* @function renderResults
* @param {Array<Object>} results - An array of search result objects to display.
* @param {Function} onResultInteraction - A callback function for handling interaction events on results.
*/
renderResults(results, onResultInteraction) {
this.clearList();
if (results.length === 0) {
const noResultsItem = document.createElement('li');
noResultsItem.textContent = 'No results found.';
this.list.appendChild(noResultsItem);
} else {
results.forEach((result) => {
const item = this.createListItem(result);
item.addEventListener('click', (ev) => onResultInteraction(result, ev));
item.addEventListener('dblclick', (ev) => onResultInteraction(result, ev));
item.addEventListener('mouseover', (ev) => onResultInteraction(result, ev));
item.addEventListener('mouseout', (ev) => onResultInteraction(result, ev));
item.addEventListener('keydown', (ev) => onResultInteraction(result, ev));
this.list.appendChild(item);
});
}
}
/**
* Creates and returns a list item for an individual search result.
*
* @memberof IITC.search.QueryResultsView
* @function createListItem
* @param {Object} result - The search result object with properties such as title, description, and icon.
* @returns {HTMLElement} - The list item element representing the search result.
* @private
*/
createListItem(result) {
const item = document.createElement('li');
item.tabIndex = 0;
const link = document.createElement('a');
link.innerHTML = result.title;
if (result.icon) {
link.style.backgroundImage = `url("${result.icon}")`;
item.style.listStyle = 'none';
}
item.appendChild(link);
if (result.description) {
const description = document.createElement('em');
description.innerHTML = result.description;
item.appendChild(document.createElement('br'));
item.appendChild(description);
}
return item;
}
/**
* Appends the results container to a specified selector on the page.
*
* @memberof IITC.search.QueryResultsView
* @function renderIn
* @param {string} selector - The selector string for the target container.
*/
renderIn(selector) {
const target = document.querySelector(selector);
if (target) target.appendChild(this.container);
}
/**
* Removes the results container from the page.
*
* @memberof IITC.search.QueryResultsView
* @function remove
* @private
*/
remove() {
if (this.container.parentNode) {
this.container.parentNode.removeChild(this.container);
}
}
/**
* Clears all items from the results list.
*
* @memberof IITC.search.QueryResultsView
* @function clearList
* @private
*/
clearList() {
this.list.innerHTML = '';
}
}
IITC.search.QueryResultsView = QueryResultsView;