ng2-bootstrap
Version:
angular2 bootstrap components
309 lines (308 loc) • 13.5 kB
JavaScript
"use strict";
var core_1 = require('@angular/core');
var forms_1 = require('@angular/forms');
var typeahead_container_component_1 = require('./typeahead-container.component');
var typeahead_options_class_1 = require('./typeahead-options.class');
var typeahead_utils_1 = require('./typeahead-utils');
var Observable_1 = require('rxjs/Observable');
require('rxjs/add/observable/from');
require('rxjs/add/operator/debounceTime');
require('rxjs/add/operator/filter');
require('rxjs/add/operator/map');
require('rxjs/add/operator/mergeMap');
require('rxjs/add/operator/toArray');
var components_helper_service_1 = require('../utils/components-helper.service');
var typeahead_match_class_1 = require('./typeahead-match.class');
/* tslint:disable-next-line */
var KeyboardEvent = global.KeyboardEvent;
var TypeaheadDirective = (function () {
function TypeaheadDirective(control, viewContainerRef, element, renderer, componentsHelper) {
this.typeaheadLoading = new core_1.EventEmitter(false);
this.typeaheadNoResults = new core_1.EventEmitter(false);
this.typeaheadOnSelect = new core_1.EventEmitter(false);
this.typeaheadMinLength = void 0;
this.typeaheadAsync = void 0;
this.typeaheadLatinize = true;
this.typeaheadSingleWords = true;
this.typeaheadWordDelimiters = ' ';
this.typeaheadPhraseDelimiters = '\'"';
this.isTypeaheadOptionsListActive = false;
this.keyUpEventEmitter = new core_1.EventEmitter();
this.placement = 'bottom-left';
this.element = element;
this.ngControl = control;
this.viewContainerRef = viewContainerRef;
this.renderer = renderer;
this.componentsHelper = componentsHelper;
}
TypeaheadDirective.prototype.onChange = function (e) {
if (this.container) {
// esc
if (e.keyCode === 27) {
this.hide();
return;
}
// up
if (e.keyCode === 38) {
this.container.prevActiveMatch();
return;
}
// down
if (e.keyCode === 40) {
this.container.nextActiveMatch();
return;
}
// enter
if (e.keyCode === 13) {
this.container.selectActiveMatch();
return;
}
}
// For `<input>`s, use the `value` property. For others that don't have a
// `value` (such as `<span contenteditable="true">`, use `innerText`.
var value = e.target.value !== undefined ? e.target.value : e.target.innerText;
if (value.trim().length >= this.typeaheadMinLength) {
this.typeaheadLoading.emit(true);
this.keyUpEventEmitter.emit(e.target.value);
}
else {
this.typeaheadLoading.emit(false);
this.typeaheadNoResults.emit(false);
this.hide();
}
};
TypeaheadDirective.prototype.onFocus = function () {
if (this.typeaheadMinLength === 0) {
this.typeaheadLoading.emit(true);
this.keyUpEventEmitter.emit('');
}
};
TypeaheadDirective.prototype.onBlur = function () {
if (this.container && !this.container.isFocused) {
this.hide();
}
};
TypeaheadDirective.prototype.onKeydown = function (e) {
// no container - no problems
if (!this.container) {
return;
}
// if items is visible - prevent form submition
if (e.keyCode === 13) {
e.preventDefault();
return;
}
// if tab default browser behavior will select next input field, and therefore we should close the items list
if (e.keyCode === 9) {
this.hide();
return;
}
};
TypeaheadDirective.prototype.ngOnInit = function () {
this.typeaheadOptionsLimit = this.typeaheadOptionsLimit || 20;
this.typeaheadMinLength = this.typeaheadMinLength === void 0 ? 1 : this.typeaheadMinLength;
this.typeaheadWaitMs = this.typeaheadWaitMs || 0;
// async should be false in case of array
if (this.typeaheadAsync === undefined && !(this.typeahead instanceof Observable_1.Observable)) {
this.typeaheadAsync = false;
}
if (this.typeahead instanceof Observable_1.Observable) {
this.typeaheadAsync = true;
}
if (this.typeaheadAsync) {
this.asyncActions();
}
else {
this.syncActions();
}
};
TypeaheadDirective.prototype.changeModel = function (match) {
var valueStr = match.value;
this.ngControl.viewToModelUpdate(valueStr);
this.ngControl.control.setValue(valueStr);
this.hide();
};
Object.defineProperty(TypeaheadDirective.prototype, "matches", {
get: function () {
return this._matches;
},
enumerable: true,
configurable: true
});
TypeaheadDirective.prototype.show = function () {
var options = new typeahead_options_class_1.TypeaheadOptions({
typeaheadRef: this,
placement: this.placement,
animation: false
});
var binding = core_1.ReflectiveInjector.resolve([
{ provide: typeahead_options_class_1.TypeaheadOptions, useValue: options }
]);
this.popup = this.componentsHelper
.appendNextToLocation(typeahead_container_component_1.TypeaheadContainerComponent, this.viewContainerRef, binding);
this.popup.instance.position(this.viewContainerRef.element);
this.container = this.popup.instance;
this.container.parent = this;
// This improves the speed as it won't have to be done for each list item
var normalizedQuery = (this.typeaheadLatinize
? typeahead_utils_1.TypeaheadUtils.latinize(this.ngControl.control.value)
: this.ngControl.control.value).toString()
.toLowerCase();
this.container.query = this.typeaheadSingleWords
? typeahead_utils_1.TypeaheadUtils.tokenize(normalizedQuery, this.typeaheadWordDelimiters, this.typeaheadPhraseDelimiters)
: normalizedQuery;
this.container.matches = this._matches;
this.element.nativeElement.focus();
};
TypeaheadDirective.prototype.hide = function () {
if (this.container) {
this.popup.destroy();
this.container = void 0;
}
};
TypeaheadDirective.prototype.asyncActions = function () {
var _this = this;
this.keyUpEventEmitter
.debounceTime(this.typeaheadWaitMs)
.mergeMap(function () { return _this.typeahead; })
.subscribe(function (matches) {
_this.finalizeAsyncCall(matches);
}, function (err) {
console.error(err);
});
};
TypeaheadDirective.prototype.syncActions = function () {
var _this = this;
this.keyUpEventEmitter
.debounceTime(this.typeaheadWaitMs)
.mergeMap(function (value) {
var normalizedQuery = _this.normalizeQuery(value);
return Observable_1.Observable.from(_this.typeahead)
.filter(function (option) {
return option && _this.testMatch(_this.normalizeOption(option), normalizedQuery);
})
.toArray();
})
.subscribe(function (matches) {
_this.finalizeAsyncCall(matches);
}, function (err) {
console.error(err);
});
};
TypeaheadDirective.prototype.normalizeOption = function (option) {
var optionValue = typeahead_utils_1.TypeaheadUtils.getValueFromObject(option, this.typeaheadOptionField);
var normalizedOption = this.typeaheadLatinize ? typeahead_utils_1.TypeaheadUtils.latinize(optionValue) : optionValue;
return normalizedOption.toLowerCase();
};
TypeaheadDirective.prototype.normalizeQuery = function (value) {
// If singleWords, break model here to not be doing extra work on each iteration
var normalizedQuery = (this.typeaheadLatinize ? typeahead_utils_1.TypeaheadUtils.latinize(value) : value)
.toString()
.toLowerCase();
normalizedQuery = this.typeaheadSingleWords ?
typeahead_utils_1.TypeaheadUtils.tokenize(normalizedQuery, this.typeaheadWordDelimiters, this.typeaheadPhraseDelimiters) :
normalizedQuery;
return normalizedQuery;
};
TypeaheadDirective.prototype.testMatch = function (match, test) {
var spaceLength;
if (typeof test === 'object') {
spaceLength = test.length;
for (var i = 0; i < spaceLength; i += 1) {
if (test[i].length > 0 && match.indexOf(test[i]) < 0) {
return false;
}
}
return true;
}
else {
return match.indexOf(test) >= 0;
}
};
TypeaheadDirective.prototype.finalizeAsyncCall = function (matches) {
this.prepareMatches(matches);
this.typeaheadLoading.emit(false);
this.typeaheadNoResults.emit(!this.hasMatches());
if (!this.hasMatches()) {
this.hide();
return;
}
if (this.container) {
// This improves the speed as it won't have to be done for each list item
var normalizedQuery = (this.typeaheadLatinize
? typeahead_utils_1.TypeaheadUtils.latinize(this.ngControl.control.value)
: this.ngControl.control.value).toString()
.toLowerCase();
this.container.query = this.typeaheadSingleWords
? typeahead_utils_1.TypeaheadUtils.tokenize(normalizedQuery, this.typeaheadWordDelimiters, this.typeaheadPhraseDelimiters)
: normalizedQuery;
this.container.matches = this._matches;
}
else {
this.show();
}
};
TypeaheadDirective.prototype.prepareMatches = function (options) {
var _this = this;
var limited = options.slice(0, this.typeaheadOptionsLimit);
if (this.typeaheadGroupField) {
var matches_1 = [];
// extract all group names
var groups = limited
.map(function (option) { return typeahead_utils_1.TypeaheadUtils.getValueFromObject(option, _this.typeaheadGroupField); })
.filter(function (v, i, a) { return a.indexOf(v) === i; });
groups.forEach(function (group) {
// add group header to array of matches
matches_1.push(new typeahead_match_class_1.TypeaheadMatch(group, group, true));
// add each item of group to array of matches
matches_1 = matches_1.concat(limited
.filter(function (option) { return typeahead_utils_1.TypeaheadUtils.getValueFromObject(option, _this.typeaheadGroupField) === group; })
.map(function (option) { return new typeahead_match_class_1.TypeaheadMatch(option, typeahead_utils_1.TypeaheadUtils.getValueFromObject(option, _this.typeaheadOptionField)); }));
});
this._matches = matches_1;
}
else {
this._matches = limited.map(function (option) { return new typeahead_match_class_1.TypeaheadMatch(option, typeahead_utils_1.TypeaheadUtils.getValueFromObject(option, _this.typeaheadOptionField)); });
}
};
TypeaheadDirective.prototype.hasMatches = function () {
return this._matches.length > 0;
};
TypeaheadDirective.decorators = [
{ type: core_1.Directive, args: [{
/* tslint:disable */
selector: '[typeahead][ngModel],[typeahead][formControlName]'
},] },
];
/** @nocollapse */
TypeaheadDirective.ctorParameters = [
{ type: forms_1.NgControl, },
{ type: core_1.ViewContainerRef, },
{ type: core_1.ElementRef, },
{ type: core_1.Renderer, },
{ type: components_helper_service_1.ComponentsHelper, },
];
TypeaheadDirective.propDecorators = {
'typeaheadLoading': [{ type: core_1.Output },],
'typeaheadNoResults': [{ type: core_1.Output },],
'typeaheadOnSelect': [{ type: core_1.Output },],
'typeahead': [{ type: core_1.Input },],
'typeaheadMinLength': [{ type: core_1.Input },],
'typeaheadWaitMs': [{ type: core_1.Input },],
'typeaheadOptionsLimit': [{ type: core_1.Input },],
'typeaheadOptionField': [{ type: core_1.Input },],
'typeaheadGroupField': [{ type: core_1.Input },],
'typeaheadAsync': [{ type: core_1.Input },],
'typeaheadLatinize': [{ type: core_1.Input },],
'typeaheadSingleWords': [{ type: core_1.Input },],
'typeaheadWordDelimiters': [{ type: core_1.Input },],
'typeaheadPhraseDelimiters': [{ type: core_1.Input },],
'typeaheadItemTemplate': [{ type: core_1.Input },],
'onChange': [{ type: core_1.HostListener, args: ['keyup', ['$event'],] },],
'onFocus': [{ type: core_1.HostListener, args: ['focus',] },],
'onBlur': [{ type: core_1.HostListener, args: ['blur',] },],
'onKeydown': [{ type: core_1.HostListener, args: ['keydown', ['$event'],] },],
};
return TypeaheadDirective;
}());
exports.TypeaheadDirective = TypeaheadDirective;