UNPKG

coveo-search-ui

Version:

Coveo JavaScript Search Framework

318 lines (285 loc) • 12.2 kB
import { $$, Dom } from '../../utils/Dom'; import { Utils } from '../../utils/Utils'; import * as _ from 'underscore'; import 'styling/_InitializationPlaceholder'; import { QueryEvents } from '../../events/QueryEvents'; import { InitializationEvents } from '../../events/InitializationEvents'; import { ResultListEvents } from '../../events/ResultListEvents'; import { HashUtils } from '../../utils/HashUtils'; import { ComponentsTypes } from '../../utils/ComponentsTypes'; export interface InitializationPlaceholderOption { searchInterface?: boolean; facet?: boolean; searchbox?: boolean; resultList?: boolean; layout?: string; waitingForFirstQueryMode?: boolean; } export class InitializationPlaceholder { public facetPlaceholder = `<div class="coveo-placeholder-title"></div> <div class="coveo-facet-placeholder-line"> <div class="coveo-facet-placeholder-checkbox"></div> <div class="coveo-placeholder-text"></div> </div> <div class="coveo-facet-placeholder-line"> <div class="coveo-facet-placeholder-checkbox"></div> <div class="coveo-placeholder-text"></div> </div> <div class="coveo-facet-placeholder-line"> <div class="coveo-facet-placeholder-checkbox"></div> <div class="coveo-placeholder-text"></div> </div> <div class="coveo-facet-placeholder-line"> <div class="coveo-facet-placeholder-checkbox"></div> <div class="coveo-placeholder-text"></div> </div> <div class="coveo-facet-placeholder-line"> <div class="coveo-facet-placeholder-checkbox"></div> <div class="coveo-placeholder-text"></div> </div>`; public resultListPlaceholder = `<div class="coveo-result-frame coveo-placeholder-result"> <div class="coveo-result-row"> <div class="coveo-result-cell" style="width:85px;text-align:center;"> <div class="coveo-placeholder-icon"></div> </div> <div class="coveo-result-cell" style="padding-left:15px;"> <div class="coveo-result-row"> <div class="coveo-result-cell"> <div class="coveo-placeholder-title" style="width: 60%"></div> </div> <div class="coveo-result-cell" style="width:120px; text-align:right;"> <div class="coveo-placeholder-text" style="width: 80%"></div> </div> </div> <div class="coveo-result-row"> <div class="coveo-result-cell"> <div class="coveo-placeholder-text" style="width: 70%"></div> <div class="coveo-placeholder-text" style="width: 90%"></div> <div class="coveo-placeholder-text" style="width: 60%"></div> </div> </div> </div> </div> </div>`; public cardResultListPlaceholder = `<div class="coveo-card-layout coveo-placeholder-result CoveoResult"> <div class="coveo-result-frame"> <div class="coveo-result-row" style="margin-bottom: 20px;"> <div class="coveo-result-cell" style="width: 32px; vertical-align: middle;"> <div class="coveo-placeholder-icon-small"></div> </div> <div class="coveo-result-cell" style="text-align:left; padding-left: 10px; vertical-align: middle;"> <div class="coveo-placeholder-title" style="width: 60%"></div> </div> </div> <div class="coveo-result-row" style="margin-bottom: 20px;"> <div class="coveo-result-cell"> <div class="coveo-placeholder-text" style="width: 70%"></div> <div class="coveo-placeholder-text" style="width: 90%"></div> <div class="coveo-placeholder-text" style="width: 60%"></div> </div> <div class="coveo-result-cell"> <div class="coveo-placeholder-text" style="width: 90%"></div> <div class="coveo-placeholder-text" style="width: 70%"></div> <div class="coveo-placeholder-text" style="width: 60%"></div> </div> </div> <div class="coveo-result-row"> <div class="coveo-result-cell"> <div class="coveo-placeholder-text" style="width: 90%"></div> <div class="coveo-placeholder-text" style="width: 100%"></div> </div> </div> </div> </div> `; public recommendationResultListPlaceholder = `<div class="coveo-result-frame coveo-placeholder-result"> <div class="coveo-result-row"> <div class="coveo-result-cell" style="width: 32px; vertical-align: middle;"> <div class="coveo-placeholder-icon-small"></div> </div> <div class="coveo-result-cell" style="padding-left:10px; vertical-align: middle;"> <div class="coveo-result-row"> <div class="coveo-result-cell"> <div class="coveo-placeholder-title" style="width: 90%"></div> </div> </div> </div> </div> `; public static NUMBER_OF_FACETS = 3; public static NUMBER_OF_RESULTS = 10; public static NUMBER_OF_RESULTS_RECOMMENDATION = 5; public static INITIALIZATION_CLASS = 'coveo-during-initialization'; public static AFTER_INITIALIZATION_CLASS = 'coveo-after-initialization'; private eventToRemovePlaceholder = InitializationEvents.afterComponentsInitialization; constructor(public root: HTMLElement) {} public withEventToRemovePlaceholder(event: string) { this.eventToRemovePlaceholder = event; return this; } public withFullInitializationStyling() { $$(this.root).addClass(InitializationPlaceholder.INITIALIZATION_CLASS); $$(this.root).removeClass(InitializationPlaceholder.AFTER_INITIALIZATION_CLASS); $$(this.root).one(this.eventToRemovePlaceholder, () => { $$(this.root).removeClass(InitializationPlaceholder.INITIALIZATION_CLASS); $$(this.root).addClass(InitializationPlaceholder.AFTER_INITIALIZATION_CLASS); }); return this; } public withHiddenRootElement() { $$(this.root).addClass('coveo-hidden'); return this; } public withVisibleRootElement() { $$(this.root).removeClass('coveo-hidden'); return this; } public withWaitingForFirstQueryMode() { $$(this.root).addClass('coveo-waiting-for-query'); $$(this.root).one(QueryEvents.duringQuery, () => { $$(this.root).removeClass('coveo-waiting-for-query'); }); return this; } public withAllPlaceholders() { this.withPlaceholderForFacets(); this.withPlaceholderForResultList(); this.withPlaceholderSearchbox(); return this; } public withPlaceholderForFacets() { // Render an arbitrary number of placeholder facet. // Facets should become usable on the first deferredQuerySuccess const facetElements = ComponentsTypes.getAllFacetElementsFromElement(this.root); if (Utils.isNonEmptyArray(facetElements)) { const placeholders: Dom[] = []; _.each(facetElements, (facetElement: HTMLElement) => $$(facetElement).addClass(InitializationPlaceholder.INITIALIZATION_CLASS)); _.each(_.first(facetElements, InitializationPlaceholder.NUMBER_OF_FACETS), (facetElement: HTMLElement) => { $$(facetElement).addClass('coveo-with-placeholder'); const placeHolder = $$('div', { className: 'coveo-facet-placeholder' }, this.facetPlaceholder); facetElement.appendChild(placeHolder.el); placeholders.push(placeHolder); }); $$(this.root).one(this.eventToRemovePlaceholder, () => { const toExecuteAfterInitialization = () => { _.each(placeholders, (placeholder: Dom) => placeholder.remove()); _.each(facetElements, (facetElement: HTMLElement) => $$(facetElement).removeClass(InitializationPlaceholder.INITIALIZATION_CLASS) ); _.each(facetElements, (facetElement: HTMLElement) => $$(facetElement).removeClass('coveo-with-placeholder')); }; $$(this.root).one(QueryEvents.queryError, () => toExecuteAfterInitialization()); $$(this.root).one(QueryEvents.deferredQuerySuccess, () => toExecuteAfterInitialization()); }); } return this; } public withPlaceholderSearchbox() { // Searchbox should be good/usable afterComponentsInitialization // Create a placeholder until we reach that event. const searchBoxElements = $$(this.root).findAll('.CoveoSearchbox'); if (Utils.isNonEmptyArray(searchBoxElements)) { _.each(searchBoxElements, el => { $$(el).addClass(InitializationPlaceholder.INITIALIZATION_CLASS); }); $$(this.root).one(this.eventToRemovePlaceholder, () => { _.each(searchBoxElements, (el: HTMLElement) => $$(el).removeClass(InitializationPlaceholder.INITIALIZATION_CLASS)); }); } return this; } public withPlaceholderForResultList() { // Render an arbitrary number of placeholder in the first result list we find // When we get the first newResultDisplayedEvent, the result list should be usable. const resultListsElements = $$(this.root).findAll('.CoveoResultList'); if (Utils.isNonEmptyArray(resultListsElements)) { _.each(resultListsElements, el => $$(el).addClass(InitializationPlaceholder.INITIALIZATION_CLASS)); const { placeholderToUse, resultListToUse, rootToUse } = this.determineResultListPlaceholder(resultListsElements); $$(resultListToUse).append(rootToUse); $$(resultListToUse).addClass('coveo-with-placeholder'); _.times( this.isRecommendationRoot() ? InitializationPlaceholder.NUMBER_OF_RESULTS_RECOMMENDATION : InitializationPlaceholder.NUMBER_OF_RESULTS, () => { rootToUse.innerHTML += placeholderToUse; } ); const reset = () => { $$(rootToUse).remove(); _.each(resultListsElements, el => $$(el).removeClass(InitializationPlaceholder.INITIALIZATION_CLASS)); $$(resultListToUse).removeClass('coveo-with-placeholder'); }; $$(this.root).one(ResultListEvents.newResultDisplayed, () => reset()); $$(this.root).one(QueryEvents.queryError, () => reset()); $$(this.root).one(QueryEvents.noResults, () => reset()); } return this; } private determineResultListPlaceholder(resultListElements: HTMLElement[]) { let currentLayout; if (resultListElements.length > 1) { currentLayout = HashUtils.getValue('layout', HashUtils.getHash()); } else if (resultListElements.length == 1) { currentLayout = resultListElements[0].getAttribute('data-layout'); } if (!currentLayout) { currentLayout = 'list'; } if (resultListElements.length > 1) { let resultListElement = _.find(resultListElements, resultListElement => { return resultListElement.getAttribute('data-layout') == currentLayout; }); if (!resultListElement) { // No data-layout default to list resultListElement = _.find(resultListElements, resultListElement => { return resultListElement.getAttribute('data-layout') == null; }); } if (!resultListElement) { // Last fallback resultListElement = _.first(resultListElements); } return { placeholderToUse: this.determineResultListFromLayout(currentLayout), resultListToUse: resultListElement, rootToUse: this.determineRootFromLayout(currentLayout) }; } else if (resultListElements.length == 1) { return { placeholderToUse: this.determineResultListFromLayout(currentLayout), resultListToUse: resultListElements[0], rootToUse: this.determineRootFromLayout(currentLayout) }; } else { return null; } } private determineResultListFromLayout(layout: string) { switch (layout) { case 'list': if (this.isRecommendationRoot()) { return this.recommendationResultListPlaceholder; } else { return this.resultListPlaceholder; } case 'card': return this.cardResultListPlaceholder; default: return this.resultListPlaceholder; } } private determineRootFromLayout(layout: string): HTMLElement { switch (layout) { case 'list': return $$('div').el; case 'card': return $$('div', { className: 'coveo-result-list-container coveo-card-layout-container' }).el; default: return $$('div').el; } } private isRecommendationRoot(): boolean { return $$(this.root).hasClass('CoveoRecommendation'); } }