Skip to content
This repository has been archived by the owner on May 29, 2019. It is now read-only.

Commit

Permalink
refactor(tab): change to use centralized active state
Browse files Browse the repository at this point in the history
- Change to store active index on tabset controller
- Set index on tab by tab basis

BREAKING CHANGE: The tab API is changed - please refer to the
documentation on new usage of tabs
  • Loading branch information
wesleycho committed Feb 6, 2016
1 parent 10eac7c commit 42ab60b
Show file tree
Hide file tree
Showing 6 changed files with 234 additions and 176 deletions.
6 changes: 3 additions & 3 deletions misc/demo/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -233,13 +233,13 @@ <h1><%= module.displayName %><small>
<div class="pull-right">
<button type="button" class="btn btn-info plunk-btn" ng-click="edit('<%= ngversion%>', '<%= bsversion %>', '<%= pkg.version%>', '<%= module.name %>')"><i class="glyphicon glyphicon-edit"></i> Edit in plunker</button>
</div>
<uib-tabset>
<uib-tab heading="Markup">
<uib-tabset active="activeTab">
<uib-tab index="0" heading="Markup">
<div plunker-content="markup">
<pre ng-non-bindable><code data-language="html"><%- module.docs.html %></code></pre>
</div>
</uib-tab>
<uib-tab heading="JavaScript">
<uib-tab index="1" heading="JavaScript">
<div plunker-content="javascript">
<pre ng-non-bindable><code data-language="javascript"><%- module.docs.js %></code></pre>
</div>
Expand Down
34 changes: 17 additions & 17 deletions src/tabs/docs/demo.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,20 @@
<div ng-controller="TabsDemoCtrl">
<p>Select a tab by setting active binding to true:</p>
<p>
<button type="button" class="btn btn-default btn-sm" ng-click="tabs[0].active = true">Select second tab</button>
<button type="button" class="btn btn-default btn-sm" ng-click="tabs[1].active = true">Select third tab</button>
<button type="button" class="btn btn-default btn-sm" ng-click="scope.active = 1">Select second tab</button>
<button type="button" class="btn btn-default btn-sm" ng-click="scope.active = 2">Select third tab</button>
</p>
<p>
<button type="button" class="btn btn-default btn-sm" ng-click="tabs[1].disabled = ! tabs[1].disabled">Enable / Disable third tab</button>
</p>
<hr />

<uib-tabset>
<uib-tab heading="Static title">Static content</uib-tab>
<uib-tab ng-repeat="tab in tabs" heading="{{tab.title}}" active="tab.active" disable="tab.disabled">
<uib-tabset active="active">
<uib-tab index="0" heading="Static title">Static content</uib-tab>
<uib-tab index="$index + 1" ng-repeat="tab in tabs" heading="{{tab.title}}" active="tab.active" disable="tab.disabled">
{{tab.content}}
</uib-tab>
<uib-tab select="alertMe()">
<uib-tab index="3" select="alertMe()">
<uib-tab-heading>
<i class="glyphicon glyphicon-bell"></i> Alert!
</uib-tab-heading>
Expand All @@ -30,36 +30,36 @@

<hr />

<uib-tabset vertical="true" type="pills">
<uib-tab heading="Vertical 1">Vertical content 1</uib-tab>
<uib-tab heading="Vertical 2">Vertical content 2</uib-tab>
<uib-tabset active="activePill" vertical="true" type="pills">
<uib-tab index="0" heading="Vertical 1">Vertical content 1</uib-tab>
<uib-tab index="1" heading="Vertical 2">Vertical content 2</uib-tab>
</uib-tabset>

<hr />

<uib-tabset justified="true">
<uib-tab heading="Justified">Justified content</uib-tab>
<uib-tab heading="SJ">Short Labeled Justified content</uib-tab>
<uib-tab heading="Long Justified">Long Labeled Justified content</uib-tab>
<uib-tabset active="activeJustified" justified="true">
<uib-tab index="0" heading="Justified">Justified content</uib-tab>
<uib-tab index="1" heading="SJ">Short Labeled Justified content</uib-tab>
<uib-tab index="2" heading="Long Justified">Long Labeled Justified content</uib-tab>
</uib-tabset>

<hr />

Tabs using nested forms:
<form name="outerForm" class="tab-form-demo">
<uib-tabset>
<uib-tab heading="Form Tab">
<uib-tabset active="activeForm">
<uib-tab index="0" heading="Form Tab">
<ng-form name="nestedForm">
<div class="form-group">
<label>Name</label>
<input type="text" class="form-control" required ng-model="model.name"/>
</div>
</ng-form>
</uib-tab>
<uib-tab heading="Tab One">
<uib-tab index="1" heading="Tab One">
Some Tab Content
</uib-tab>
<uib-tab heading="Tab Two">
<uib-tab index="2" heading="Tab Two">
More Tab Content
</uib-tab>
</uib-tabset>
Expand Down
8 changes: 8 additions & 0 deletions src/tabs/docs/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@ AngularJS version of the tabs directive.

### uib-tabset settings

