Last active
April 19, 2023 10:43
-
-
Save endymion1818/1cc07dbedc37c430f0f9f30692d0db49 to your computer and use it in GitHub Desktop.
toggles
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
document.addEventListener("DOMContentLoaded", function() { | |
// Theme Toggle | |
const themeToggle = document.getElementById("dark-mode-toggle"); | |
const themeToggleCircle = document.getElementById("dark-mode-toggle-circle"); | |
// Check if the user has set a theme preference set in localStorage or if the browser preferences | |
if (localStorage.theme === 'dark' || (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)) { | |
document.documentElement.classList.add('dark') | |
} else { | |
document.documentElement.classList.remove('dark') | |
} | |
// Toggle the "dark" class on the <html> element when the button is clicked | |
themeToggle?.addEventListener("click", function() { | |
document.documentElement.classList.toggle("dark"); | |
themeToggleCircle.classList.toggle("translate-x-5"); | |
// Save the user preference in localStorage | |
if (document.documentElement.classList.contains("dark")) { | |
localStorage.setItem("theme", "dark"); | |
} else { | |
localStorage.setItem("theme", "light"); | |
} | |
}); | |
// All Mega Menu buttons | |
const megaMenuButtons = document.querySelectorAll('.mega-menu-toggle'); | |
// All mega menus | |
const megaMenus = document.querySelectorAll('.mega-menu'); | |
// Timeout delay so we can add a transition then hide the menu | |
const timeoutDelay = 300 | |
// For each button | |
megaMenuButtons?.forEach(megaMenuButton => { | |
// listen for clicks on this buttons | |
megaMenuButton.addEventListener('click', function() { | |
// Get the targeted menu | |
const megaMenu = document.getElementById(megaMenuButton.dataset.toggle); | |
// and the button icon | |
const megaMenuIcon = megaMenuButton.querySelector('svg'); | |
// Is the menu open already? | |
const isExpanded = megaMenuButton.getAttribute('aria-expanded'); | |
console.log(megaMenuButton.getAttribute('aria-expanded')) | |
// If the menu is open, close it | |
if(isExpanded === 'true') { | |
megaMenu.classList.add('animate-fade-out-down'); | |
megaMenuButton.classList.remove('bg-primary-800', 'dark:bg-zinc-800'); | |
megaMenuIcon.classList.remove('rotate-180'); | |
megaMenuButton.setAttribute("aria-expanded", 'false'); | |
setTimeout(() => { | |
megaMenu.classList.add('hidden'); | |
megaMenu.classList.remove('block', 'animate-fade-in-up'); | |
}, timeoutDelay); | |
return; | |
} | |
// Toggle classes on the targeted menu | |
megaMenu.classList.remove('hidden', 'animate-fade-out-down'); | |
megaMenu.classList.add('block', 'animate-fade-in-up'); | |
// Toggle classes and attributes on the button | |
megaMenuButton.classList.add('bg-primary-800', 'dark:bg-zinc-800'); | |
megaMenuButton.setAttribute("aria-expanded", 'true'); | |
megaMenuIcon.classList.add('rotate-180'); | |
// Toggle classes on all other mega menus | |
megaMenus.forEach(megaMenuItem => { | |
// but only if they're not the current menu | |
if(megaMenuItem.id !== megaMenu.id) { | |
megaMenuItem.classList.add('animate-fade-out-down'); | |
megaMenuItem.classList.remove('animate-fade-in-up'); | |
setTimeout(function() { | |
megaMenuItem.classList.add('hidden'); | |
megaMenu.classList.remove('block', 'animate-fade-in-up'); | |
}, timeoutDelay); | |
} | |
}); | |
// Toggle classes on all other mega menu buttons | |
megaMenuButtons.forEach(megaMenuButtonItem => { | |
// but only if they're not the current menu button | |
if(megaMenuButtonItem.dataset.toggle !== megaMenu.id) { | |
megaMenuButtonItem.classList.remove('bg-primary-800', 'dark:bg-zinc-800'); | |
megaMenuButtonItem.querySelector('svg').classList.remove('rotate-180'); | |
megaMenuButtonItem.setAttribute("aria-expanded", 'false'); | |
} | |
}) | |
}); | |
}); | |
// Event listener for all toggle buttons in the DOM | |
const tabToggles = document.querySelectorAll('.tab-toggle'); | |
// We want this to be relative to the parent element | |
// So do nothing else at this stage | |
// Except listen for clicks on this buttons | |
tabToggles?.forEach(tabToggle => { | |
// Add event for each toggle | |
tabToggle.addEventListener('click', function() { | |
// change color of toggle button | |
tabToggle.classList.remove('text-zinc-400', 'border-b-transparent'); | |
tabToggle.classList.add('text-primary-600', 'border-b-primary-600'); | |
tabToggle.setAttribute("aria-expanded", true); | |
// reset the color of all other toggle buttons | |
Object.values(tabToggles).forEach(tabToggleItem => { | |
if(tabToggleItem.dataset.toggle !== tabToggle.dataset.toggle) { | |
tabToggleItem.classList.remove('text-primary-600', 'border-b-primary-600'); | |
tabToggleItem.classList.add('text-zinc-400', 'border-b-transparent'); | |
} | |
}); | |
// Get the parent element for this toggle group | |
const tabParent = tabToggle.parentElement.parentElement; | |
// get adjacent panes | |
const tabPanes = tabParent.getElementsByClassName(`tab-pane`); | |
// Get the targeted pane | |
const tabPane = tabParent.querySelector(`#${tabToggle.dataset.toggle}`); | |
// toggle classes on the targeted pane | |
tabPane.classList.add('translate-x-15'); | |
setTimeout(function() { | |
tabPane.classList.remove('hidden'); | |
tabPane.classList.add('block'); | |
}, timeoutDelay); | |
// toggle classes on all adjacent panes | |
Object.values(tabPanes).forEach(tabPaneItem => { | |
// But only if it's not the targeted pane | |
if(tabPaneItem.id !== tabToggle.dataset.toggle) { | |
setTimeout(function() { | |
tabPaneItem.classList.remove('block', 'translate-x-15'); | |
tabPaneItem.classList.add('hidden'); | |
}, timeoutDelay); | |
} | |
}); | |
tabToggles.forEach(tabToggleItem => { | |
if(tabToggleItem.dataset.toggle !== tabToggle.dataset.toggle) { | |
tabToggleItem.setAttribute("aria-expanded", false); | |
} | |
}); | |
}); | |
}); | |
// Toggle the search open on click | |
const searchButtons = document.querySelectorAll('.search-menu-toggle'); | |
// loop through each search button | |
searchButtons?.forEach(searchButton => { | |
// listen for clicks on this button | |
searchButton.addEventListener('click', function() { | |
searchButton.classList.add('bg-primary-800', 'dark:bg-zinc-800', 'rounded-l-md'); | |
searchButton.classList.toggle('rounded-md'); | |
// get the element containing the search input | |
const searchMenu = searchButton.parentElement.querySelector('.search-menu-container'); | |
searchMenu.classList.toggle('hidden'); | |
searchMenu.classList.toggle('block'); | |
searchMenu.querySelector('input').focus(); | |
}); | |
}); | |
// Toggle the location menu open on click | |
const locationButtons = document.querySelectorAll('.location-menu-toggle'); | |
locationButtons.forEach(locationButton => { | |
const locationMenu = locationButton.parentElement.querySelector('.location-menu-container'); | |
locationButton.addEventListener('click', function() { | |
locationButton.querySelector('svg').classList.toggle('rotate-180'); | |
locationMenu.classList.toggle('hidden'); | |
locationMenu.classList.toggle('block'); | |
}); | |
}); | |
// Toggle mobile nav - Mark, this pattern can be re-used everywhere | |
const toggleMobileNav = document.querySelectorAll( | |
'button[data-toggle="mobile-menu"]' | |
); | |
toggleMobileNav?.forEach((toggleMobileNav) => { | |
toggleMobileNav.addEventListener("click", () => { | |
// Stop the body from scrolling when the menu is open | |
document.body.classList.toggle("overflow-hidden"); | |
// for the menu itself | |
const target = document.querySelector( | |
`#${toggleMobileNav.dataset.toggle}` | |
); | |
target.classList.toggle("hidden"); | |
target.classList.toggle("block"); | |
// for the button and icon | |
toggleMobileNav.classList.toggle("bg-primary-800"); | |
toggleMobileNav.querySelector("#icon-open").classList.toggle("hidden"); | |
toggleMobileNav.querySelector("#icon-open").classList.toggle("rotate-180"); | |
toggleMobileNav.setAttribute("aria-expanded", `${!(toggleMobileNav.getAttribute('aria-expanded') === 'true')}`); | |
toggleMobileNav | |
.querySelector("#icon-closed") | |
.classList.toggle("hidden"); | |
}); | |
}); | |
// Nice API for toggling any amount of dropdowns | |
const dropdownToggles = document.querySelectorAll('button[data-dropdown]'); | |
dropdownToggles?.forEach(dropdownToggle => { | |
dropdownToggle.addEventListener('click', function (e) { | |
const hasTransform = !dropdownToggle.querySelector('svg').classList.contains('no-rotate'); | |
hasTransform && dropdownToggle.querySelector('svg').classList.toggle('tw-rotate-180'); | |
const target = document.querySelector(`.${dropdownToggle.dataset.dropdown}`); | |
target.classList.toggle('tw-hidden'); | |
target.classList.toggle('tw-block'); | |
target.setAttribute("aria-expanded", "true"); | |
}); | |
}); | |
// Also close the menu and search if anywhere outside the menu or search is clicked | |
document.body.addEventListener('click', function(e) { | |
const isOnMenuOrMenuButton = e.target.closest('.mega-menu-toggle') !== null | |
|| e.target.closest('.mega-menu') !== null | |
megaMenus?.forEach(megaMenu => { | |
// If the click is on the menu button, do nothing | |
if(isOnMenuOrMenuButton) { | |
return | |
} | |
// otherwise close the menu | |
megaMenu.classList.add('animate-fade-out-down'); | |
megaMenu.classList.remove('animate-fade-in-up'); | |
setTimeout(function() { | |
megaMenu.classList.add('hidden'); | |
megaMenu.classList.remove('block', 'animate-fade-in-up'); | |
}, timeoutDelay); | |
}); | |
// reset the button state | |
megaMenuButtons.forEach(megaMenuButton => { | |
if(isOnMenuOrMenuButton) { | |
return | |
} | |
megaMenuButton.setAttribute("aria-expanded", "false"); | |
megaMenuButton.classList.remove('bg-primary-800', 'dark:bg-zinc-800'); | |
megaMenuButton.querySelector('svg').classList.remove('rotate-180'); | |
}); | |
searchButtons.forEach(searchButton => { | |
const searchMenu = searchButton.parentElement.querySelector('.search-menu-container'); | |
// If the click is on the menu button, do nothing | |
if( | |
e.target.closest('.search-menu-toggle') !== null | |
|| e.target.closest('input[type=search]') !== null | |
) { | |
return | |
} | |
searchButton.classList.remove('bg-primary-800', 'dark:bg-zinc-800', 'rounded-l-md') | |
searchButton.classList.add('rounded-md'); | |
searchMenu.classList.add('hidden'); | |
searchMenu.classList.remove('block'); | |
}); | |
locationButtons.forEach(locationButton => { | |
const locationMenu = locationButton.parentElement.querySelector('.location-menu-container'); | |
// If the click is on the menu button, do nothing | |
if( | |
e.target.closest('.location-menu-toggle') !== null | |
|| e.target.closest('.location-menu-container') !== null | |
) { | |
return | |
} | |
locationButton.querySelector('svg').classList.remove('rotate-180'); | |
locationMenu.classList.add('hidden'); | |
locationMenu.classList.remove('block'); | |
}); | |
dropdownToggles.forEach(dropdownToggle => { | |
const isNearDropdownOrTarget = | |
e.target.closest('button[data-dropdown]') !== null; | |
console.log({isNearDropdownOrTarget}) | |
if(!isNearDropdownOrTarget) { | |
const target = document.querySelector(`.${dropdownToggle.dataset.dropdown}`); | |
console.log({target}) | |
target.classList.remove('tw-block'); | |
target.classList.add('tw-hidden'); | |
} | |
}); | |
}) | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Also add this to your
tailwind.config.js
: