coveo-search-ui
Version:
Coveo JavaScript Search Framework
204 lines (180 loc) • 5.51 kB
text/typescript
import { exportGlobally } from '../../GlobalExports';
import { SVGIcons } from '../../utils/SVGIcons';
import { $$, Dom } from '../../utils/Dom';
import { KEYBOARD } from '../../utils/KeyboardUtils';
import { IFormWidget, IFormWidgetSettable } from './FormWidgets';
import { SVGDom } from '../../utils/SVGDom';
export interface ITextInputOptions {
/**
* Whether to add a placeholder to the `TextInput` rather than a label.
*
* **Default:** `false`
*/
usePlaceholder?: boolean;
/**
* The class name to add to the `TextInput`'s HTML element.
*
* **Default:** `coveo-input`
*/
className?: string;
/**
* Whether to trigger the `TextInput`'s `onChange` function on every key press
* rather than when the enter key is pressed or the input is blurred.
*
* **Default:** `false`
*/
triggerOnChangeAsYouType?: boolean;
/**
* Whether to set the required attribute to `true` or `false`.
*
* **Default:** `true`
*/
isRequired?: boolean;
/**
* A custom aria-label attribute to add to the `TextInput`'s HTML element.
*/
ariaLabel?: string;
/**
* The name of an icon to display inside the input at its beginning.
*
* **Default:** `undefined`
*/
icon?: string;
}
const defaultOptions: ITextInputOptions = {
usePlaceholder: false,
className: 'coveo-input',
triggerOnChangeAsYouType: false,
isRequired: true
};
/**
* A text input widget with standard styling.
*/
export class TextInput implements IFormWidget, IFormWidgetSettable {
private element: HTMLElement;
private input: Dom;
private icon: Dom;
private lastQueryText: string = '';
static doExport() {
exportGlobally({
TextInput: TextInput
});
}
/**
* Creates a new `TextInput`.
* @param onChange The function to call when the value entered in the text input changes. This function takes the
* current `TextInput` instance as an argument.
* @param name The text to display in the text input label or placeholder.
*/
constructor(
public onChange: (textInput: TextInput) => void = (textInput: TextInput) => {},
public name?: string,
private options?: ITextInputOptions
) {
this.options = {
...defaultOptions,
...this.options
};
this.buildContent();
this.buildIcon();
}
/**
* Gets the element on which the text input is bound.
* @returns {HTMLElement} The text input element.
*/
public getElement(): HTMLElement {
return this.element;
}
/**
* Gets the value currently entered in the text input.
* @returns {string} The text input current value.
*/
public getValue(): string {
return (<HTMLInputElement>$$(this.element).find('input')).value;
}
/**
* Sets the value in the text input.
* @param value The value to set the text input to.
*/
public setValue(value: string) {
const currentValue = this.getValue();
(<HTMLInputElement>$$(this.element).find('input')).value = value;
if (currentValue != value) {
this.onChange(this);
}
this.lastQueryText = this.getInput().value;
}
/**
* Resets the text input.
*/
public reset() {
const currentValue = this.getValue();
(<HTMLInputElement>$$(this.element).find('input')).value = '';
if (currentValue != '') {
this.onChange(this);
}
this.lastQueryText = this.getInput().value;
}
/**
* Gets the element on which the text input is bound.
* @returns {HTMLElement} The text input element.
*/
public build() {
return this.element;
}
/**
* Gets the `input` element (the text input itself).
* @returns {HTMLElement} The `input` element.
*/
public getInput(): HTMLInputElement {
return <HTMLInputElement>$$(this.element).find('input');
}
private buildContent() {
this.element = $$('div', { className: this.options.className }).el;
this.input = $$('input', { type: 'text', autocomplete: 'off' });
this.options.isRequired && this.input.setAttribute('required', 'true');
this.options.ariaLabel && this.input.setAttribute('aria-label', this.options.ariaLabel);
this.addEventListeners();
this.element.appendChild(this.input.el);
this.name && this.createLabelOrPlaceholder();
}
private buildIcon() {
if (!this.options.icon) {
return;
}
const iconClassName = `${this.options.className}-icon`;
this.icon = $$('span', { className: iconClassName });
this.icon.setHtml(SVGIcons.icons[this.options.icon]);
SVGDom.addClassToSVGInContainer(this.icon.el, `${iconClassName}-svg`);
$$(this.element).prepend(this.icon.el);
}
private addEventListeners() {
this.options.triggerOnChangeAsYouType ? this.addOnTypeEventListener() : this.addOnChangeEventListener();
}
private addOnChangeEventListener() {
this.input.on(['keydown', 'blur'], (e: Event) => {
if (e.type == 'blur' || (<KeyboardEvent>e).keyCode == KEYBOARD.ENTER) {
this.triggerChange();
}
});
}
private addOnTypeEventListener() {
this.input.on(['keyup'], () => {
this.triggerChange();
});
}
private createLabelOrPlaceholder() {
if (this.options.usePlaceholder) {
return this.input.setAttribute('placeholder', this.name);
}
const label = $$('label');
label.text(this.name);
this.element.appendChild(label.el);
}
private triggerChange() {
if (this.lastQueryText != this.getInput().value) {
this.onChange(this);
this.lastQueryText = this.getInput().value;
}
}
}