* `active`
<small class="badge">$</small>
_(Default: Index of first tab)_
Active index of tab.

* `justified`
<small class="badge">$</small>
_(Default: `false`)_ -
Expand Down Expand Up @@ -37,6 +42,9 @@ AngularJS version of the tabs directive.
* `heading` -
Heading text.

* `index` -
Tab index

* `select()`
<small class="badge">$</small> -
An optional expression called when tab is activated.
Expand Down
123 changes: 81 additions & 42 deletions src/tabs/tabs.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,66 +2,103 @@ angular.module('ui.bootstrap.tabs', [])

.controller('UibTabsetController', ['$scope', function ($scope) {
var ctrl = this,
tabs = ctrl.tabs = $scope.tabs = [];

ctrl.select = function(selectedTab) {
angular.forEach(tabs, function(tab) {
if (tab.active && tab !== selectedTab) {
tab.active = false;
tab.onDeselect();
selectedTab.selectCalled = false;
oldIndex;
ctrl.tabs = [];

ctrl.select = function(index) {
if (!destroyed) {
var previousIndex = findTabIndex(oldIndex);
var previousSelected = ctrl.tabs[previousIndex];
if (previousSelected) {
previousSelected.tab.onDeselect();
previousSelected.tab.active = false;
}

var selected = ctrl.tabs[index];
if (selected) {
selected.tab.onSelect();
selected.tab.active = true;
ctrl.active = selected.index;
oldIndex = selected.index;
} else if (!selected && angular.isNumber(oldIndex)) {
ctrl.active = null;
oldIndex = null;
}
});
selectedTab.active = true;
// only call select if it has not already been called
if (!selectedTab.selectCalled) {
selectedTab.onSelect();
selectedTab.selectCalled = true;
}
};

ctrl.addTab = function addTab(tab) {
tabs.push(tab);
// we can't run the select function on the first tab
// since that would select it twice
if (tabs.length === 1 && tab.active !== false) {
tab.active = true;
} else if (tab.active) {
ctrl.select(tab);
} else {
tab.active = false;
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.isNumber(ctrl.active) && ctrl.tabs.length === 1) {
var newActiveIndex = findTabIndex(tab.index);
ctrl.select(newActiveIndex);
}
};

ctrl.removeTab = function removeTab(tab) {
var index = tabs.indexOf(tab);
//Select a new tab if the tab to be removed is selected and not destroyed
if (tab.active && tabs.length > 1 && !destroyed) {
//If this is the last tab, select the previous tab. else, the next tab.
var newActiveIndex = index === tabs.length - 1 ? index - 1 : index + 1;
ctrl.select(tabs[newActiveIndex]);
var index = findTabIndex(tab.index);

if (tab.index === ctrl.active) {
var newActiveTabIndex = index === ctrl.tabs.length - 1 ?
index - 1 : index + 1 % ctrl.tabs.length;
ctrl.select(newActiveTabIndex);
}
tabs.splice(index, 1);

ctrl.tabs.splice(index, 1);
};

$scope.$watch('tabset.active', function(val) {
if (angular.isNumber(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: {
scope: {},
bindToController: {
active: '=',
type: '@'
},
controller: 'UibTabsetController',
controllerAs: 'tabset',
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;
scope.vertical = angular.isDefined(attrs.vertical) ?
scope.$parent.$eval(attrs.vertical) : false;
scope.justified = angular.isDefined(attrs.justified) ?
scope.$parent.$eval(attrs.justified) : false;
}
};
})
Expand All @@ -73,8 +110,8 @@ angular.module('ui.bootstrap.tabs', [])
templateUrl: 'uib/template/tabs/tab.html',
transclude: true,
scope: {
active: '=?',
heading: '@',
index: '=',
onSelect: '&select', //This callback is called in contentHeadingTransclude
//once it inserts the tab's content into the dom
onDeselect: '&deselect'
Expand All @@ -84,12 +121,6 @@ angular.module('ui.bootstrap.tabs', [])
},
controllerAs: 'tab',
link: function(scope, elm, attrs, tabsetCtrl, transclude) {
scope.$watch('active', function(active) {
if (active) {
tabsetCtrl.select(scope);
}
});

scope.disabled = false;
if (attrs.disable) {
scope.$parent.$watch($parse(attrs.disable), function(value) {
Expand All @@ -99,7 +130,15 @@ angular.module('ui.bootstrap.tabs', [])

scope.select = function() {
if (!scope.disabled) {
scope.active = true;
var index;
for (var i = 0; i < tabsetCtrl.tabs.length; i++) {
if (tabsetCtrl.tabs[i].tab === scope) {
index = i;
break;
}
}

tabsetCtrl.select(index);
}
};

Expand Down Expand Up @@ -135,7 +174,7 @@ angular.module('ui.bootstrap.tabs', [])
restrict: 'A',
require: '^uibTabset',
link: function(scope, elm, attrs) {
var tab = scope.$eval(attrs.uibTabContentTransclude);
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.
Expand Down
Loading

0 comments on commit 42ab60b

Please sign in to comment.