angular-bootstrap-scrolling-tabs
Version:
Angular directive for scrollable Bootstrap Tabs
241 lines (190 loc) • 8.62 kB
JavaScript
/* ***********************************************************************************
* ElementsHandler - Class that each instance of ScrollingTabsControl will instantiate
* **********************************************************************************/
function ElementsHandler(scrollingTabsControl) {
var ehd = this;
ehd.stc = scrollingTabsControl;
}
// ElementsHandler prototype methods
(function (p) {
p.initElements = function (options) {
var ehd = this,
stc = ehd.stc,
$tabsContainer = stc.$tabsContainer;
ehd.setElementReferences();
if (options.isWrappingAngularUITabset) {
ehd.moveTabContentOutsideScrollContainer(options);
}
ehd.setEventListeners();
};
p.moveTabContentOutsideScrollContainer = function (options) {
var ehd = this,
stc = ehd.stc,
$tabsContainer = stc.$tabsContainer,
$movableContainer = $tabsContainer.find('.scrtabs-tabs-movable-container'),
$tabContentInMovableContainer = $movableContainer.find('.tab-content'),
uibTabsetController = $movableContainer.find('> div').eq(0).data('$uibTabsetController');
$tabContentInMovableContainer.show().appendTo($tabsContainer);
if (uibTabsetController) {
$tabContentInMovableContainer.data('$uibTabsetController', uibTabsetController);
}
};
p.refreshAllElementSizes = function () {
var ehd = this,
stc = ehd.stc,
smv = stc.scrollMovement,
scrollArrowsWereVisible = stc.scrollArrowsVisible,
actionsTaken = {
didScrollToActiveTab: false
},
minPos;
ehd.setElementWidths();
ehd.setScrollArrowVisibility();
if (stc.scrollArrowsVisible) {
ehd.setFixedContainerWidthForJustVisibleScrollArrows();
}
// this could have been a window resize or the removal of a
// dynamic tab, so 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 (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
});
actionsTaken.didScrollToActiveTab = true;
}
} else {
// scroll arrows went away after resize, so position movable container at 0
stc.movableContainerLeftPos = 0;
smv.slideMovableContainerToLeftPos();
}
}
return actionsTaken;
};
p.setElementReferences = function () {
var ehd = this,
stc = ehd.stc,
$tabsContainer = stc.$tabsContainer;
stc.isNavPills = false;
stc.$fixedContainer = $tabsContainer.find('.scrtabs-tabs-fixed-container');
stc.$movableContainer = $tabsContainer.find('.scrtabs-tabs-movable-container');
stc.$tabsUl = stc.$movableContainer.find('.nav-tabs');
// check for pills
if (!stc.$tabsUl.length) {
stc.$tabsUl = stc.$movableContainer.find('.nav-pills');
if (stc.$tabsUl.length) {
stc.isNavPills = true;
}
}
stc.$tabsLiCollection = stc.$tabsUl.find('> li');
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;
if (!stc.$win) {
return;
}
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.off('.scrtabs').on({
'mousedown.scrtabs touchstart.scrtabs': function (e) { evh.handleMousedownOnLeftScrollArrow.call(evh, e); },
'mouseup.scrtabs touchend.scrtabs': function (e) { evh.handleMouseupOnLeftScrollArrow.call(evh, e); },
'click.scrtabs': function (e) { evh.handleClickOnLeftScrollArrow.call(evh, e); }
});
stc.$rightScrollArrow.off('.scrtabs').on({
'mousedown.scrtabs touchstart.scrtabs': function (e) { evh.handleMousedownOnRightScrollArrow.call(evh, e); },
'mouseup.scrtabs touchend.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,
tabsContainerRect = stc.$tabsContainer.get(0).getBoundingClientRect();
stc.fixedContainerWidth = tabsContainerRect.width || (tabsContainerRect.right - tabsContainerRect.left);
stc.$fixedContainer.width(stc.fixedContainerWidth);
};
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,
$tabLi = stc.$tabsUl.find('li'),
fixedContainerHeight = stc.$fixedContainer.height(),
maxWidthExpansions = 10; // prevent infinite loop
stc.movableContainerWidth = 0;
if ($tabLi.length) {
$tabLi.each(function __getLiWidth() {
var $li = $(this),
totalMargin = 0;
if (stc.isNavPills) { // pills have a margin-left, tabs have no margin
totalMargin = parseInt($li.css('margin-left'), 10) + parseInt($li.css('margin-right'), 10);
}
stc.movableContainerWidth += ($li.outerWidth() + totalMargin);
});
stc.movableContainerWidth += 1;
// if the tabs don't span the width of the page, force the
// movable container width to full page width so the bottom
// border spans the page width instead of just spanning the
// width of the tabs
if (stc.movableContainerWidth < stc.fixedContainerWidth) {
stc.movableContainerWidth = stc.fixedContainerWidth;
}
}
stc.$movableContainer.width(stc.movableContainerWidth);
// when we have >35 pills, sometimes the movable container width ends
// up being a pixel short, causing the last pill to wrap; so we do
// this check for a wrapped pill--we check if the movable container
// height has expanded beyond the fixed container height (indicating
// a wrapped pill), and if it has, we increase the movable container
// width 1 pixel at a time until there's no wrap. We also put a
// limit on the number of checks/expansions to prevent an infinite
// loop, in case we get into some weird situation where adding width
// never fixes the wrap.
while ((stc.$movableContainer.height() > fixedContainerHeight) && maxWidthExpansions--) {
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));