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