stf-angular-select
Version:
Custom reusable select for angular with wide posibilities
402 lines (401 loc) • 19.3 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
var Observable_1 = require("rxjs/Observable");
require("rxjs/add/operator/map");
require("rxjs/add/operator/throttleTime");
require("rxjs/add/operator/debounceTime");
require("rxjs/add/observable/fromEvent");
var _ = require("lodash");
var StfSelectDirective = (function () {
function StfSelectDirective($translate, $window, $compile, _NP_STF_SELECT_THROTTLE_TIME) {
this.$translate = $translate;
this.$window = $window;
this.$compile = $compile;
this._NP_STF_SELECT_THROTTLE_TIME = _NP_STF_SELECT_THROTTLE_TIME;
this._getScope = function () { return null; };
this.template = "\n<section class=\"stf-select\" ng-class=\"{'stf-select_has-value': ngModel? true : false, 'stf-select_focused': focused, 'stf-select_disabled': disabled || ngDisabled}\">\n <div class=\"stf-select__label\" ng-transclude=\"label\"></div>\n <section class=\"stf-select__container\" > \n <section class=\"stf-select__inner-wrapper\">\n <div class=\"stf-select__value\" ng-transclude=\"value\"></div>\n <div class=\"stf-select__placeholder\" ng-transclude=\"label\"></div>\n <div class=\"stf-select__icon\"></div>\n </section>\n\n <section class=\"stf-select__search-input\" tabindex=\"{{ngDisabled ? '-1' : '0'}}\" ng-transclude=\"searchInput\"></section>\n <section class=\"stf-select__options\">\n <div class=\"stf-select__fixed-option sss\"></div> \n </section>\n </section>\n</section>\n ";
this.restrict = 'E';
this.require = "ngModel";
this.scope = {
fixNgModal: "<",
ngDisabled: "<",
disabled: "<",
optionsClass: "<"
};
this.transclude = {
label: 'stfSelectLabel',
value: 'stfSelectValue',
searchInput: '?stfSearchInput',
};
}
StfSelectDirective.prototype.link = function (scope, element, attributes, ngModelController, transcludeFn) {
var _this = this;
if (scope.fixNgModal)
mdFixes();
element.addClass("stf-select-root");
var $mainContainer = $(window);
var openScrollTimerId;
var jqSelectOptions = element.find('.stf-select__options');
var placeHolder = element.find('.stf-select__placeholder');
var self = this;
var elementChildren = element.children('.stf-select');
var valueClicked = false;
render();
ngModelController.$render = render;
scope.selectId = Math.round(Math.random() * 100000000000);
element.attr('data-stf-select-id', scope.selectId);
var $body = $('body');
var $searchInputContainer = element.find('.stf-select__search-input');
var elemetClickSubscription = Observable_1.Observable.fromEvent($searchInputContainer, 'click')
.subscribe(function (event) {
event.stopPropagation();
});
$searchInputContainer.delegate("input", "keydown", function (event) {
var inputs = $searchInputContainer.find('input');
var keyCode = event.keyCode || event.which;
if (inputs.length > 1) {
if (event.target !== inputs[inputs.length - 1] && keyCode === 9) {
event.stopPropagation();
}
}
});
var $searchInputContainerKeyDownSubscription = Observable_1.Observable.fromEvent($searchInputContainer, 'keydown')
.subscribe(function (event) {
var keyCode = event.keyCode || event.which;
if (scope.focused) {
switch (keyCode) {
case 9:
hideDropDown();
break;
case 40:
selectKeyDownPressed();
break;
case 38:
selectKeyUpPressed();
break;
case 27:
$searchInputContainer.focus();
hideDropDown();
event.preventDefault();
event.stopPropagation();
break;
case 13:
if (keyCode === 13) {
event.stopPropagation();
var optionForClick = $("#stf-select-optins-" + scope.selectId + " .stf-select-option__selected");
if (!optionForClick.length) {
optionForClick = $("#stf-select-optins-" + scope.selectId + " .stf-select-option").first();
}
optionForClick.click();
return;
}
break;
}
}
else {
switch (keyCode) {
case 13:
showDropDown();
break;
}
}
});
var $searchInputFocusSubscription = Observable_1.Observable.fromEvent($searchInputContainer, 'focus')
.subscribe(function (event) {
elementChildren.addClass('stf-select__tab-focus');
});
var $searchInputBlurSubscription = Observable_1.Observable.fromEvent($searchInputContainer, 'blur')
.subscribe(function (event) {
elementChildren.removeClass('stf-select__tab-focus');
});
var $searchInputKePressSubscription = Observable_1.Observable.fromEvent($searchInputContainer, 'click')
.subscribe(function (event) {
showDropDown();
});
var valueContainer = element.find('.stf-select__inner-wrapper');
var valueContainerObservable = Observable_1.Observable.fromEvent(valueContainer, 'click').throttleTime(this._NP_STF_SELECT_THROTTLE_TIME);
var valueContainerSubscription = valueContainerObservable.subscribe(function (event) {
if (!scope.disabled && !scope.ngDisabled) {
scope.focused = true;
scope.$apply();
}
});
var iconEl = element.find('.stf-select__icon');
var iconElObservable = Observable_1.Observable.fromEvent(iconEl, 'click').throttleTime(this._NP_STF_SELECT_THROTTLE_TIME);
var iconElSubscription = iconElObservable.subscribe(function (event) {
if (scope.focused) {
event.stopPropagation();
scope.focused = false;
scope.$apply();
}
});
var windowClickObservable = Observable_1.Observable.fromEvent(this.$window, 'click').throttleTime(100);
var windowClickSubscription = windowClickObservable.subscribe(function (event) {
if (!$(event.target).closest("[data-stf-select-id=" + scope.selectId + "]").length) {
scope.focused = false;
scope.$applyAsync();
}
});
var windowResizeObservable = Observable_1.Observable.fromEvent(this.$window, 'resize').throttleTime(100);
var windowResizeSubscription = windowResizeObservable.subscribe(function (event) { return calculatePositionAnsSize(); });
var scrollListener = _.debounce(function () {
calculatePositionAnsSize();
}, 100);
document.addEventListener('scroll', scrollListener, true);
var elementMouseWheelObservable = Observable_1.Observable.fromEvent(element, "mousewheel");
var elementMouseWheelSubscription = elementMouseWheelObservable.subscribe(function (event) {
if (scope.focused) {
event.stopPropagation();
event.returnValue = false;
}
});
var stfSelectOption = element.find('.stf-select-option');
scope.$on('stf-select-option.selected', function (event, value) {
var old = ngModelController.$viewValue;
scope.focused = false;
if (old !== value) {
ngModelController.$setViewValue(value);
scope.ngModel = value;
scope.$broadcast('stf-select.value_changed', value);
}
setTimeout(function () { return $searchInputContainer.focus(); }, 100);
});
scope.$watch('focused', function (newValue, oldValue) {
if (scope.focused) {
if (element.find('.stf-select__search-input input').length) {
setTimeout(function () { return scope.focused && element.find('.stf-select__search-input input').first().focus(); }, 200);
}
else if ($searchInputContainer.length) {
setTimeout(function () { return scope.focused && $searchInputContainer.first().focus(); }, 200);
}
scrollUnscrollContainers();
calculatePositionAnsSize();
}
else {
scrollUnscrollContainers();
}
});
var options;
var fixedOpt;
var jOptins;
var jOptinsParent;
var transcludedScope;
transcludeFn(function (transcludeEls, scopeTr) {
_.each(transcludeEls, function (el) {
if (el.tagName === "STF-SELECT-OPTIONS") {
options = el;
}
if (el.tagName === "STF-FIXED-OPTION") {
fixedOpt = el;
}
});
transcludedScope = scopeTr;
$('body').append(_this.$compile("\n <div ng-attr-class=\"{{optionsClass}}\">\n <div ng-class=\"{'stf-select_has-value': ngModel? true : false, \n 'stf-select_focused': focused, 'stf-select_disabled': disabled || ngDisabled}\">\n <div class=\"stf-select__options\" id=\"stf-select-optins-" + scope.selectId + "\"></div>\n </div>\n </div>\n ")(scope));
jOptins = $("#stf-select-optins-" + scope.selectId);
jOptins.append(options);
jOptins.append('<div tabindex="0" class="stf-select__fixed-option"></div>');
jOptins.children('.stf-select__fixed-option').append(fixedOpt);
jOptinsParent = jOptins.parent();
jOptins.delegate('.stf-select-option', "keydown", (function (event) {
var _this = this;
var keyCode = event.keyCode || event.which;
switch (keyCode) {
case 13:
setTimeout(function () { return _this.click(); }, 100);
break;
case 9:
event.preventDefault();
event.stopPropagation();
element.find('.stf-select__search-input').focus();
hideDropDown();
break;
case 40:
{
optionKeyDown($(event.target));
event.preventDefault();
}
break;
case 38:
{
optionKeyUp($(event.target));
event.preventDefault();
}
break;
case 27:
element.find('.stf-select__search-input').focus();
hideDropDown();
event.preventDefault();
event.stopPropagation();
break;
}
}));
jOptins.find('.stf-select__fixed-option').keydown(function (event) {
var keyCode = event.keyCode || event.which;
switch (keyCode) {
case 13:
{
jOptins.find('stf-fixed-option').click();
hideDropDown();
}
break;
case 38:
fixedKeyUpPressed();
break;
case 40:
fixedKeyDownPressed();
break;
case 9:
hideDropDown();
break;
case 27:
hideDropDown();
event.preventDefault();
event.stopPropagation();
break;
}
});
});
scope.$on('$destroy', function () {
valueContainerSubscription.unsubscribe();
windowClickSubscription.unsubscribe();
windowResizeSubscription.unsubscribe();
jOptinsParent.parent().remove();
elementMouseWheelSubscription.unsubscribe();
elemetClickSubscription.unsubscribe();
iconElSubscription.unsubscribe();
document.removeEventListener('scroll', scrollListener, true);
$('body, .modal-content').css('overflow-y', '');
clearTimeout(openScrollTimerId);
$searchInputFocusSubscription.unsubscribe();
$searchInputKePressSubscription.unsubscribe();
transcludedScope.$destroy();
});
function calculatePositionAnsSize() {
if (!scope.focused) {
return;
}
var elOffset = element.children().offset();
jOptinsParent.width(elementChildren.width());
jOptins.width(elementChildren.width());
console.log($mainContainer.scrollTop());
if ((jqSelectOptions.offset().top - $mainContainer.scrollTop()
+ jOptins.height() + 10)
> (window.innerHeight
|| document.documentElement.clientHeight
|| document.body.clientHeight)) {
jOptinsParent.addClass('top');
jOptins.css('top', elOffset.top - jOptins.height() - 28);
}
else {
jOptinsParent.removeClass('top');
jOptins.css('top', jqSelectOptions.offset().top);
}
jOptins.css('left', elOffset.left);
}
function mdFixes() {
}
function scrollUnscrollContainers() {
if (scope.focused) {
var jqContainer_1 = $('body, .modal-content');
jqContainer_1.css('overflow-y', 'hidden');
openScrollTimerId = setTimeout(function () { return jqContainer_1.css('overflow-y', 'hidden'); }, 200);
}
else {
$('body, .modal-content').css('overflow-y', '');
}
}
element;
function showDropDown() {
if (!scope.disabled && !scope.ngDisabled) {
valueClicked = true;
scope.focused = true;
scope.$apply();
}
}
function hideDropDown() {
scope.focused = false;
jOptinsParent.width(0);
setTimeout(function () {
scope.focused || scope.$applyAsync();
}, 0);
}
function selectKeyDownPressed() {
var elementForFocus = jOptins.find('.stf-select-option').first();
if (!elementForFocus.length) {
elementForFocus = jOptins.find('.stf-select__fixed-option');
}
setTimeout(function () { return elementForFocus.focus(); }, 100);
}
function selectKeyUpPressed() {
var elementForFocus = jOptins.find('.stf-select__fixed-option').last();
if (!elementForFocus.children().length)
elementForFocus.length = 0;
if (!elementForFocus.length) {
elementForFocus = jOptins.find('.stf-select-option').last();
}
setTimeout(function () { return elementForFocus.focus(); }, 100);
}
function fixedKeyUpPressed() {
var elementForFocus = jOptins.find('.stf-select-option').last();
if (!elementForFocus.length) {
elementForFocus = $searchInputContainer.find('input').length ? $searchInputContainer.find('input').first() : $searchInputContainer;
}
setTimeout(function () { return elementForFocus.focus(); }, 100);
}
function fixedKeyDownPressed() {
var elementForFocus = $searchInputContainer.find('input').length ? $searchInputContainer.find('input').first() : jOptins.find('.stf-select-option').first();
setTimeout(function () { return elementForFocus.focus(); }, 100);
}
function optionKeyDown(element) {
var elementParentFocus = element.parent().next();
var elementForFocus = elementParentFocus.children();
if (!elementForFocus.length) {
elementParentFocus = elementParentFocus.next();
elementForFocus = elementParentFocus.children();
}
if (!elementForFocus.length) {
var fixed = jOptins.find('.stf-select__fixed-option');
if (fixed.children().length)
elementForFocus = jOptins.find('.stf-select__fixed-option');
}
if (!elementForFocus.length) {
elementForFocus = $searchInputContainer.find('input').first();
}
if (!elementForFocus.length) {
elementForFocus = jOptins.find('.stf-select-option').first();
}
elementForFocus.focus();
}
function optionKeyUp(element) {
var elementParentFocus = element.parent().prev();
var elementForFocus = elementParentFocus.children();
if (!elementForFocus.length) {
elementParentFocus = elementParentFocus.prev();
elementForFocus = elementParentFocus.children();
}
if (!elementForFocus.length) {
elementForFocus = $searchInputContainer.find('input').first();
}
if (!elementForFocus.length) {
var fixed = jOptins.find('.stf-select__fixed-option');
if (fixed.children().length)
elementForFocus = jOptins.find('.stf-select__fixed-option');
}
if (!elementForFocus.length) {
elementForFocus = jOptins.find('.stf-select-option').last();
}
elementForFocus.focus();
}
function render() {
scope.ngModel = ngModelController.$viewValue;
scope.$broadcast('stf-select.value_changed', scope.ngModel);
return scope.ngModel;
}
};
StfSelectDirective.Factory = function ($translate, $window, $compile, NP_STF_SELECT_THROTTLE_TIME) {
return new StfSelectDirective($translate, $window, $compile, NP_STF_SELECT_THROTTLE_TIME);
};
return StfSelectDirective;
}());
exports.StfSelectDirective = StfSelectDirective;
StfSelectDirective.Factory.$inject = ['$translate', "$window", "$compile", "NP_STF_SELECT_THROTTLE_TIME"];