UNPKG

coveo-search-ui

Version:

Coveo JavaScript Search Framework

289 lines (243 loc) • 11 kB
import { CategoryFacet } from './CategoryFacet'; import { FacetSearchElement } from '../Facet/FacetSearchElement'; import { pluck, debounce, last } from 'underscore'; import { $$, Dom } from '../../utils/Dom'; import { SVGDom } from '../../utils/SVGDom'; import { SVGIcons } from '../../utils/SVGIcons'; import { l } from '../../strings/Strings'; import { IGroupByValue } from '../../rest/GroupByValue'; import 'styling/_CategoryFacetSearch'; import { StringUtils } from '../../utils/StringUtils'; import { analyticsActionCauseList, IAnalyticsCategoryFacetMeta } from '../Analytics/AnalyticsActionListMeta'; import { IFacetSearch } from '../Facet/IFacetSearch'; import { IIndexFieldValue } from '../../rest/FieldValue'; import { AccessibleButton } from '../../utils/AccessibleButton'; import * as Globalize from 'globalize'; export class CategoryFacetSearch implements IFacetSearch { public container: Dom | undefined; public facetSearchElement: FacetSearchElement; public displayNewValues: () => void; public currentlyDisplayedResults: string[]; public moreValuesToFetch = true; public facetSearchPromise: Promise<IIndexFieldValue[]>; private numberOfValuesToFetch: number; constructor(private categoryFacet: CategoryFacet, private displayButton = true) { this.facetSearchElement = new FacetSearchElement(this); this.displayNewValues = debounce(this.getDisplayNewValuesFunction(), this.categoryFacet.options.facetSearchDelay); this.categoryFacet.root.addEventListener('click', (e: MouseEvent) => this.handleClickElsewhere(e)); this.numberOfValuesToFetch = this.categoryFacet.options.numberOfResultsInFacetSearch; } public get facetType() { return CategoryFacet.ID; } public get facetTitle() { return this.categoryFacet.options.title || this.categoryFacet.options.field.toString(); } public setExpandedFacetSearchAccessibilityAttributes(searchResultsElements: HTMLElement) { this.container.setAttribute('aria-expanded', 'true'); } public setCollapsedFacetSearchAccessibilityAttributes() { this.container.setAttribute('aria-expanded', 'false'); } public build() { this.container = $$('div', { className: 'coveo-category-facet-search-container', role: 'button' }); this.container.toggleClass('coveo-category-facet-search-without-button', !this.displayButton); this.displayButton && this.buildButton(); this.container.append(this.facetSearchElement.build()); $$(this.facetSearchElement.search).toggleClass('without-animation', !this.displayButton); return this.container; } private buildButton() { new AccessibleButton() .withElement(this.container) .withSelectAction(() => { $$(this.categoryFacet.element).addClass('coveo-category-facet-searching'); this.focus(); }) .withLabel(l('SearchFacetResults', this.facetTitle)) .build(); this.buildfacetSearchPlaceholder(); } public focus() { this.facetSearchElement.focus(); } public clear() { this.dismissSearchResults(); this.container && this.container.detach(); } public dismissSearchResults() { this.removeNoResultsCssClasses(); $$(this.categoryFacet.element).removeClass('coveo-category-facet-searching'); $$(this.facetSearchElement.searchResults).empty(); this.facetSearchElement.clearSearchInput(); this.facetSearchElement.hideSearchResultsElement(); this.currentlyDisplayedResults = null; this.numberOfValuesToFetch = this.categoryFacet.options.numberOfResultsInFacetSearch; this.moreValuesToFetch = true; } public keyboardEventDefaultHandler() { this.moreValuesToFetch = true; this.displayNewValues(); } public keyboardNavigationEnterPressed() { this.selectCurrentResult(); } public fetchMoreValues() { this.numberOfValuesToFetch += this.categoryFacet.options.numberOfResultsInFacetSearch; this.displayNewValues(); } public getCaptions() { const searchResults = $$(this.facetSearchElement.searchResults); const captions = searchResults .findAll('.coveo-category-facet-search-value-caption') .concat(searchResults.findAll('.coveo-category-facet-search-path-parents')) .concat(searchResults.findAll('.coveo-category-facet-search-path-last-value')); return captions; } public updateAriaLive(text: string) { this.categoryFacet.searchInterface.ariaLive.updateText(text); } private selectCurrentResult() { if (this.facetSearchElement.currentResult) { const currentResultPathData = this.facetSearchElement.currentResult.el.dataset.path; const delimiter = this.categoryFacet.options.delimitingCharacter; const path = currentResultPathData.split(delimiter); this.categoryFacet.changeActivePath(path); this.categoryFacet.logAnalyticsEvent(analyticsActionCauseList.categoryFacetSelect, path); this.categoryFacet.executeQuery(); this.categoryFacet.scrollToTop(); } } private handleClickElsewhere(e: MouseEvent) { const closestContainer = $$(<HTMLElement>e.target).closest('.coveo-category-facet-search-container'); const isSelfContainer = this.container && closestContainer === this.container.el; if (!closestContainer || !isSelfContainer) { this.dismissSearchResults(); } } private buildfacetSearchPlaceholder() { const placeholder = $$('div', { className: 'coveo-category-facet-search-placeholder' }); const icon = $$('div', { className: 'coveo-category-facet-search-icon' }, SVGIcons.icons.checkboxHookExclusionMore); SVGDom.addClassToSVGInContainer(icon.el, 'coveo-category-facet-search-icon-svg'); const label = $$('span', { className: 'coveo-category-facet-search-label' }, l('Search')); placeholder.append(icon.el); placeholder.append(label.el); this.container.append(placeholder.el); } private getDisplayNewValuesFunction() { return async () => { this.facetSearchElement.showFacetSearchWaitingAnimation(); this.categoryFacet.logger.info('Triggering new Category Facet search'); const categoryFacetValues = await this.categoryFacet.categoryFacetQueryController.searchFacetValues( this.facetSearchElement.input.value, this.numberOfValuesToFetch ); this.logAnalyticsEvent(); if (categoryFacetValues.length < this.numberOfValuesToFetch) { this.moreValuesToFetch = false; } if (categoryFacetValues.length == 0) { this.noFacetSearchResults(); return; } this.removeNoResultsCssClasses(); this.setFacetSearchResults(categoryFacetValues); if (this.shouldPositionSearchResults) { this.facetSearchElement.positionSearchResults(); } this.facetSearchElement.hideFacetSearchWaitingAnimation(); }; } private setFacetSearchResults(categoryFacetValues: IGroupByValue[]) { $$(this.facetSearchElement.searchResults).empty(); this.currentlyDisplayedResults = pluck(categoryFacetValues, 'value'); for (let i = 0; i < categoryFacetValues.length; i++) { const searchResult = this.buildFacetSearchValue(categoryFacetValues[i], i); if (i == 0) { this.facetSearchElement.setAsCurrentResult(searchResult); } this.facetSearchElement.appendToSearchResults(searchResult.el); } this.highlightCurrentQueryWithinSearchResults(); this.facetSearchElement.updateAriaLiveWithResults( this.facetSearchElement.input.value, this.currentlyDisplayedResults.length, this.moreValuesToFetch ); } private getFormattedCount(count: number) { return Globalize.format(count, 'n0'); } private buildFacetSearchValue(categoryFacetValue: IGroupByValue, index: number) { const path = categoryFacetValue.value.split(this.categoryFacet.options.delimitingCharacter); const pathParents = path.slice(0, -1).length != 0 ? `${path.slice(0, -1).join('/')}/` : ''; const value = $$('span', { className: 'coveo-category-facet-search-value-caption' }, last(path)); const number = $$( 'span', { className: 'coveo-category-facet-search-value-number' }, this.getFormattedCount(categoryFacetValue.numberOfResults) ); const pathParentsCaption = $$('span', { className: 'coveo-category-facet-search-path-parents' }, pathParents); const pathToValueCaption = $$('span', { className: 'coveo-category-facet-search-path' }, pathParentsCaption); const firstRow = $$('div', { className: 'coveo-category-facet-search-first-row' }, value, number); const secondRow = $$('div', { className: 'coveo-category-facet-search-second-row' }, pathToValueCaption); const item = $$( 'li', { id: `coveo-category-facet-search-suggestion-${index}`, role: 'option', ariaSelected: 'false', className: 'coveo-category-facet-search-value', title: path }, firstRow, secondRow ); item.el.dataset.path = categoryFacetValue.value; const countLabel = l('ResultCount', this.getFormattedCount(categoryFacetValue.numberOfResults), categoryFacetValue.numberOfResults); const label = l('IncludeValueWithResultCount', last(path), countLabel); new AccessibleButton() .withElement(item) .withSelectAction(() => { this.categoryFacet.changeActivePath(path); this.categoryFacet.scrollToTop(); this.categoryFacet.logAnalyticsEvent(analyticsActionCauseList.categoryFacetSelect, path); this.categoryFacet.executeQuery(); }) .withLabel(label) .build(); return item; } private noFacetSearchResults() { this.facetSearchElement.hideFacetSearchWaitingAnimation(); this.facetSearchElement.emptyAndShowNoResults(); $$(this.facetSearchElement.search).addClass('coveo-facet-search-no-results'); $$(this.categoryFacet.element).addClass('coveo-no-results'); } private removeNoResultsCssClasses() { this.facetSearchElement.search && $$(this.facetSearchElement.search).removeClass('coveo-facet-search-no-results'); $$(this.categoryFacet.element).removeClass('coveo-no-results'); } private highlightCurrentQueryWithinSearchResults() { const regex = new RegExp(`(${StringUtils.stringToRegex(this.facetSearchElement.input.value, true)})`, 'ig'); this.facetSearchElement.highlightCurrentQueryInSearchResults(regex); } private logAnalyticsEvent() { this.categoryFacet.usageAnalytics.logCustomEvent<IAnalyticsCategoryFacetMeta>( analyticsActionCauseList.categoryFacetSearch, { categoryFacetId: this.categoryFacet.options.id, categoryFacetField: this.categoryFacet.options.field.toString(), categoryFacetTitle: this.categoryFacet.options.title }, this.categoryFacet.root ); } private get shouldPositionSearchResults() { const searchResults = this.facetSearchElement.searchResults; return searchResults && !searchResults.parentElement; } }