coveo-search-ui
Version:
Coveo JavaScript Search Framework
318 lines (285 loc) • 12.2 kB
text/typescript
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');
}
}