UNPKG

coveo-search-ui

Version:

Coveo JavaScript Search Framework

151 lines (124 loc) 4.2 kB
import { $$ } from '../../utils/Dom'; import { ComboboxInput } from './ComboboxInput'; import { uniqueId, throttle } from 'underscore'; import { SVGIcons } from '../../utils/SVGIcons'; import { SVGDom } from '../../utils/SVGDom'; import { ComboboxValues } from './ComboboxValues'; import 'styling/_Combobox'; import { Utils } from '../../utils/Utils'; import { l } from '../../strings/Strings'; import { IComboboxOptions, ICombobox, IComboboxAccessibilityAttributes } from './ICombobox'; export class Combobox implements ICombobox { public element: HTMLElement; public id: string; public values: ComboboxValues; private input: ComboboxInput; private waitAnimationElement: HTMLElement; private defaultOptions: Partial<IComboboxOptions> = { wrapperClassName: '', clearOnBlur: false }; private throttlingDelay = 600; private isRequestCancelled = false; constructor(public options: IComboboxOptions) { this.options = { ...this.defaultOptions, ...this.options }; this.id = uniqueId('coveo-combobox-'); this.create(); } private create() { this.element = $$('div', { className: `coveo-combobox-wrapper ${this.options.wrapperClassName}` }).el; this.createAndAppendInput(); this.createAndAppendWaitAnimation(); this.createAndAppendValues(); } private createAndAppendInput() { this.input = new ComboboxInput(this); this.element.appendChild(this.input.element); } private createAndAppendWaitAnimation() { this.waitAnimationElement = $$('div', { className: 'coveo-combobox-wait-animation' }, SVGIcons.icons.loading).el; SVGDom.addClassToSVGInContainer(this.waitAnimationElement, 'coveo-combobox-wait-animation-svg'); this.toggleWaitAnimation(false); this.input.element.appendChild(this.waitAnimationElement); } private toggleWaitAnimation(show: boolean) { $$(this.waitAnimationElement).toggle(show); } private createAndAppendValues() { this.values = new ComboboxValues(this); this.element.appendChild(this.values.element); } public clearAll() { this.clearValues(); this.input.clearInput(); } private clearValues() { this.values.clearValues(); this.cancelRequest(); } private cancelRequest() { this.toggleWaitAnimation(false); this.throttledRequest.cancel(); this.isRequestCancelled = true; } public onInputChange(value: string) { if (Utils.isEmptyString(value)) { return this.clearValues(); } this.throttledRequest( () => this.options.requestValues(value), () => this.values.resetScroll() ); } public onInputBlur() { if (this.values.mouseIsOverValue) { return; } if (this.values.isRenderingNewValues) { return; } if (this.options.clearOnBlur) { return this.clearAll(); } this.clearValues(); } public updateAccessibilityAttributes(attributes: IComboboxAccessibilityAttributes) { this.input.updateAccessibilityAttributes(attributes); } public updateAriaLive() { if (!this.values.hasValues()) { this.options.ariaLive.updateText(l('NoValuesFound')); return; } let text = l('ShowingResultsWithQuery', this.values.numberOfValues, this.input.value, this.values.numberOfValues); if (this.options.scrollable && this.options.scrollable.areMoreValuesAvailable()) { text = `${text} (${l('MoreValuesAvailable')})`; } this.options.ariaLive.updateText(text); } public onScrollEndReached() { this.values.saveFocusedValue(); this.options.scrollable && this.throttledRequest( () => this.options.scrollable.requestMoreValues(), () => this.values.restoreFocusedValue() ); } private throttledRequest = throttle(this.triggerRequest, this.throttlingDelay, { leading: true, trailing: true }); private async triggerRequest(request: () => Promise<any>, callback?: Function) { this.isRequestCancelled = false; this.toggleWaitAnimation(true); const response = await request(); this.toggleWaitAnimation(false); if (!this.isRequestCancelled) { this.values.renderFromResponse(response); callback && callback(); } } }