coveo-search-ui
Version:
Coveo JavaScript Search Framework
236 lines (204 loc) • 9.05 kB
text/typescript
import { ResponsiveComponentsManager, IResponsiveComponent, IResponsiveComponentOptions } from './ResponsiveComponentsManager';
import { ResponsiveComponentsUtils } from './ResponsiveComponentsUtils';
import { SearchInterface } from '../SearchInterface/SearchInterface';
import { Utils } from '../../utils/Utils';
import { $$, Dom } from '../../utils/Dom';
import { Logger } from '../../misc/Logger';
import { Recommendation } from '../Recommendation/Recommendation';
import { RecommendationDropdownContent } from './ResponsiveDropdown/RecommendationDropdownContent';
import { ResponsiveDropdownHeader } from './ResponsiveDropdown/ResponsiveDropdownHeader';
import { ResponsiveDropdown } from './ResponsiveDropdown/ResponsiveDropdown';
import { l } from '../../strings/Strings';
import { Component } from '../Base/Component';
import { get } from '../Base/RegisteredNamedMethods';
import { QueryEvents, IQuerySuccessEventArgs, INoResultsEventArgs } from '../../events/QueryEvents';
import * as _ from 'underscore';
import 'styling/_ResponsiveRecommendation';
import { Defer } from '../../MiscModules';
export class ResponsiveRecommendation implements IResponsiveComponent {
public static DROPDOWN_CONTAINER_CSS_CLASS_NAME: string = 'coveo-recommendation-dropdown-container';
public static RESPONSIVE_BREAKPOINT = 1000;
public recommendationRoot: Dom;
private breakpoint: number;
private dropdown: ResponsiveDropdown;
private dropdownHeaderLabel: string;
private searchInterface: SearchInterface;
public static init(root: HTMLElement, component, options: IResponsiveComponentOptions) {
let logger = new Logger('ResponsiveRecommendation');
let coveoRoot = this.findParentRootOfRecommendationComponent(root);
if (!coveoRoot) {
logger.info('Recommendation component has no parent interface. Disabling responsive mode for this component.');
return;
}
if (!$$(coveoRoot).find('.coveo-results-column')) {
logger.info('Cannot find element with class coveo-results-column. Disabling responsive mode for this component.');
return;
}
ResponsiveComponentsManager.register(
ResponsiveRecommendation,
$$(coveoRoot),
Recommendation.ID,
component,
_.extend({}, options, { initializationEventRoot: $$(root) })
);
}
private static findParentRootOfRecommendationComponent(root: HTMLElement): Dom {
let coveoRoot = $$(root).parents(Component.computeCssClassName(SearchInterface));
if (coveoRoot[0]) {
return $$(coveoRoot[0]);
}
return null;
}
constructor(
public coveoRoot: Dom,
public ID: string,
options: IResponsiveComponentOptions,
public responsiveDropdown?: ResponsiveDropdown
) {
this.recommendationRoot = this.getRecommendationRoot();
this.dropdownHeaderLabel = options.dropdownHeaderLabel;
this.breakpoint = this.defineResponsiveBreakpoint(options);
this.searchInterface = <SearchInterface>Component.get(this.coveoRoot.el, SearchInterface, false);
this.dropdown = this.buildDropdown(responsiveDropdown);
this.registerOnOpenHandler();
this.registerQueryEvents();
this.handleResizeEvent();
}
public handleResizeEvent(): void {
if (this.needSmallMode() && !ResponsiveComponentsUtils.isSmallRecommendationActivated(this.coveoRoot)) {
this.changeToSmallMode();
} else if (!this.needSmallMode() && ResponsiveComponentsUtils.isSmallRecommendationActivated(this.coveoRoot)) {
this.changeToLargeMode();
}
if (this.dropdown.isOpened) {
this.dropdown.dropdownContent.positionDropdown();
}
}
public needDropdownWrapper(): boolean {
return this.needSmallMode();
}
private needSmallMode(): boolean {
const isWidthSmallerThanBreakpoint = this.coveoRoot.width() <= this.breakpoint;
if (!this.searchInterface) {
return isWidthSmallerThanBreakpoint;
}
switch (this.searchInterface.responsiveComponents.getResponsiveMode()) {
case 'small':
return true;
case 'auto':
return isWidthSmallerThanBreakpoint;
default:
return false;
}
}
private changeToSmallMode() {
this.dropdown.close();
const header = this.coveoRoot.find(`.${ResponsiveComponentsManager.DROPDOWN_HEADER_WRAPPER_CSS_CLASS}`);
if (!header) {
// It's possible that recommendation gets initialized before the main interface is completed.
// We defer the resize event execution in that case.
Defer.defer(() => this.handleResizeEvent());
} else {
$$(header).append(this.dropdown.dropdownHeader.element.el);
this.disableFacetPreservePosition();
ResponsiveComponentsUtils.activateSmallRecommendation(this.coveoRoot);
ResponsiveComponentsUtils.activateSmallRecommendation(this.recommendationRoot);
}
}
private changeToLargeMode() {
this.enableFacetPreservePosition();
this.dropdown.cleanUp();
ResponsiveComponentsUtils.deactivateSmallRecommendation(this.coveoRoot);
ResponsiveComponentsUtils.deactivateSmallRecommendation(this.recommendationRoot);
}
private buildDropdown(responsiveDropdown?: ResponsiveDropdown): ResponsiveDropdown {
let dropdownContent = this.buildDropdownContent();
let dropdownHeader = this.buildDropdownHeader();
let dropdown = responsiveDropdown ? responsiveDropdown : new ResponsiveDropdown(dropdownContent, dropdownHeader, this.coveoRoot);
dropdown.disablePopupBackground();
return dropdown;
}
private buildDropdownHeader(): ResponsiveDropdownHeader {
let dropdownHeaderElement = $$('a');
let content = $$('p');
content.text(l(this.dropdownHeaderLabel));
dropdownHeaderElement.el.appendChild(content.el);
let dropdownHeader = new ResponsiveDropdownHeader('recommendation', dropdownHeaderElement);
return dropdownHeader;
}
private buildDropdownContent(): RecommendationDropdownContent {
let dropdownContentElement: Dom;
let recommendationColumn = this.coveoRoot.find('.coveo-recommendation-column');
if (recommendationColumn) {
dropdownContentElement = $$(recommendationColumn);
} else {
dropdownContentElement = $$(this.coveoRoot.find('.' + Component.computeCssClassName(Recommendation)));
}
let dropdownContent = new RecommendationDropdownContent('recommendation', dropdownContentElement, this.coveoRoot);
return dropdownContent;
}
private defineResponsiveBreakpoint(options: IResponsiveComponentOptions): number {
let breakpoint;
if (Utils.isNullOrUndefined(options.responsiveBreakpoint)) {
breakpoint = ResponsiveRecommendation.RESPONSIVE_BREAKPOINT;
} else {
breakpoint = options.responsiveBreakpoint;
}
return breakpoint;
}
private getFacetSliders(): any[] {
const facetSliders = [];
this.coveoRoot.findAll(Component.computeSelectorForType('FacetSlider')).forEach(facetSliderElement => {
const facetSlider = Component.get(facetSliderElement);
facetSlider && facetSliders.push(facetSlider);
});
return facetSliders;
}
private getFacets(): any[] {
const facets = [];
this.coveoRoot.findAll(Component.computeSelectorForType('Facet')).forEach(facetElement => {
const facet = Component.get(facetElement);
facet && facets.push(facet);
});
return facets;
}
private enableFacetPreservePosition(): void {
this.getFacets().forEach(facet => (facet.options.preservePosition = true));
}
private disableFacetPreservePosition(): void {
this.getFacets().forEach(facet => (facet.options.preservePosition = false));
}
private drawFacetSliderGraphs(): void {
this.getFacetSliders().forEach(facetSlider => facetSlider.drawDelayedGraphData());
}
private registerOnOpenHandler(): void {
this.dropdown.registerOnOpenHandler(this.drawFacetSliderGraphs, this);
}
private getRecommendationRoot(): Dom {
return $$(this.coveoRoot.find('.' + Component.computeCssClassName(Recommendation)));
}
private registerQueryEvents() {
let recommendationInstance = <Recommendation>get(this.recommendationRoot.el, SearchInterface);
if (recommendationInstance && recommendationInstance.options.hideIfNoResults) {
this.coveoRoot.on(QueryEvents.querySuccess, (e: Event, data: IQuerySuccessEventArgs) => this.handleRecommnendationQuerySucess(data));
this.coveoRoot.on(QueryEvents.noResults, (e: Event, data: INoResultsEventArgs) => this.handleRecommendationNoResults());
}
this.coveoRoot.on(QueryEvents.queryError, () => this.handleRecommendationQueryError());
}
private handleRecommnendationQuerySucess(data: IQuerySuccessEventArgs) {
if (data.results.totalCount === 0) {
this.dropdown.close();
this.dropdown.dropdownHeader.hide();
} else {
this.dropdown.dropdownHeader.show();
}
}
private handleRecommendationNoResults() {
this.dropdown.close();
this.dropdown.dropdownHeader.hide();
}
private handleRecommendationQueryError() {
this.dropdown.close();
this.dropdown.dropdownHeader.hide();
}
}