-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #268 from ucsc/feature/ucsc-134/fe-sidebar-menu
[UCSC-134] Sidebar Menu
- Loading branch information
Showing
6 changed files
with
334 additions
and
9 deletions.
There are no files selected for viewing
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
const el = document.querySelectorAll( '.ucsc__sidebar-with-navigation' ); | ||
|
||
const selectors = { | ||
itemSelector: '.wp-block-navigation-item', | ||
itemExpandedClass: 'ucsc__sidebar-with-navigation--expanded', | ||
linkSelector: 'a.wp-block-navigation-item__content', | ||
toggleSelector: '.wp-block-navigation-submenu__toggle', | ||
submenuWrapperClass: 'wp-block-navigation__submenu-transition-wrapper', | ||
submenuSelector: '.wp-block-navigation__submenu-container', | ||
}; | ||
|
||
/** | ||
* Show or hide the submenu associated with a given toggle button. | ||
* | ||
* @param {HTMLElement} toggle The actioned submenu toggle button. | ||
*/ | ||
const toggleSubmenu = ( toggle ) => { | ||
const item = toggle.closest( selectors.itemSelector ); | ||
const submenu = item.querySelector( selectors.submenuSelector ); | ||
|
||
// Hide | ||
if ( item.classList.contains( selectors.itemExpandedClass ) ) { | ||
item.classList.remove( selectors.itemExpandedClass ); | ||
submenu.setAttribute( 'aria-hidden', true ); | ||
submenu | ||
.querySelectorAll( selectors.linkSelector ) | ||
.forEach( ( subItemLink ) => { | ||
subItemLink.setAttribute( 'tabIndex', -1 ); | ||
} ); | ||
} | ||
// Show | ||
else { | ||
item.classList.add( selectors.itemExpandedClass ); | ||
submenu.setAttribute( 'aria-hidden', false ); | ||
submenu | ||
.querySelectorAll( selectors.linkSelector ) | ||
.forEach( ( subItemLink ) => { | ||
subItemLink.removeAttribute( 'tabIndex' ); | ||
} ); | ||
} | ||
}; | ||
|
||
/** | ||
* Fix toggle button ARIA attributes, which the core block JS sets incorrectly | ||
* since we've disabled accordion behavior. | ||
*/ | ||
const fixAriaAttributes = () => { | ||
el.forEach( ( block ) => { | ||
block.querySelectorAll( selectors.itemSelector ).forEach( ( item ) => { | ||
const isExpanded = item.classList.contains( | ||
selectors.itemExpandedClass | ||
); | ||
|
||
const toggle = item.querySelector( selectors.toggleSelector ); | ||
if ( toggle ) { | ||
toggle.setAttribute( 'aria-expanded', isExpanded ); | ||
} | ||
|
||
const submenu = item.querySelector( selectors.submenuSelector ); | ||
if ( submenu ) { | ||
toggle.setAttribute( 'aria-hidden', ! isExpanded ); | ||
} | ||
} ); | ||
} ); | ||
}; | ||
|
||
/** | ||
* Bind event listeners. | ||
*/ | ||
const bindEvents = () => { | ||
el.forEach( ( block ) => { | ||
block | ||
.querySelectorAll( selectors.toggleSelector ) | ||
.forEach( ( toggle ) => { | ||
toggle.addEventListener( 'click', () => { | ||
toggleSubmenu( toggle ); | ||
} ); | ||
} ); | ||
|
||
block | ||
.querySelectorAll( '.' + selectors.submenuWrapperClass ) | ||
.forEach( ( wrapper ) => { | ||
wrapper.addEventListener( 'transitionend', () => { | ||
fixAriaAttributes(); | ||
} ); | ||
} ); | ||
} ); | ||
}; | ||
|
||
/** | ||
* Set up custom block markup. | ||
*/ | ||
const initBlockMarkup = () => { | ||
el.forEach( ( block ) => { | ||
block | ||
.querySelectorAll( selectors.submenuSelector ) | ||
.forEach( ( submenu ) => { | ||
// Initialize submenu ARIA attribute. | ||
submenu.setAttribute( 'aria-hidden', true ); | ||
submenu | ||
.querySelectorAll( selectors.linkSelector ) | ||
.forEach( ( subItemLink ) => { | ||
subItemLink.setAttribute( 'tabIndex', -1 ); | ||
} ); | ||
|
||
// Add a wrapper around submenus in order to do height transitions. | ||
const item = submenu.closest( selectors.itemSelector ); | ||
const wrapper = document.createElement( 'div' ); | ||
wrapper.classList.add( selectors.submenuWrapperClass ); | ||
item.append( wrapper ); | ||
wrapper.append( submenu ); | ||
} ); | ||
|
||
// Remove nested submenus and their toggles. | ||
block | ||
.querySelectorAll( | ||
`${ selectors.submenuSelector } ${ selectors.submenuSelector }` | ||
) | ||
.forEach( ( nestedSubmenu ) => { | ||
const item = nestedSubmenu.closest( selectors.itemSelector ); | ||
const toggle = item.querySelector( selectors.toggleSelector ); | ||
toggle.remove(); | ||
nestedSubmenu.remove(); | ||
} ); | ||
} ); | ||
}; | ||
|
||
/** | ||
* Initialize custom block behaviors. | ||
*/ | ||
const init = () => { | ||
if ( ! el.length ) { | ||
return; | ||
} | ||
|
||
initBlockMarkup(); | ||
bindEvents(); | ||
fixAriaAttributes(); | ||
}; | ||
|
||
export default init; |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
/** | ||
* @module block-patterns | ||
* @description clearing house for all block pattern behaviors. | ||
*/ | ||
|
||
import sidebarWithNavigation from '../block-patterns/sidebar-with-navigation.js'; | ||
|
||
const init = () => { | ||
sidebarWithNavigation(); | ||
}; | ||
|
||
export default init; |
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,167 @@ | ||
/* BLOCK PATTERN: Content with left navigation */ | ||
|
||
.ucsc__sidebar-with-navigation { | ||
flex-direction: column-reverse; | ||
|
||
@include media-query($wp-columns-unstack) { | ||
flex-direction: row; | ||
} | ||
|
||
// Block | ||
.wp-block-navigation { | ||
margin-top: 0; | ||
|
||
.wp-block-navigation-item__content { | ||
|
||
&:hover, | ||
&:focus-visible { | ||
text-decoration: underline; | ||
text-decoration-color: var(--wp--preset--color--ucsc-primary-yellow); | ||
text-decoration-skip-ink: auto; | ||
text-decoration-thickness: 0.12rem; | ||
text-underline-offset: 0.12em; | ||
} | ||
|
||
&[aria-current="page"] { | ||
font-weight: 700; | ||
} | ||
} | ||
|
||
.wp-block-navigation__submenu-container .wp-block-navigation-item__content { | ||
|
||
&:focus-visible { | ||
outline-offset: -1px; | ||
} | ||
} | ||
|
||
// Navigation menu | ||
> .wp-block-navigation__container { | ||
width: 100%; | ||
gap: 0; | ||
|
||
// Top-level menu item | ||
> .wp-block-navigation-item { | ||
width: 100%; | ||
display: grid; | ||
grid-template-columns: 1fr auto; | ||
grid-template-rows: auto 0fr; | ||
grid-template-areas: | ||
"link toggle" | ||
"submenu submenu"; | ||
border-top: 1px solid var(--wp--preset--color--light-gray); | ||
transition: grid-template-rows 0.25s ease; | ||
|
||
&:last-child { | ||
border-bottom: 1px solid var(--wp--preset--color--light-gray); | ||
} | ||
|
||
// Top-level menu link | ||
> .wp-block-navigation-item__content { | ||
grid-area: link; | ||
padding: 12px 0; | ||
display: block; | ||
} | ||
|
||
// Submenu toggle button | ||
> .wp-block-navigation-submenu__toggle { | ||
grid-area: toggle; | ||
display: flex; | ||
height: 100%; | ||
min-width: 32px; | ||
align-items: flex-start; | ||
justify-content: flex-end; | ||
color: inherit; | ||
position: relative; | ||
|
||
&::before { | ||
content: ""; | ||
display: none; | ||
width: 28px; | ||
height: calc(100% - 2px); | ||
position: absolute; | ||
border: 2px solid #000; | ||
border-radius: 3px; | ||
transform: translateX(5px); | ||
} | ||
|
||
&::after { | ||
content: ""; | ||
display: block; | ||
width: 10px; | ||
height: 10px; | ||
border-width: 1px 1px 0 0; | ||
border-style: solid; | ||
border-color: currentcolor; | ||
transform: translate(-5px, 12px) rotate(-225deg); | ||
transition: transform 0.25s ease; | ||
} | ||
|
||
&:hover, | ||
&:focus-visible { | ||
outline: 0 none; | ||
|
||
&::after { | ||
border-width: 2px 2px 0 0; | ||
} | ||
} | ||
|
||
&:focus-visible { | ||
|
||
&::before { | ||
display: block; | ||
} | ||
} | ||
|
||
svg { | ||
display: none; | ||
} | ||
} | ||
|
||
// Unwrapped submenu prior to JS initialization | ||
> .wp-block-navigation__submenu-container { | ||
display: none; | ||
} | ||
|
||
// Submenu transition wrapper | ||
> .wp-block-navigation__submenu-transition-wrapper { | ||
grid-area: submenu; | ||
display: grid; | ||
grid-template-rows: 0fr; | ||
transition: grid-template-rows 0.25s ease; | ||
|
||
// Submenu | ||
> .wp-block-navigation__submenu-container { | ||
position: static; | ||
width: 100%; | ||
background-color: transparent; | ||
border: 0 none; | ||
color: inherit; | ||
height: auto; | ||
opacity: 1; | ||
overflow: hidden; | ||
visibility: visible; | ||
|
||
// Hide everything beyond the top two levels of the menu. | ||
.wp-block-navigation-submenu__toggle, | ||
.wp-block-navigation__submenu-container { | ||
display: none; | ||
} | ||
} | ||
} | ||
|
||
// Top-level menu item with submenu expanded | ||
&.ucsc__sidebar-with-navigation--expanded { | ||
|
||
> .wp-block-navigation-submenu__toggle::after { | ||
transform: translate(-5px, 18px) rotate(-45deg); | ||
} | ||
|
||
> .wp-block-navigation__submenu-transition-wrapper { | ||
grid-template-rows: 1fr; | ||
padding-bottom: 12px; | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} |
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