-
Notifications
You must be signed in to change notification settings - Fork 4.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Navigation: Add an option to allow navigation to switch to overlay mode when wrapping #57587
Closed
+341
−35
Closed
Changes from 27 commits
Commits
Show all changes
28 commits
Select commit
Hold shift + click to select a range
266055a
Navigation: Add an option to allow navigation to switch to overlay mo…
scruffian 94eb09a
reinstate the correct nav wrapping check
scruffian 4f49b5f
remove the recursion
scruffian 73e4a0f
fix flicker
scruffian 6ca6911
update navigation block on resize
scruffian 927f0fd
Just get the width, don't bother getting the whole rect
scruffian a90a0ce
Just return when we have a truthful result
scruffian 8685579
replace foreach with map
scruffian fba9042
remove the listener on unmount
scruffian cfccc80
fix the array map
scruffian ae6948d
move to a hook
scruffian 1ed6e61
remove event listener on unmount
scruffian ca3f86c
don't parseInt, we want an exact value
scruffian 02ce2c9
check flex parent when wrapping
scruffian 5de3831
target only direct descendants of the parent
MaggieCabrera f8d132e
fix child selector
MaggieCabrera 55e7728
use parseInt to compare widths
MaggieCabrera aa60154
use a select control instead
scruffian e41111f
update the state when layout changes not just on resize
scruffian f94192c
open the collapsed nav so we can measure to see if we need to hide it
scruffian 1685e57
prevent infinite loop
scruffian df3f335
update to use customselectcontrol
scruffian 3e3acc0
update copy
scruffian e7173c6
reverted UI changes to original state
MaggieCabrera a16e10b
added experiment setting
MaggieCabrera 9e5b861
made auto the default when flag is present
MaggieCabrera 67e700f
rebased and moved changes to WP_Navigation_Block_Renderer new location
MaggieCabrera b3783d0
encapsulate experiment changes
MaggieCabrera File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
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
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,108 @@ | ||
/** | ||
* Determines if the children of a wrapper element are wrapping. | ||
* | ||
* @param {Element} wrapper The element to determine if its children are wrapping | ||
* @param {Array} children The children of the wrapper element | ||
* | ||
* @return {boolean} Whether the children of the wrapper element are wrapping. | ||
*/ | ||
function areItemsWrapping( | ||
wrapper, | ||
children = wrapper.querySelectorAll( | ||
'ul > .wp-block-navigation-item:not(ul ul .wp-block-navigation-item)' | ||
) | ||
) { | ||
const wrapperDimensions = wrapper.getBoundingClientRect(); | ||
const wrapperWidth = wrapperDimensions.width; | ||
//we store an array with the width of each item | ||
const itemsWidths = getItemWidths( children ); | ||
let totalWidth = 0; | ||
|
||
//the nav block may have row-gap applied, which is not calculated in getItemWidths | ||
const computedStyle = window.getComputedStyle( wrapper ); | ||
const rowGap = parseFloat( computedStyle.rowGap ) || 0; | ||
|
||
for ( let i = 0, len = itemsWidths.length; i < len; i++ ) { | ||
totalWidth += itemsWidths[ i ]; | ||
if ( rowGap > 0 && i > 0 ) { | ||
totalWidth += rowGap; | ||
} | ||
|
||
if ( parseInt( totalWidth ) > parseInt( wrapperWidth ) ) { | ||
return true; | ||
} | ||
} | ||
return false; | ||
} | ||
|
||
/** | ||
* Determines if the navigation element itself is wrapping. | ||
* | ||
* @param {Element} navElement Wrapper element of the navigation block. | ||
* @return {boolean} Whether the nav element itself is wrapping. | ||
*/ | ||
function isNavElementWrapping( navElement ) { | ||
//how can we check if the nav element is wrapped inside its parent if we don't know anything about it (the parent)? | ||
//for debugging purposes | ||
const container = getFlexParent( navElement ); | ||
if ( container !== null ) { | ||
return areItemsWrapping( container, Array.from( container.children ) ); | ||
} | ||
|
||
return false; | ||
} | ||
|
||
/** | ||
* Returns an array with the width of each item. | ||
* | ||
* @param {Array} items The items to get the width of. | ||
* @return {Array} An array with the width of each item. | ||
*/ | ||
function getItemWidths( items ) { | ||
return Array.from( items ).map( ( item ) => { | ||
const style = item.currentStyle || window.getComputedStyle( item ); | ||
const itemDimensions = item.getBoundingClientRect(); | ||
const width = parseFloat( itemDimensions.width ); | ||
const marginLeft = parseFloat( style.marginLeft ); | ||
const marginRight = parseFloat( style.marginRight ); | ||
const totalWidth = width + marginLeft + marginRight; | ||
|
||
return totalWidth; | ||
} ); | ||
} | ||
|
||
function getFlexParent( element ) { | ||
// We need to do this check rather than document.body to account for iframes | ||
if ( element.tagName === 'BODY' ) { | ||
// Base case: Stop recursion once we go all the way to the body to avoid infinite recursion | ||
return null; | ||
} | ||
const parent = element.parentNode; | ||
const containerStyles = window.getComputedStyle( parent ); | ||
const isFlexWrap = | ||
containerStyles.getPropertyValue( 'flex-wrap' ) === 'wrap'; | ||
if ( isFlexWrap ) { | ||
return parent; | ||
} | ||
// This recursion checks whether the nav element is wrapped inside a flex container. | ||
return getFlexParent( parent ); | ||
} | ||
|
||
/** | ||
* Determines if the navigation block is wrapping. | ||
* | ||
* @param {Element} navElement Wrapper element of the navigation block. | ||
* @return {boolean} Whether the navigation block is wrapping. | ||
*/ | ||
function navigationIsWrapping( navElement ) { | ||
if ( ! navElement ) { | ||
return false; | ||
} | ||
|
||
return ( | ||
areItemsWrapping( navElement ) === true || | ||
isNavElementWrapping( navElement ) === true | ||
); | ||
} | ||
|
||
export default navigationIsWrapping; |
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,97 @@ | ||
/** | ||
* WordPress dependencies | ||
*/ | ||
import { debounce, useMediaQuery } from '@wordpress/compose'; | ||
import { useState, useLayoutEffect } from '@wordpress/element'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import { NAVIGATION_MOBILE_COLLAPSE } from './constants'; | ||
import navigationIsWrapping from './is-wrapping'; | ||
|
||
function useIsCollapsed( overlayMenu, navRef ) { | ||
const isMobileBreakPoint = useMediaQuery( | ||
`(max-width: ${ NAVIGATION_MOBILE_COLLAPSE })` | ||
); | ||
|
||
// Determines the conditions under which the navigation should be collapsed. | ||
const shouldBeCollapsed = () => { | ||
// If the overlay menu is set to always, then it should always be collapsed. | ||
if ( 'always' === overlayMenu ) { | ||
return true; | ||
} | ||
|
||
// If the overlay menu is set to mobile and the screen is at the mobile breakpoint, then it should be collapsed. | ||
if ( 'mobile' === overlayMenu && isMobileBreakPoint ) { | ||
return true; | ||
} | ||
|
||
// If the overlay menu is set to auto, then we need to check if the navigation is wrapping. | ||
if ( 'auto' === overlayMenu ) { | ||
if ( ! navRef.current ) { | ||
return false; | ||
} | ||
|
||
// If the navigation is already collapsed, then it should stay collapsed. | ||
// We uncollapse it when the screen is resized so that we can measure the full width of the nav. | ||
// It's not ideal to use the actual class name here. | ||
if ( navRef.current.classList.contains( 'is-collapsed' ) ) { | ||
return true; | ||
} | ||
|
||
return navigationIsWrapping( navRef.current ); | ||
} | ||
}; | ||
|
||
const [ isCollapsed, setIsCollapsed ] = useState( shouldBeCollapsed() ); | ||
|
||
// We need a layout effect to respond to changed in isMobileBreakPoint. | ||
useLayoutEffect( () => { | ||
function updateIsCollapsed() { | ||
setIsCollapsed( shouldBeCollapsed() ); | ||
} | ||
|
||
function setIsCollapsedFalse() { | ||
setIsCollapsed( false ); | ||
} | ||
|
||
// This is wrapped in a function so that we can unbind it later. | ||
function debouncedSetIsCollapsedFalse() { | ||
return debounce( setIsCollapsedFalse, 50 ); // Has to be less than debouncedUpdateIsCollapsed. | ||
} | ||
Comment on lines
+60
to
+62
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. At this point it's probably worth moving to a |
||
|
||
// This is wrapped in a function so that we can unbind it later. | ||
function debouncedUpdateIsCollapsed() { | ||
return debounce( updateIsCollapsed, 100 ); | ||
} | ||
|
||
// Set the value of isCollapsed when the effect runs. | ||
updateIsCollapsed(); | ||
|
||
// We only need to add listeners if the overlayMenu is set to auto. | ||
if ( 'auto' === overlayMenu ) { | ||
// Adds a listener to set isCollapsed be false so we can measure the full width of the nav. | ||
window.addEventListener( 'resize', debouncedSetIsCollapsedFalse() ); | ||
|
||
// Then add a debounced listener to update isCollapsed. | ||
window.addEventListener( 'resize', debouncedUpdateIsCollapsed() ); | ||
|
||
// Remove the listener when the component is unmounted. | ||
return () => { | ||
window.removeEventListener( | ||
'resize', | ||
debouncedUpdateIsCollapsed() | ||
); | ||
window.removeEventListener( | ||
'resize', | ||
debouncedSetIsCollapsedFalse() | ||
); | ||
}; | ||
} | ||
} ); | ||
|
||
return isCollapsed; | ||
} | ||
|
||
export default useIsCollapsed; |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Something about this hook is buggy. Its possible to see a wrapped navigation block in the editor.