generator-addin
Version:
MyGeotab/Geotab Drive add-in generator
217 lines (198 loc) • 8.96 kB
JavaScript
class NavHandler {
/**
* Params handed in from login.js. Allows use of focus and blur from navbar
* interactions
*
* @param {object} api - GeotabAPI object
* @param {object} state - mock addin state - Contains addin controls (blur, etc.)
*/
constructor(navFact, props){
this.navFact = navFact;
this.props = props;
this.focus = true;
}
/**
* Assigns and handles behaviour of the navbar when clicked
*
* @param {event} event click event for the click
* @param {object} self DOM Element making the click
* @param {HTMLCollection} menuHeaders The headers of the nav bar
* @param {object} floatingMenu DOM Element for the floating menu bar - appears when navbar is collapsed
* @param {bool} navigationBarExtended
* @param {bool} submenu if the item being clicked has a submenu or not -> Changes the way the headers behave
*/
clickHandler(event, self, menuHeaders, floatingMenu, navigationBarExtended, submenu=false){
// Add active state to clicked nav button if not already selected
if(!self.className.includes('activeStateButton')){
self.className = self.className += ' activeStateButton';
}
let parent = self.parentElement;
// Handling submenu expansion
if(navigationBarExtended){
if(parent.className.includes(' mainMenuHeaderExpanded')){
parent.className = parent.className.replace(' mainMenuHeaderExpanded', '');
} else {
parent.className = parent.className += ' mainMenuHeaderExpanded';
}
} else {// Showing the floating bar if it is not visible
// Ensuring the header has a submenu -> parent.children[1] is an associated submenu
if(parent.children[1] !== undefined){
// Making the floating menu a duplicate of the submenu associated with the header
floatingMenu.innerHTML = parent.children[1].innerHTML;
if(floatingMenu.style.display == 'block'){
floatingMenu.style.display = 'none';
} else {
floatingMenu.style.display = 'block';
floatingMenu.style.top = `${event.y - 50}px`; // Rough approximation
}
// Adding listeners to each floating menu item to close the floating menu when selected
let floatingListElements = floatingMenu.children[0].children;
for (let j = 0; j < floatingListElements.length; j++) {
floatingListElements[j].children[0].addEventListener('click', function(){
floatingMenu.style.display = 'none';
});
}
} else {
floatingMenu.style.display = 'none';
}
}
// Iterating the clicked header's siblings
for(let i=0; i<menuHeaders.length; i++){
let sibling = menuHeaders[i].children[0];
// Will remove the highlighting of main menu provided the selection isn't in a submenu
if(sibling !== self && !submenu){
sibling.className = sibling.className.replace(' activeStateButton', '');
}
// Closing other open submenus
if(menuHeaders[i] !== parent && !submenu){
if(menuHeaders[i].className.includes('mainMenuHeaderExpanded')){
menuHeaders[i].className = menuHeaders[i].className.replace('mainMenuHeaderExpanded', '');
}
}
}
}
/**
Generates HTML based on the props.js file using NavFactory to
* scaffold out the components
*
* Grabs references and adds event listeners
*/
generateContent(){
// Self defined for referencing clickHandler
let self = this;
this.createNavBar();
// Referencing the new navbar
let navigationBar = document.getElementById('menuId');
let toggleButton = document.getElementById('menuToggle');
let centerPane = document.getElementById('<%= root %>');
let chevronIcon = toggleButton.children.item(0);
let menuHeaders = document.getElementsByClassName('mainMenuHeader');
let floatingMenu = document.getElementById('hiddenMenu');
let navigationBarExtended = true;
// Handling Navbar pop in/out
toggleButton.addEventListener('click', ()=>{
if(!navigationBarExtended){
navigationBar.className = navigationBar.className.replace('menuCollapsed', '');
centerPane.style.left = '250px';
chevronIcon.style.transform = 'rotate(0deg)';
} else {
navigationBar.className += ' menuCollapsed';
centerPane.style.left = '50px';
chevronIcon.style.transform = 'rotate(180deg)';
}
// Closing the floating menu
floatingMenu.style.display = 'none';
// Closing any open menu headers
for(let i=0; i<menuHeaders.length; i++){
menuHeaders[i].className = menuHeaders[i].className.replace(' mainMenuHeaderExpanded', '');
}
// Inverting extended status
navigationBarExtended = !navigationBarExtended
});
// Handling Button click (Highlighting)
for(let i=0; i<menuHeaders.length; i++){
let submenu = menuHeaders[i].children[1];
if(submenu){
let menuItems = submenu.children[0].children;
// Adding in the selection and menu handlers for submenu items
if(navigationBarExtended){
for (let j = 0; j < menuItems.length; j++) {
menuItems[j].children[0].addEventListener('click', function(event){
self.clickHandler(event, this, menuHeaders, floatingMenu, navigationBarExtended, true);
});
}
}
}
// All menu headers require the functionality
menuHeaders[i].children[0].addEventListener('click', function(event){
self.clickHandler(event, this, menuHeaders, floatingMenu, navigationBarExtended);
});
}
}
createNavBar(){
// Generating Navbar
let navHTML = ``;
let navBase = document.getElementById('navBase');
let headerCount = 0;
// Reading the JSON props file and building out the navbar
this.props.forEach(prop => {
prop.id = headerCount;
// Main header
navHTML += this.navFact.openMainHeader(prop);
if(prop.hasSubmenu){
// Sub Menu
navHTML += this.navFact.openSubMenu(prop);
prop.submenuItems.forEach( item => {
// Sub header
navHTML += this.navFact.subHeader(item);
})
navHTML += this.navFact.closeSubMenu();
}
navHTML += this.navFact.closeMainHeader();
headerCount++;
});
navBase.innerHTML = navHTML;
}
updateMenuItem(){
this.createNavBar();
}
/**
* Toggles the development addin
* allows user to see any processes that take place when the addin is blurred/focused
*/
enableDisplayToggle(){
let displayToggle = document.getElementById('toggleBtn');
displayToggle.addEventListener('click', () => {
if(this.focus){
displayToggle.innerHTML = 'Focus add-in';
global.geotab.addin.<%= root%>.blur();
} else {
displayToggle.innerHTML = 'Blur add-in';
global.geotab.addin.<%= root%>.focus(global.api, global.state);
}
this.focus = !this.focus;
});
}
/**
* Hash listener -> Due to the call stack order, it's necessary to repeatedly check for this
* during page operation. This is what we base the blur() effect on. Without this, when we
* try to update the hash, we get the previous one, causing the update to be one behind the
* active hash.
*
* onHashChange does not appear to be supported as an event in this browser
*/
listenToHash(){
this.storedHash = window.location.hash;
setInterval(()=>{
if(window.location.hash !== this.storedHash){
this.storedHash = window.location.hash;
if(this.storedHash !== '#' + '<%= root %>'){
geotab.addin.<%= root %>.blur();
} else {
geotab.addin.<%= root %>.focus(global.api, global.state);
}
}
}, 100);
}
}
module.exports = NavHandler;