Skip to content

Commit

Permalink
Add View Transitions as an example
Browse files Browse the repository at this point in the history
  • Loading branch information
markteekman committed Dec 26, 2023
1 parent 1174b7c commit dd298c9
Show file tree
Hide file tree
Showing 4 changed files with 148 additions and 145 deletions.
1 change: 0 additions & 1 deletion src/components/Header.astro
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ import { Icon } from 'astro-icon'

path {
fill: var(--action-color);
transition: fill 0.2s ease-in-out;
}
}

Expand Down
286 changes: 143 additions & 143 deletions src/components/Navigation.astro
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
---
import ResponsiveToggle from './ResponsiveToggle.astro'
import { DarkMode } from 'accessible-astro-components'
import { Image } from "astro:assets"
import logo from "../assets/img/logo.svg"
import { Image } from 'astro:assets'
import logo from '../assets/img/logo.svg'
---

<div id="main-navigation" class="is-desktop py-8">
Expand All @@ -29,180 +29,182 @@ import logo from "../assets/img/logo.svg"
</div>

<script>
// variables
const mainNav = document.querySelector('#main-navigation')
const mainMenu = mainNav.querySelector('ul')
const dropdownMenus = [...document.querySelectorAll('.has-dropdown button')]

// functions
const setActiveMenuItem = () => {
const mobileDesktopMenus = mainNav.querySelectorAll('nav > ul')
const currenPathname = window.location.pathname

mobileDesktopMenus.forEach((menu) => {
const menuItems = [...menu.querySelectorAll('a:not([rel*="external"])')] as HTMLAnchorElement[]

menuItems.forEach((menuItem) => {
if (currenPathname.includes(menuItem.pathname.replaceAll('/', '')) && menuItem.textContent !== 'Home') {
menuItem.classList.add('is-active')
menuItem.setAttribute('aria-current', 'page')
} else if (menuItem.textContent === 'Home' && currenPathname === '/') {
menuItem.classList.add('is-active')
menuItem.setAttribute('aria-current', 'page')
}
document.addEventListener('astro:page-load', () => {
// variables
const mainNav = document.querySelector('#main-navigation')
const mainMenu = mainNav.querySelector('ul')
const dropdownMenus = [...document.querySelectorAll('.has-dropdown button')]

// functions
const setActiveMenuItem = () => {
const mobileDesktopMenus = mainNav.querySelectorAll('nav > ul')
const currenPathname = window.location.pathname

mobileDesktopMenus.forEach((menu) => {
const menuItems = [...menu.querySelectorAll('a:not([rel*="external"])')] as HTMLAnchorElement[]

menuItems.forEach((menuItem) => {
if (currenPathname.includes(menuItem.pathname.replaceAll('/', '')) && menuItem.textContent !== 'Home') {
menuItem.classList.add('is-active')
menuItem.setAttribute('aria-current', 'page')
} else if (menuItem.textContent === 'Home' && currenPathname === '/') {
menuItem.classList.add('is-active')
menuItem.setAttribute('aria-current', 'page')
}
})
})
})
}
}

const checkMenuSize = () => {
const mainNavWidth = mainNav.getBoundingClientRect().width
const desktopMenuWidth = document.querySelector('.desktop-menu').getBoundingClientRect().width
const logoWidthBuffer = 300
const totalMenuWidth = Math.round(desktopMenuWidth) + logoWidthBuffer

if (totalMenuWidth >= mainNavWidth) {
mainNav.classList.remove('is-desktop')
mainNav.classList.add('is-mobile')
} else if (totalMenuWidth <= mainNavWidth) {
mainNav.classList.add('is-desktop')
mainNav.classList.remove('is-mobile')
const checkMenuSize = () => {
const mainNavWidth = mainNav.getBoundingClientRect().width
const desktopMenuWidth = document.querySelector('.desktop-menu').getBoundingClientRect().width
const logoWidthBuffer = 300
const totalMenuWidth = Math.round(desktopMenuWidth) + logoWidthBuffer

if (totalMenuWidth >= mainNavWidth) {
mainNav.classList.remove('is-desktop')
mainNav.classList.add('is-mobile')
} else if (totalMenuWidth <= mainNavWidth) {
mainNav.classList.add('is-desktop')
mainNav.classList.remove('is-mobile')
}
}
}

const isOutOfViewport = (element) => {
const elementBounds = element.getBoundingClientRect()
return elementBounds.right > (window.innerWidth || document.documentElement.clientWidth)
}
const isOutOfViewport = (element) => {
const elementBounds = element.getBoundingClientRect()
return elementBounds.right > (window.innerWidth || document.documentElement.clientWidth)
}

const openDropdownMenu = (dropdownMenu) => {
const dropdownList = dropdownMenu.parentNode.querySelector('ul')
const openDropdownMenu = (dropdownMenu) => {
const dropdownList = dropdownMenu.parentNode.querySelector('ul')

dropdownMenu.classList.add('show')
dropdownMenu.setAttribute('aria-expanded', 'true')
dropdownMenu.classList.add('show')
dropdownMenu.setAttribute('aria-expanded', 'true')

if (isOutOfViewport(dropdownList)) {
dropdownList.style.left = 'auto'
if (isOutOfViewport(dropdownList)) {
dropdownList.style.left = 'auto'
}
}
}

const closeDropdownMenu = (dropdownMenu) => {
dropdownMenu.classList.remove('show')
dropdownMenu.setAttribute('aria-expanded', 'false')
}

const closeAllDropdownMenus = () => {
for (let i = 0; i < dropdownMenus.length; i++) {
closeDropdownMenu(dropdownMenus[i])
const closeDropdownMenu = (dropdownMenu) => {
dropdownMenu.classList.remove('show')
dropdownMenu.setAttribute('aria-expanded', 'false')
}
}

const toggleDropdownMenu = (event) => {
if (event.target.getAttribute('aria-expanded') === 'false') {
closeAllDropdownMenus()
openDropdownMenu(event.target)
} else {
closeDropdownMenu(event.target)
const closeAllDropdownMenus = () => {
for (let i = 0; i < dropdownMenus.length; i++) {
closeDropdownMenu(dropdownMenus[i])
}
}
}

// execution
mainMenu &&
mainMenu.addEventListener('keydown', (event) => {
const element = event.target as Element
const currentMenuItem = element.closest('li')
const menuItems = [...mainMenu.querySelectorAll('.menu-item')]
const currentDropdownMenu = element.closest('.has-dropdown button')
const currentDropdownMenuItem = element.closest('.has-dropdown li')
const currentIndex = menuItems.findIndex((item) => item === currentMenuItem)

const key = event.key
let targetItem

if (key === 'ArrowRight') {
if (menuItems.indexOf(currentMenuItem) === menuItems.length - 1) {
targetItem = menuItems[0]
} else {
targetItem = menuItems[currentIndex + 1]
}
const toggleDropdownMenu = (event) => {
if (event.target.getAttribute('aria-expanded') === 'false') {
closeAllDropdownMenus()
openDropdownMenu(event.target)
} else {
closeDropdownMenu(event.target)
}
}

if (key === 'ArrowLeft') {
if (menuItems.indexOf(currentMenuItem) === 0) {
targetItem = menuItems[menuItems.length - 1]
} else {
targetItem = menuItems[currentIndex - 1]
// execution
mainMenu &&
mainMenu.addEventListener('keydown', (event) => {
const element = event.target as Element
const currentMenuItem = element.closest('li')
const menuItems = [...mainMenu.querySelectorAll('.menu-item')]
const currentDropdownMenu = element.closest('.has-dropdown button')
const currentDropdownMenuItem = element.closest('.has-dropdown li')
const currentIndex = menuItems.findIndex((item) => item === currentMenuItem)

const key = event.key
let targetItem

if (key === 'ArrowRight') {
if (menuItems.indexOf(currentMenuItem) === menuItems.length - 1) {
targetItem = menuItems[0]
} else {
targetItem = menuItems[currentIndex + 1]
}
}
}

if (key === 'Escape') {
targetItem = menuItems[0]
}

if (currentDropdownMenu) {
const firstDropdownItem = currentDropdownMenu.nextElementSibling.querySelector('li')

if (key === 'ArrowDown') {
event.preventDefault()
openDropdownMenu(currentDropdownMenu)
targetItem = firstDropdownItem
if (key === 'ArrowLeft') {
if (menuItems.indexOf(currentMenuItem) === 0) {
targetItem = menuItems[menuItems.length - 1]
} else {
targetItem = menuItems[currentIndex - 1]
}
}

if (key === 'Escape') {
closeDropdownMenu(currentDropdownMenu)
targetItem = menuItems[0]
}
}

if (currentDropdownMenuItem) {
const currentDropdownList = currentDropdownMenuItem.parentNode
const dropdownMenuItems = [...currentDropdownList.querySelectorAll('li')]
const currentIndex = dropdownMenuItems.findIndex((item) => item === currentDropdownMenuItem)
if (currentDropdownMenu) {
const firstDropdownItem = currentDropdownMenu.nextElementSibling.querySelector('li')

if (key === 'ArrowDown') {
event.preventDefault()
if (key === 'ArrowDown') {
event.preventDefault()
openDropdownMenu(currentDropdownMenu)
targetItem = firstDropdownItem
}

if (dropdownMenuItems.indexOf(currentDropdownMenuItem as HTMLLIElement) === dropdownMenuItems.length - 1) {
targetItem = dropdownMenuItems[0]
} else {
targetItem = dropdownMenuItems[currentIndex + 1]
if (key === 'Escape') {
closeDropdownMenu(currentDropdownMenu)
}
}

if (key === 'ArrowUp') {
event.preventDefault()
if (currentDropdownMenuItem) {
const currentDropdownList = currentDropdownMenuItem.parentNode
const dropdownMenuItems = [...currentDropdownList.querySelectorAll('li')]
const currentIndex = dropdownMenuItems.findIndex((item) => item === currentDropdownMenuItem)

if (dropdownMenuItems.indexOf(currentDropdownMenuItem as HTMLLIElement) === 0) {
targetItem = dropdownMenuItems[dropdownMenuItems.length - 1]
} else {
targetItem = dropdownMenuItems[currentIndex - 1]
if (key === 'ArrowDown') {
event.preventDefault()

if (dropdownMenuItems.indexOf(currentDropdownMenuItem as HTMLLIElement) === dropdownMenuItems.length - 1) {
targetItem = dropdownMenuItems[0]
} else {
targetItem = dropdownMenuItems[currentIndex + 1]
}
}
}

if (key === 'Escape') {
const currentDropdownMenu = (currentDropdownList as Element).previousElementSibling
targetItem = currentDropdownMenu.parentNode
closeAllDropdownMenus()
if (key === 'ArrowUp') {
event.preventDefault()

if (dropdownMenuItems.indexOf(currentDropdownMenuItem as HTMLLIElement) === 0) {
targetItem = dropdownMenuItems[dropdownMenuItems.length - 1]
} else {
targetItem = dropdownMenuItems[currentIndex - 1]
}
}

if (key === 'Escape') {
const currentDropdownMenu = (currentDropdownList as Element).previousElementSibling
targetItem = currentDropdownMenu.parentNode
closeAllDropdownMenus()
}
}
}

if (targetItem) {
targetItem.querySelector('a, button, input').focus()
}
})
if (targetItem) {
targetItem.querySelector('a, button, input').focus()
}
})

dropdownMenus &&
dropdownMenus.forEach((dropdownMenu) => {
dropdownMenu.addEventListener('click', toggleDropdownMenu)
})
dropdownMenus &&
dropdownMenus.forEach((dropdownMenu) => {
dropdownMenu.addEventListener('click', toggleDropdownMenu)
})

setActiveMenuItem()
checkMenuSize()
setActiveMenuItem()
checkMenuSize()

window.addEventListener('resize', checkMenuSize)
window.addEventListener('click', (event) => {
const element = event.target as Element
if (!element.hasAttribute('aria-haspopup') && !element.classList.contains('submenu-item')) {
closeAllDropdownMenus()
}
window.addEventListener('resize', checkMenuSize)
window.addEventListener('click', (event) => {
const element = event.target as Element
if (!element.hasAttribute('aria-haspopup') && !element.classList.contains('submenu-item')) {
closeAllDropdownMenus()
}
})
})
</script>

Expand Down Expand Up @@ -272,7 +274,6 @@ import logo from "../assets/img/logo.svg"
text-decoration: none;
font-size: 1.125rem;
line-height: 1.6875rem;
transition: color 0.15s ease-in-out;
}

a:hover,
Expand Down Expand Up @@ -386,7 +387,6 @@ import logo from "../assets/img/logo.svg"

svg path {
fill: var(--action-color);
transition: fill 0.2s ease-in-out;
}

&:hover {
Expand Down
4 changes: 4 additions & 0 deletions src/components/SiteMeta.astro
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
---
import { ViewTransitions } from 'astro:transitions'
const { title, description, url, image, author } = Astro.props
let subtitle = 'Accessible Astro Starter'
Expand All @@ -20,3 +22,5 @@ let subtitle = 'Accessible Astro Starter'

<!-- page title -->
<title>{title} - {subtitle}</title>

<ViewTransitions />
2 changes: 1 addition & 1 deletion src/layouts/DefaultLayout.astro
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ const {
</head>
<body>
<Header />
<main id="main-content">
<main id="main-content" transition:animate="fade">
<slot />
</main>
<Footer />
Expand Down

0 comments on commit dd298c9

Please sign in to comment.