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

refactor(tab): change to use centralized active state #5425

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing the dash at the end and also backstick to quote that.

_(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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing a period.


* `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