<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css">
<link rel="stylesheet" href="scrolling-tabs.css">
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="scrolling-tabs-header">Angular Bootstrap Scrolling Tabs</div>
<div ng-app="myapp">
<div ng-controller="MainController as main">
<button class="btn btn-primary" ng-click="main.addTab()">Add Tab</button>
<br/>
<br/>
<!-- wrap nav-tabs ul in a div with scrolling-tabs-wrapper direc tive on it -->
<div scrolling-tabs-wrapper watch-tabs="main.uc2Tabs">
<!-- Standard Bootstrap ul.nav-tabs -->
<ul class="nav nav-tabs" role="tablist">
<li ng-class="{ 'active': tab.active, 'disabled': tab.disabled }" ng-repeat="tab in main.uc2Tabs">
<a ng-href="{{'#' + tab.paneId}}" role="tab" data-toggle="tab">{{tab.title}}</a>
</li>
</ul>
</div>
<!-- Tab panes -->
<div class="tab-content">
<div id="{{tab.paneId}}" class="tab-pane" ng-class="{ 'active': tab.active }" ng-repeat="tab in main.uc2Tabs">{{tab.content}}</div>
</div>
<pre>{{main.uc2Tabs | json}}</pre>
</div>
</div>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.25/angular.min.js"></script>
<script src="//netdna.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min.js"></script>
<script src="scrolling-tabs.js"></script>
<script src="app.js"></script>
<script src="MainService.js"></script>
<script src="MainController.js"></script>
</body>
</html>
/* line 6, scrolling-tabs.scss */
.scrtabs-tab-container * {
box-sizing: border-box;
}
/* line 10, scrolling-tabs.scss */
.scrtabs-tab-container {
height: 42px;
}
/* line 14, scrolling-tabs.scss */
.scrtabs-tabs-fixed-container {
float: left;
height: 42px;
overflow: hidden;
width: 100%;
}
/* line 21, scrolling-tabs.scss */
.scrtabs-tabs-movable-container {
position: relative;
}
/* line 25, scrolling-tabs.scss */
.scrtabs-tab-scroll-arrow {
border: 1px solid #dddddd;
border-top: none;
color: #428bca;
cursor: pointer;
float: left;
font-size: 12px;
height: 42px;
margin-bottom: -1px;
padding-left: 2px;
padding-top: 13px;
width: 20px;
}
/* line 37, scrolling-tabs.scss */
.scrtabs-tab-scroll-arrow:hover {
background-color: #eeeeee;
}
/* line 42, scrolling-tabs.scss */
.scrtabs-tabs-fixed-container ul.nav-tabs {
height: 41px;
}
/* line 44, scrolling-tabs.scss */
.scrtabs-tabs-fixed-container ul.nav-tabs > li {
white-space: nowrap;
}
/* line 49, scrolling-tabs.scss */
.scrtabs-tab-content-hidden {
display: none;
}
;(function () {
'use strict';
var CONSTANTS = {
CONTINUOUS_SCROLLING_TIMEOUT_INTERVAL: 50, // timeout interval for repeatedly moving the tabs container
// by one increment while the mouse is held down--decrease to
// make mousedown continous scrolling faster
SCROLL_OFFSET_FRACTION: 6, // each click moves the container this fraction of the fixed container--decrease
// to make the tabs scroll farther per click
DATA_KEY_IS_MOUSEDOWN: 'ismousedown'
},
scrollingTabsModule = angular.module('mj.scrollingTabs', []),
/* *************************************************************
* scrolling-tabs element directive template
* *************************************************************/
// plunk: http://plnkr.co/edit/YhKiIhuAPkpAyacu6tuk
scrollingTabsTemplate = [
'<div class="scrtabs-tab-container">',
' <div class="scrtabs-tab-scroll-arrow scrtabs-js-tab-scroll-arrow-left"><span class="glyphicon glyphicon-chevron-left"></span></div>',
' <div class="scrtabs-tabs-fixed-container">',
' <div class="scrtabs-tabs-movable-container">',
' <ul class="nav nav-tabs" role="tablist">',
' <li ng-class="{ \'active\': tab[propActive || \'active\'], ',
' \'disabled\': tab[propDisabled || \'disabled\'] }" ',
' data-tab="{{tab}}" data-index="{{$index}}" ng-repeat="tab in tabsArr">',
' <a ng-href="{{\'#\' + tab[propPaneId || \'paneId\']}}" role="tab"',
' data-toggle="{{tab[propDisabled || \'disabled\'] ? \'\' : \'tab\'}}" ',
' ng-bind-html="sanitize(tab[propTitle || \'title\']);">',
' </a>',
' </li>',
' </ul>',
' </div>',
' </div>',
' <div class="scrtabs-tab-scroll-arrow scrtabs-js-tab-scroll-arrow-right"><span class="glyphicon glyphicon-chevron-right"></span></div>',
'</div>'
].join(''),
/* *************************************************************
* scrolling-tabs-wrapper element directive template
* *************************************************************/
// plunk: http://plnkr.co/edit/lWeQxxecKPudK7xlQxS3
scrollingTabsWrapperTemplate = [
'<div class="scrtabs-tab-container">',
' <div class="scrtabs-tab-scroll-arrow scrtabs-js-tab-scroll-arrow-left"><span class="glyphicon glyphicon-chevron-left"></span></div>',
' <div class="scrtabs-tabs-fixed-container">',
' <div class="scrtabs-tabs-movable-container" ng-transclude></div>',
' </div>',
' <div class="scrtabs-tab-scroll-arrow scrtabs-js-tab-scroll-arrow-right"><span class="glyphicon glyphicon-chevron-right"></span></div>',
'</div>'
].join('');
// smartresize from Paul Irish (debounced window resize)
(function ($, sr) {
var debounce = function (func, threshold, execAsap) {
var timeout;
return function debounced() {
var obj = this, args = arguments;
function delayed() {
if (!execAsap)
func.apply(obj, args);
timeout = null;
};
if (timeout)
clearTimeout(timeout);
else if (execAsap)
func.apply(obj, args);
timeout = setTimeout(delayed, threshold || 100);
};
};
jQuery.fn[sr] = function (fn) { return fn ? this.bind('resize.scrtabs', debounce(fn)) : this.trigger(sr); };
})(jQuery, 'smartresize');
/* ***********************************************************************************
* EventHandlers - Class that each instance of ScrollingTabsControl will instantiate
* **********************************************************************************/
function EventHandlers(scrollingTabsControl) {
var evh = this;
evh.stc = scrollingTabsControl;
}
// prototype methods
(function (p){
p.handleClickOnLeftScrollArrow = function (e) {
var evh = this,
stc = evh.stc;
stc.scrollMovement.incrementScrollLeft();
};
p.handleClickOnRightScrollArrow = function (e) {
var evh = this,
stc = evh.stc,
scrollMovement = stc.scrollMovement;
scrollMovement.incrementScrollRight(scrollMovement.getMinPos());
};
p.handleMousedownOnLeftScrollArrow = function (e) {
var evh = this,
stc = evh.stc;
stc.scrollMovement.startScrollLeft();
};
p.handleMousedownOnRightScrollArrow = function (e) {
var evh = this,
stc = evh.stc;
stc.scrollMovement.startScrollRight();
};
p.handleMouseupOnLeftScrollArrow = function (e) {
var evh = this,
stc = evh.stc;
stc.scrollMovement.stopScrollLeft();
};
p.handleMouseupOnRightScrollArrow = function (e) {
var evh = this,
stc = evh.stc;
stc.scrollMovement.stopScrollRight();
};
p.handleWindowResize = function (e) {
var evh = this,
stc = evh.stc,
newWinWidth = stc.$win.width();
if (newWinWidth === stc.winWidth) {
return false; // false alarm
}
stc.winWidth = newWinWidth;
stc.elementsHandler.refreshAllElementSizes(true); // true -> check for scroll arrows not being necessary anymore
};
}(EventHandlers.prototype));
/* ***********************************************************************************
* ElementsHandler - Class that each instance of ScrollingTabsControl will instantiate
* **********************************************************************************/
function ElementsHandler(scrollingTabsControl) {
var ehd = this;
ehd.stc = scrollingTabsControl;
}
// prototype methods
(function (p) {
p.initElements = function (options) {
var ehd = this;
ehd.setElementReferences();
if (options.isWrapperDirective) {
ehd.moveTabContentOutsideScrollContainer(options);
}
ehd.setEventListeners();
};
p.moveTabContentOutsideScrollContainer = function (options) {
var ehd = this,
stc = ehd.stc,
$tabsContainer = stc.$tabsContainer,
tabContentCloneCssClass = 'scrtabs-tab-content-clone',
tabContentHiddenCssClass = 'scrtabs-tab-content-hidden',
$tabContent = $tabsContainer.find('.tab-content').not('.' + tabContentCloneCssClass),
$currTcClone,
$newTcClone;
// if the tabs array won't be changing, we can just move the
// the .tab-content outside the scrolling container right now
if (!options.isWatchingTabsArray) {
$tabContent.appendTo($tabsContainer);
return;
}
/* if we're watching the tabs array for changes, we can't just
* move the .tab-content outside the scrolling container because
* that will break the angular-ui directive dependencies, and
* an error will be thrown as soon as the tabs array changes;
* so we leave the .tab-content where it is but hide it, then
* make a clone and move the clone outside the scroll container,
* which will be the visible .tab-content.
*/
// hide the original .tab-content if it's not already hidden
if (!$tabContent.hasClass(tabContentHiddenCssClass)) {
$tabContent.addClass(tabContentHiddenCssClass);
}
// create new clone
$newTcClone = $tabContent
.clone()
.removeClass(tabContentHiddenCssClass)
.addClass(tabContentCloneCssClass);
// get the current clone, if it exists
$currTcClone = $tabsContainer.find('.' + tabContentCloneCssClass);
if ($currTcClone.length) { // already a clone there so replace it
$currTcClone.replaceWith($newTcClone);
} else {
$tabsContainer.append($newTcClone);
}
};
p.refreshAllElementSizes = function (isPossibleArrowVisibilityChange) {
var ehd = this,
stc = ehd.stc,
smv = stc.scrollMovement,
scrollArrowsWereVisible = stc.scrollArrowsVisible,
minPos;
ehd.setElementWidths();
ehd.setScrollArrowVisibility();
if (stc.scrollArrowsVisible) {
ehd.setFixedContainerWidthForJustVisibleScrollArrows();
}
// if this was a window resize, make sure the movable container is positioned
// correctly because, if it is far to the left and we increased the window width, it's
// possible that the tabs will be too far left, beyond the min pos.
if (isPossibleArrowVisibilityChange && (stc.scrollArrowsVisible || scrollArrowsWereVisible)) {
if (stc.scrollArrowsVisible) {
// make sure container not too far left
minPos = smv.getMinPos();
if (stc.movableContainerLeftPos < minPos) {
smv.incrementScrollRight(minPos);
} else {
smv.scrollToActiveTab({
isOnWindowResize: true
});
}
} else {
// scroll arrows went away after resize, so position movable container at 0
stc.movableContainerLeftPos = 0;
smv.slideMovableContainerToLeftPos();
}
}
};
p.setElementReferences = function () {
var ehd = this,
stc = ehd.stc,
$tabsContainer = stc.$tabsContainer;
stc.$fixedContainer = $tabsContainer.find('.scrtabs-tabs-fixed-container');
stc.$movableContainer = $tabsContainer.find('.scrtabs-tabs-movable-container');
stc.$tabsUl = $tabsContainer.find('.nav-tabs');
stc.$leftScrollArrow = $tabsContainer.find('.scrtabs-js-tab-scroll-arrow-left');
stc.$rightScrollArrow = $tabsContainer.find('.scrtabs-js-tab-scroll-arrow-right');
stc.$scrollArrows = stc.$leftScrollArrow.add(stc.$rightScrollArrow);
stc.$win = $(window);
};
p.setElementWidths = function () {
var ehd = this,
stc = ehd.stc;
stc.containerWidth = stc.$tabsContainer.outerWidth();
stc.winWidth = stc.$win.width();
stc.scrollArrowsCombinedWidth = stc.$leftScrollArrow.outerWidth() + stc.$rightScrollArrow.outerWidth();
ehd.setFixedContainerWidth();
ehd.setMovableContainerWidth();
};
p.setEventListeners = function () {
var ehd = this,
stc = ehd.stc,
evh = stc.eventHandlers; // eventHandlers
stc.$leftScrollArrow.on({
'mousedown.scrtabs': function (e) { evh.handleMousedownOnLeftScrollArrow.call(evh, e); },
'mouseup.scrtabs': function (e) { evh.handleMouseupOnLeftScrollArrow.call(evh, e); },
'click.scrtabs': function (e) { evh.handleClickOnLeftScrollArrow.call(evh, e); }
});
stc.$rightScrollArrow.on({
'mousedown.scrtabs': function (e) { evh.handleMousedownOnRightScrollArrow.call(evh, e); },
'mouseup.scrtabs': function (e) { evh.handleMouseupOnRightScrollArrow.call(evh, e); },
'click.scrtabs': function (e) { evh.handleClickOnRightScrollArrow.call(evh, e); }
});
stc.$win.smartresize(function (e) { evh.handleWindowResize.call(evh, e); });
};
p.setFixedContainerWidth = function () {
var ehd = this,
stc = ehd.stc;
stc.$fixedContainer.width(stc.fixedContainerWidth = stc.$tabsContainer.outerWidth());
};
p.setFixedContainerWidthForJustHiddenScrollArrows = function () {
var ehd = this,
stc = ehd.stc;
stc.$fixedContainer.width(stc.fixedContainerWidth);
};
p.setFixedContainerWidthForJustVisibleScrollArrows = function () {
var ehd = this,
stc = ehd.stc;
stc.$fixedContainer.width(stc.fixedContainerWidth - stc.scrollArrowsCombinedWidth);
};
p.setMovableContainerWidth = function () {
var ehd = this,
stc = ehd.stc;
stc.movableContainerWidth = 0;
stc.$tabsUl.find('li').each(function __getLiWidth() {
var $li = $(this);
stc.movableContainerWidth += $li.outerWidth();
});
stc.$movableContainer.width(stc.movableContainerWidth += 1);
};
p.setScrollArrowVisibility = function () {
var ehd = this,
stc = ehd.stc,
shouldBeVisible = stc.movableContainerWidth > stc.fixedContainerWidth;
if (shouldBeVisible && !stc.scrollArrowsVisible) {
stc.$scrollArrows.show();
stc.scrollArrowsVisible = true;
ehd.setFixedContainerWidthForJustVisibleScrollArrows();
} else if (!shouldBeVisible && stc.scrollArrowsVisible) {
stc.$scrollArrows.hide();
stc.scrollArrowsVisible = false;
ehd.setFixedContainerWidthForJustHiddenScrollArrows();
}
};
}(ElementsHandler.prototype));
/* ***********************************************************************************
* ScrollMovement - Class that each instance of ScrollingTabsControl will instantiate
* **********************************************************************************/
function ScrollMovement(scrollingTabsControl) {
var smv = this;
smv.stc = scrollingTabsControl;
}
// prototype methods
(function (p) {
p.continueScrollLeft = function () {
var smv = this,
stc = smv.stc;
stc.$timeout(function() {
if (stc.$leftScrollArrow.data(CONSTANTS.DATA_KEY_IS_MOUSEDOWN) && (stc.movableContainerLeftPos < 0)) {
if (!smv.incrementScrollLeft()) { // scroll limit not reached, so keep scrolling
smv.continueScrollLeft();
}
}
}, CONSTANTS.CONTINUOUS_SCROLLING_TIMEOUT_INTERVAL);
};
p.continueScrollRight = function (minPos) {
var smv = this,
stc = smv.stc;
stc.$timeout(function() {
if (stc.$rightScrollArrow.data(CONSTANTS.DATA_KEY_IS_MOUSEDOWN) && (stc.movableContainerLeftPos > minPos)) {
// slide tabs LEFT -> decrease movable container's left position
// min value is (movableContainerWidth - $tabHeader width)
if (!smv.incrementScrollRight(minPos)) {
smv.continueScrollRight(minPos);
}
}
}, CONSTANTS.CONTINUOUS_SCROLLING_TIMEOUT_INTERVAL);
};
p.decrementMovableContainerLeftPos = function (minPos) {
var smv = this,
stc = smv.stc;
stc.movableContainerLeftPos -= (stc.fixedContainerWidth / CONSTANTS.SCROLL_OFFSET_FRACTION);
if (stc.movableContainerLeftPos < minPos) {
stc.movableContainerLeftPos = minPos;
}
};
p.getMinPos = function () {
var smv = this,
stc = smv.stc;
return stc.scrollArrowsVisible ? (stc.fixedContainerWidth - stc.movableContainerWidth - stc.scrollArrowsCombinedWidth) : 0;
};
p.getMovableContainerCssLeftVal = function () {
var smv = this,
stc = smv.stc;
return (stc.movableContainerLeftPos === 0) ? '0' : stc.movableContainerLeftPos + 'px';
};
p.incrementScrollLeft = function () {
var smv = this,
stc = smv.stc;
stc.movableContainerLeftPos += (stc.fixedContainerWidth / CONSTANTS.SCROLL_OFFSET_FRACTION);
if (stc.movableContainerLeftPos > 0) {
stc.movableContainerLeftPos = 0;
}
smv.slideMovableContainerToLeftPos();
return (stc.movableContainerLeftPos === 0); // indicates scroll limit reached
};
p.incrementScrollRight = function (minPos) {
var smv = this,
stc = smv.stc;
smv.decrementMovableContainerLeftPos(minPos);
smv.slideMovableContainerToLeftPos();
return (stc.movableContainerLeftPos === minPos);
};
p.scrollToActiveTab = function (options) {
var smv = this,
stc = smv.stc,
$activeTab,
activeTabWidth,
activeTabLeftPos,
rightArrowLeftPos,
overlap;
// if the active tab is not fully visible, scroll till it is
if (!stc.scrollArrowsVisible) {
return;
}
$activeTab = stc.$tabsUl.find('li.active');
if (!$activeTab.length) {
return;
}
activeTabWidth = $activeTab.outerWidth();
activeTabLeftPos = $activeTab.offset().left;
rightArrowLeftPos = stc.$rightScrollArrow.offset().left;
overlap = activeTabLeftPos + activeTabWidth - rightArrowLeftPos;
if (overlap > 0) {
stc.movableContainerLeftPos = (options.isOnWindowResize || options.isOnWrapperRefresh) ? (stc.movableContainerLeftPos - overlap) : -overlap;
smv.slideMovableContainerToLeftPos();
}
};
p.slideMovableContainerToLeftPos = function () {
var smv = this,
stc = smv.stc,
leftVal;
stc.movableContainerLeftPos = stc.movableContainerLeftPos / 1;
leftVal = smv.getMovableContainerCssLeftVal();
stc.$movableContainer.stop().animate({ left: leftVal }, 'slow', function __slideAnimComplete() {
var newMinPos = smv.getMinPos();
// if we slid past the min pos--which can happen if you resize the window
// quickly--move back into position
if (stc.movableContainerLeftPos < newMinPos) {
smv.decrementMovableContainerLeftPos(newMinPos);
stc.$movableContainer.stop().animate({ left: smv.getMovableContainerCssLeftVal() }, 'fast');
}
});
};
p.startScrollLeft = function () {
var smv = this,
stc = smv.stc;
stc.$leftScrollArrow.data(CONSTANTS.DATA_KEY_IS_MOUSEDOWN, true);
smv.continueScrollLeft();
};
p.startScrollRight = function () {
var smv = this,
stc = smv.stc;
stc.$rightScrollArrow.data(CONSTANTS.DATA_KEY_IS_MOUSEDOWN, true);
smv.continueScrollRight(smv.getMinPos());
};
p.stopScrollLeft = function () {
var smv = this,
stc = smv.stc;
stc.$leftScrollArrow.data(CONSTANTS.DATA_KEY_IS_MOUSEDOWN, false);
};
p.stopScrollRight = function () {
var smv = this,
stc = smv.stc;
stc.$rightScrollArrow.data(CONSTANTS.DATA_KEY_IS_MOUSEDOWN, false);
};
}(ScrollMovement.prototype));
/* **********************************************************************
* ScrollingTabsControl - Class that each directive will instantiate
* **********************************************************************/
function ScrollingTabsControl($tabsContainer, $timeout) {
var stc = this;
stc.$tabsContainer = $tabsContainer;
stc.$timeout = $timeout,
stc.movableContainerLeftPos = 0;
stc.scrollArrowsVisible = true;
stc.scrollMovement = new ScrollMovement(stc);
stc.eventHandlers = new EventHandlers(stc);
stc.elementsHandler = new ElementsHandler(stc);
}
// prototype methods
(function (p) {
p.initTabs = function (options) {
var stc = this,
elementsHandler = stc.elementsHandler,
scrollMovement = stc.scrollMovement;
stc.$timeout(function __initTabsAfterTimeout() {
elementsHandler.initElements(options);
elementsHandler.refreshAllElementSizes();
scrollMovement.scrollToActiveTab({
isOnWrapperRefresh: (options.isWrapperDirective && options.isWatchingTabsArray)
});
}, 100);
};
}(ScrollingTabsControl.prototype));
/* ********************************************************
* scrolling-tabs Directive
* ********************************************************/
function scrollingTabsDirective($timeout, $sce) {
function sanitize (html) {
return $sce.trustAsHtml(html);
}
// ------------ Directive Object ---------------------------
return {
restrict: 'E',
template: scrollingTabsTemplate,
transclude: false,
replace: true,
scope: {
tabs: '@',
propPaneId: '@',
propTitle: '@',
propActive: '@',
propDisabled: '@',
localTabClick: '&tabClick'
},
link: function(scope, element, attrs) {
var scrollingTabsControl = new ScrollingTabsControl(element, $timeout);
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
});
});
scrollingTabsControl.initTabs(false); // false -> not the wrapper directive
}
};
}
/* ********************************************************
* scrolling-tabs-wrapper Directive
* ********************************************************/
function scrollingTabsWrapperDirective($timeout) {
// ------------ Directive Object ---------------------------
return {
restrict: 'A',
template: scrollingTabsWrapperTemplate,
transclude: true,
replace: true,
link: function(scope, element, attrs) {
var scrollingTabsControl = new ScrollingTabsControl(element, $timeout);
if (!attrs.watchTabs) {
// we don't need to watch the tabs array for changes, so just
// init the tabs control and return
scrollingTabsControl.initTabs({
isWrapperDirective: true
});
return;
}
// watch the tabs array for changes and refresh the tabs
// control any time it changes (whether the change is a
// new tab or just a change in which tab is selected)
scope.$watch(attrs.watchTabs, function (newVal, oldVal) {
scrollingTabsControl.initTabs({
isWrapperDirective: true,
isWatchingTabsArray: true
});
}, true);
}
};
}
scrollingTabsDirective.$inject = ['$timeout', '$sce'];
scrollingTabsWrapperDirective.$inject = ['$timeout'];
scrollingTabsModule.directive('scrollingTabs', scrollingTabsDirective);
scrollingTabsModule.directive('scrollingTabsWrapper', scrollingTabsWrapperDirective);
}());
;(function () {
'use strict';
angular.module('myapp', ['mj.scrollingTabs']);
}());
;(function () {
'use strict';
var uc1Tabs = [
{ paneId: 'uc1tab01', title: 'Tab <strong>1</strong> of 12', content: 'Tab Number 1 Content', active: true, disabled: false },
{ paneId: 'uc1tab02', title: 'Tab <strong>2</strong> of 12', content: 'Tab Number 2 Content', active: false, disabled: false },
{ paneId: 'uc1tab03', title: 'Tab <strong>3</strong> of 12', content: 'Tab Number 3 Content', active: false, disabled: false },
{ paneId: 'uc1tab04', title: 'Tab <strong>4</strong> of 12', content: 'Tab Number 4 Content', active: false, disabled: false },
{ paneId: 'uc1tab05', title: 'Tab <strong>5</strong> of 12', content: 'Tab Number 5 Content', active: false, disabled: true },
{ paneId: 'uc1tab06', title: 'Tab <strong>6</strong> of 12', content: 'Tab Number 6 Content', active: false, disabled: false },
{ paneId: 'uc1tab07', title: 'Tab <strong>7</strong> of 12', content: 'Tab Number 7 Content', active: false, disabled: false },
{ paneId: 'uc1tab08', title: 'Tab <strong>8</strong> of 12', content: 'Tab Number 8 Content', active: false, disabled: false },
{ paneId: 'uc1tab09', title: 'Tab <strong>9</strong> of 12', content: 'Tab Number 9 Content', active: false, disabled: false },
{ paneId: 'uc1tab10', title: 'Tab <strong>10</strong> of 12', content: 'Tab Number 10 Content', active: false, disabled: false },
{ paneId: 'uc1tab11', title: 'Tab <strong>11</strong> of 12', content: 'Tab Number 11 Content', active: false, disabled: false },
{ paneId: 'uc1tab12', title: 'Tab <strong>12</strong> of 12', content: 'Tab Number 12 Content', active: false, disabled: false }
];
var uc2Tabs = [
{ paneId: 'uc2tab01', title: 'Tab 1 of 12', content: 'Tab Number 1 Content', active: true, disabled: false },
{ paneId: 'uc2tab02', title: 'Tab 2 of 12', content: 'Tab Number 2 Content', active: false, disabled: false },
{ paneId: 'uc2tab03', title: 'Tab 3 of 12', content: 'Tab Number 3 Content', active: false, disabled: false },
];
function MainService($timeout) {
var svc = this;
svc.data = {
uc1Tabs: uc1Tabs,
uc2Tabs: uc2Tabs
};
}
MainService.$inject = ['$timeout'];
angular.module('myapp').service('MainService', MainService);
}());
;(function () {
'use strict';
function MainController(MainService) {
var ctrl = this;
ctrl.uc1Tabs = MainService.data.uc1Tabs;
ctrl.uc2Tabs = MainService.data.uc2Tabs;
ctrl.handleClickOnTab = function (e, idx, tab) {
};
ctrl.counter = 4;
ctrl.addTab = function()
{
ctrl.uc2Tabs.push({
paneId: 'uc2tab0' + ctrl.counter,
title: 'Tab ' + ctrl.counter,
content: 'Tab Content' + ctrl.counter,
active: false,
disabled: false });
ctrl.counter++;
}
}
MainController.$inject = ['MainService'];
angular.module('myapp').controller('MainController', MainController);
}());
.scrolling-tabs-header {
background-color: #666666;
color: white;
font-size: 22px;
height: 40px;
padding: 4px 18px; }
.scrolling-tabs-subheader {
background-color: #f0f0f0;
color: #333;
font-size: 16px;
height: 65px;
margin-top: 50px;
padding: 8px 24px; }
.scrolling-tabs-subheader:first-child {
margin-top: 20px;
}
.scrolling-tabs-subheader div:first-child {
font-weight: 700;
margin-bottom: 2px;
}