angular-bootstrap-scrolling-tabs
Version:
Angular directive for scrollable Bootstrap Tabs
130 lines (99 loc) • 4.16 kB
JavaScript
scrollingTabsModule.directive('scrollingTabs', scrollingTabsDirective);
scrollingTabsDirective.$inject = ['$timeout', '$sce'];
function scrollingTabsDirective($timeout, $sce) {
function sanitize (html) {
return $sce.trustAsHtml(html);
}
// ------------ Directive Object ---------------------------
return {
restrict: 'E',
template: scrollingTabsTemplate,
transclude: false,
replace: true,
scope: {
tabs: '@',
watchTabs: '=',
propPaneId: '@',
propTitle: '@',
propActive: '@',
propDisabled: '@',
localTabClick: '&tabClick',
refreshOn: '='
},
link: function(scope, element, attrs) {
var scrollingTabsControl = new ScrollingTabsControl(element, $timeout),
scrollToTabEdge = attrs.scrollToTabEdge && attrs.scrollToTabEdge.toLowerCase() === 'true';
scope.tabsArr = scope.$eval(scope.tabs);
scope.propPaneId = scope.propPaneId || 'paneId';
scope.propTitle = scope.propTitle || 'title';
scope.propActive = scope.propActive || 'active';
scope.propDisabled = scope.propDisabled || 'disabled';
scope.sanitize = sanitize;
element.on('click.scrollingTabs', '.nav-tabs > li', function __handleClickOnTab(e) {
var clickedTabElData = $(this).data();
scope.localTabClick({
$event: e,
$index: clickedTabElData.index,
tab: clickedTabElData.tab
});
});
if (attrs.refreshOn) {
scope.$watch('refreshOn', function (newVal, oldVal) {
if (newVal && newVal !== oldVal) {
scrollingTabsControl.handleTriggeredRefresh();
}
});
}
if (!attrs.watchTabs) {
// we're not watching the tabs array for changes so just init
// the tabs without adding a watch
scrollingTabsControl.initTabs({
isWrapperDirective: false,
scrollToTabEdge: scrollToTabEdge,
scrollingTabsActiveOffset: attrs.scrollingTabsActiveOffset
});
return;
}
// we're watching the tabs array for changes...
scope.$watch('watchTabs', function (latestTabsArray, prevTabsArray) {
var $activeTabLi,
activeIndex;
scope.tabsArr = scope.$eval(scope.tabs);
if (latestTabsArray.length && latestTabsArray[latestTabsArray.length - 1].active) { // new tab should be active
// the tab we just added should be active, so, after giving the
// elements time to render (thus the $timeout), force a click on it so
// bootstrap's built-in tab/pane activation can do its thing, otherwise
// the tab will show as active but its content pane won't be
$timeout(function () {
element.find('ul.nav-tabs > li:last').removeClass('active').find('a[role="tab"]').click();
}, 0);
} else { // --------- preserve the currently active tab
// we've replaced the nav-tabs so, to get the currently active tab
// we need to get it from the DOM because clicking a tab doesn't update
// the tabs array (Bootstrap's js just makes the clicked tab and its
// corresponding tab-content pane active); then we need to update
// the tabs array so it reflects the currently active tab before we
// call initTabs() because initTabs() generates the tab elements based
// on the array data.
// get the index of the currently active tab
$activeTabLi = element.find('.nav-tabs > li.active');
if ($activeTabLi.length) {
activeIndex = $activeTabLi.data('index');
scope.tabsArr.some(function __forEachTabsArrItem(t) {
if (t[scope.propActive]) {
t[scope.propActive] = false;
return true; // exit loop
}
});
scope.tabsArr[activeIndex][scope.propActive] = true;
}
}
scrollingTabsControl.initTabs({
isWrapperDirective: false,
isWatchingTabs: true,
scrollToTabEdge: scrollToTabEdge
});
}, true);
}
};
}