angular-ui-bootstrap
Version:
Native AngularJS (Angular) directives for Bootstrap
237 lines (211 loc) • 6.3 kB
JavaScript
angular.module('ui.bootstrap.tabs', [])
.controller('UibTabsetController', ['$scope', function ($scope) {
var ctrl = this,
oldIndex;
ctrl.tabs = [];
ctrl.select = function(index, evt) {
if (!destroyed) {
var previousIndex = findTabIndex(oldIndex);
var previousSelected = ctrl.tabs[previousIndex];
if (previousSelected) {
previousSelected.tab.onDeselect({
$event: evt,
$selectedIndex: index
});
if (evt && evt.isDefaultPrevented()) {
return;
}
previousSelected.tab.active = false;
}
var selected = ctrl.tabs[index];
if (selected) {
selected.tab.onSelect({
$event: evt
});
selected.tab.active = true;
ctrl.active = selected.index;
oldIndex = selected.index;
} else if (!selected && angular.isDefined(oldIndex)) {
ctrl.active = null;
oldIndex = null;
}
}
};
ctrl.addTab = function addTab(tab) {
ctrl.tabs.push({
tab: tab,
index: tab.index
});
ctrl.tabs.sort(function(t1, t2) {
if (t1.index > t2.index) {
return 1;
}
if (t1.index < t2.index) {
return -1;
}
return 0;
});
if (tab.index === ctrl.active || !angular.isDefined(ctrl.active) && ctrl.tabs.length === 1) {
var newActiveIndex = findTabIndex(tab.index);
ctrl.select(newActiveIndex);
}
};
ctrl.removeTab = function removeTab(tab) {
var index;
for (var i = 0; i < ctrl.tabs.length; i++) {
if (ctrl.tabs[i].tab === tab) {
index = i;
break;
}
}
if (ctrl.tabs[index].index === ctrl.active) {
var newActiveTabIndex = index === ctrl.tabs.length - 1 ?
index - 1 : index + 1 % ctrl.tabs.length;
ctrl.select(newActiveTabIndex);
}
ctrl.tabs.splice(index, 1);
};
$scope.$watch('tabset.active', function(val) {
if (angular.isDefined(val) && val !== oldIndex) {
ctrl.select(findTabIndex(val));
}
});
var destroyed;
$scope.$on('$destroy', function() {
destroyed = true;
});
function findTabIndex(index) {
for (var i = 0; i < ctrl.tabs.length; i++) {
if (ctrl.tabs[i].index === index) {
return i;
}
}
}
}])
.directive('uibTabset', function() {
return {
transclude: true,
replace: true,
scope: {},
bindToController: {
active: '=?',
type: '@'
},
controller: 'UibTabsetController',
controllerAs: 'tabset',
templateUrl: function(element, attrs) {
return attrs.templateUrl || 'uib/template/tabs/tabset.html';
},
link: function(scope, element, attrs) {
scope.vertical = angular.isDefined(attrs.vertical) ?
scope.$parent.$eval(attrs.vertical) : false;
scope.justified = angular.isDefined(attrs.justified) ?
scope.$parent.$eval(attrs.justified) : false;
}
};
})
.directive('uibTab', ['$parse', function($parse) {
return {
require: '^uibTabset',
replace: true,
templateUrl: function(element, attrs) {
return attrs.templateUrl || 'uib/template/tabs/tab.html';
},
transclude: true,
scope: {
heading: '@',
index: '=?',
classes: '@?',
onSelect: '&select', //This callback is called in contentHeadingTransclude
//once it inserts the tab's content into the dom
onDeselect: '&deselect'
},
controller: function() {
//Empty controller so other directives can require being 'under' a tab
},
controllerAs: 'tab',
link: function(scope, elm, attrs, tabsetCtrl, transclude) {
scope.disabled = false;
if (attrs.disable) {
scope.$parent.$watch($parse(attrs.disable), function(value) {
scope.disabled = !! value;
});
}
if (angular.isUndefined(attrs.index)) {
if (tabsetCtrl.tabs && tabsetCtrl.tabs.length) {
scope.index = Math.max.apply(null, tabsetCtrl.tabs.map(function(t) { return t.index; })) + 1;
} else {
scope.index = 0;
}
}
if (angular.isUndefined(attrs.classes)) {
scope.classes = '';
}
scope.select = function(evt) {
if (!scope.disabled) {
var index;
for (var i = 0; i < tabsetCtrl.tabs.length; i++) {
if (tabsetCtrl.tabs[i].tab === scope) {
index = i;
break;
}
}
tabsetCtrl.select(index, evt);
}
};
tabsetCtrl.addTab(scope);
scope.$on('$destroy', function() {
tabsetCtrl.removeTab(scope);
});
//We need to transclude later, once the content container is ready.
//when this link happens, we're inside a tab heading.
scope.$transcludeFn = transclude;
}
};
}])
.directive('uibTabHeadingTransclude', function() {
return {
restrict: 'A',
require: '^uibTab',
link: function(scope, elm) {
scope.$watch('headingElement', function updateHeadingElement(heading) {
if (heading) {
elm.html('');
elm.append(heading);
}
});
}
};
})
.directive('uibTabContentTransclude', function() {
return {
restrict: 'A',
require: '^uibTabset',
link: function(scope, elm, attrs) {
var tab = scope.$eval(attrs.uibTabContentTransclude).tab;
//Now our tab is ready to be transcluded: both the tab heading area
//and the tab content area are loaded. Transclude 'em both.
tab.$transcludeFn(tab.$parent, function(contents) {
angular.forEach(contents, function(node) {
if (isTabHeading(node)) {
//Let tabHeadingTransclude know.
tab.headingElement = node;
} else {
elm.append(node);
}
});
});
}
};
function isTabHeading(node) {
return node.tagName && (
node.hasAttribute('uib-tab-heading') ||
node.hasAttribute('data-uib-tab-heading') ||
node.hasAttribute('x-uib-tab-heading') ||
node.tagName.toLowerCase() === 'uib-tab-heading' ||
node.tagName.toLowerCase() === 'data-uib-tab-heading' ||
node.tagName.toLowerCase() === 'x-uib-tab-heading' ||
node.tagName.toLowerCase() === 'uib:tab-heading'
);
}
});