priority-nav-scroller
Version:
A plugin for the priority+ navigation pattern. When the navigation items don’t fit on screen they are hidden in a horizontal scrollable container with controls.
202 lines (168 loc) • 8.02 kB
JavaScript
(function (global, factory) {
if (typeof define === "function" && define.amd) {
define(["exports"], factory);
} else if (typeof exports !== "undefined") {
factory(exports);
} else {
var mod = {
exports: {}
};
factory(mod.exports);
global.priorityNavScroller = mod.exports;
}
})(this, function (_exports) {
"use strict";
Object.defineProperty(_exports, "__esModule", {
value: true
});
_exports["default"] = void 0;
/**
Priority+ horizontal scrolling menu.
@param {Object} object - Container for all options.
@param {string || DOM node} selector - Element selector.
@param {string} navSelector - Nav element selector.
@param {string} contentSelector - Content element selector.
@param {string} itemSelector - Items selector.
@param {string} buttonLeftSelector - Left button selector.
@param {string} buttonRightSelector - Right button selector.
@param {integer || string} scrollStep - Amount to scroll on button click. 'average' gets the average link width.
*/
var PriorityNavScroller = function PriorityNavScroller() {
var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
_ref$selector = _ref.selector,
selector = _ref$selector === void 0 ? '.nav-scroller' : _ref$selector,
_ref$navSelector = _ref.navSelector,
navSelector = _ref$navSelector === void 0 ? '.nav-scroller-nav' : _ref$navSelector,
_ref$contentSelector = _ref.contentSelector,
contentSelector = _ref$contentSelector === void 0 ? '.nav-scroller-content' : _ref$contentSelector,
_ref$itemSelector = _ref.itemSelector,
itemSelector = _ref$itemSelector === void 0 ? '.nav-scroller-item' : _ref$itemSelector,
_ref$buttonLeftSelect = _ref.buttonLeftSelector,
buttonLeftSelector = _ref$buttonLeftSelect === void 0 ? '.nav-scroller-btn--left' : _ref$buttonLeftSelect,
_ref$buttonRightSelec = _ref.buttonRightSelector,
buttonRightSelector = _ref$buttonRightSelec === void 0 ? '.nav-scroller-btn--right' : _ref$buttonRightSelec,
_ref$scrollStep = _ref.scrollStep,
scrollStep = _ref$scrollStep === void 0 ? 80 : _ref$scrollStep;
var navScroller = typeof selector === 'string' ? document.querySelector(selector) : selector;
var validateScrollStep = function validateScrollStep() {
return Number.isInteger(scrollStep) || scrollStep === 'average';
};
if (navScroller === undefined || navScroller === null || !validateScrollStep()) {
throw new Error('There is something wrong, check your options.');
}
var navScrollerNav = navScroller.querySelector(navSelector);
var navScrollerContent = navScroller.querySelector(contentSelector);
var navScrollerContentItems = navScrollerContent.querySelectorAll(itemSelector);
var navScrollerLeft = navScroller.querySelector(buttonLeftSelector);
var navScrollerRight = navScroller.querySelector(buttonRightSelector);
var scrolling = false;
var scrollAvailableLeft = 0;
var scrollAvailableRight = 0;
var scrollingDirection = '';
var scrollOverflow = '';
var timeout; // Sets overflow and toggle buttons accordingly
var setOverflow = function setOverflow() {
scrollOverflow = getOverflow();
toggleButtons(scrollOverflow);
calculateScrollStep();
}; // Debounce setting the overflow with requestAnimationFrame
var requestSetOverflow = function requestSetOverflow() {
if (timeout) window.cancelAnimationFrame(timeout);
timeout = window.requestAnimationFrame(function () {
setOverflow();
});
}; // Gets the overflow available on the nav scroller
var getOverflow = function getOverflow() {
var scrollWidth = navScrollerNav.scrollWidth;
var scrollViewport = navScrollerNav.clientWidth;
var scrollLeft = navScrollerNav.scrollLeft;
scrollAvailableLeft = scrollLeft;
scrollAvailableRight = scrollWidth - (scrollViewport + scrollLeft); // 1 instead of 0 to compensate for number rounding
var scrollLeftCondition = scrollAvailableLeft > 1;
var scrollRightCondition = scrollAvailableRight > 1; // console.log(scrollWidth, scrollViewport, scrollAvailableLeft, scrollAvailableRight);
if (scrollLeftCondition && scrollRightCondition) {
return 'both';
} else if (scrollLeftCondition) {
return 'left';
} else if (scrollRightCondition) {
return 'right';
} else {
return 'none';
}
}; // Calculates the scroll step based on the width of the scroller and the number of links
var calculateScrollStep = function calculateScrollStep() {
if (scrollStep === 'average') {
var scrollViewportNoPadding = navScrollerNav.scrollWidth - (parseInt(getComputedStyle(navScrollerContent).getPropertyValue('padding-left')) + parseInt(getComputedStyle(navScrollerContent).getPropertyValue('padding-right')));
var scrollStepAverage = Math.floor(scrollViewportNoPadding / navScrollerContentItems.length);
scrollStep = scrollStepAverage;
}
}; // Move the scroller with a transform
var moveScroller = function moveScroller(direction) {
if (scrolling === true || scrollOverflow !== direction && scrollOverflow !== 'both') return;
var scrollDistance = scrollStep;
var scrollAvailable = direction === 'left' ? scrollAvailableLeft : scrollAvailableRight; // If there will be less than 25% of the last step visible then scroll to the end
if (scrollAvailable < scrollStep * 1.75) {
scrollDistance = scrollAvailable;
}
if (direction === 'right') {
scrollDistance *= -1;
if (scrollAvailable < scrollStep) {
navScrollerContent.classList.add('snap-align-end');
}
}
navScrollerContent.classList.remove('no-transition');
navScrollerContent.style.transform = 'translateX(' + scrollDistance + 'px)';
scrollingDirection = direction;
scrolling = true;
}; // Set the scroller position and removes transform, called after moveScroller() in the transitionend event
var setScrollerPosition = function setScrollerPosition() {
var style = window.getComputedStyle(navScrollerContent, null);
var transform = style.getPropertyValue('transform');
var transformValue = Math.abs(parseInt(transform.split(',')[4]) || 0);
if (scrollingDirection === 'left') {
transformValue *= -1;
}
navScrollerContent.classList.add('no-transition');
navScrollerContent.style.transform = '';
navScrollerNav.scrollLeft = navScrollerNav.scrollLeft + transformValue;
navScrollerContent.classList.remove('no-transition', 'snap-align-end');
scrolling = false;
}; // Toggle buttons depending on overflow
var toggleButtons = function toggleButtons(overflow) {
if (overflow === 'both' || overflow === 'left') {
navScrollerLeft.classList.add('active');
} else {
navScrollerLeft.classList.remove('active');
}
if (overflow === 'both' || overflow === 'right') {
navScrollerRight.classList.add('active');
} else {
navScrollerRight.classList.remove('active');
}
};
var init = function init() {
setOverflow();
window.addEventListener('resize', function () {
requestSetOverflow();
});
navScrollerNav.addEventListener('scroll', function () {
requestSetOverflow();
});
navScrollerContent.addEventListener('transitionend', function () {
setScrollerPosition();
});
navScrollerLeft.addEventListener('click', function () {
moveScroller('left');
});
navScrollerRight.addEventListener('click', function () {
moveScroller('right');
});
}; // Self init
init(); // Reveal API
return {
init: init
};
};
var _default = PriorityNavScroller;
_exports["default"] = _default;
});