Skip to content

Commit

Permalink
Make the inline breakpoint configurable for each container
Browse files Browse the repository at this point in the history
This allows individual containers to become inline at different breakpoints,
eg. the Chapter List remaining fixed and scrolling when the ToC is already displayed inline.
  • Loading branch information
adamwoodnz committed Jan 30, 2024
1 parent 7773409 commit 1f0b446
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 48 deletions.
4 changes: 3 additions & 1 deletion mu-plugins/blocks/sidebar-container/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,13 @@ function render( $attributes, $content, $block ) {
esc_html__( '↑ Back to top', 'wporg' )
)
: '';
$inlineBreakpoint = $attributes['inlineBreakpoint'];

$wrapper_attributes = get_block_wrapper_attributes();
return sprintf(
'<div %1$s>%2$s%3$s</div>',
'<div %1$s data-breakpoint="%2$s">%3$s%4$s</div>',
$wrapper_attributes,
esc_attr( $inlineBreakpoint ),
$content,
$back_to_top
);
Expand Down
52 changes: 33 additions & 19 deletions mu-plugins/blocks/sidebar-container/postcss/style.pcss
Original file line number Diff line number Diff line change
@@ -1,21 +1,34 @@
.wp-block-wporg-sidebar-container .is-link-to-top {
display: none;
.wp-block-wporg-sidebar-container {
--local--offset-top: var(--wp-admin--admin-bar--height, 0);

& a {
text-decoration-line: none;
/* These vars are used in JS calcs */
--local--nav--offset: var(--wp--custom--local-navigation-bar--spacing--height, 60px);
--local--padding-top: var(--wp--preset--spacing--20);

&:hover {
text-decoration-line: underline;
background-color: aliceblue;

/* Account for local nav height on larger screens where it becomes fixed. */
@media (min-width: 890px) {
/* stylelint-disable-next-line length-zero-no-unit */
--local--nav--offset: 0px;
--local--offset-top: calc(var(--wp-admin--admin-bar--height, 0px) + var(--wp--custom--local-navigation-bar--spacing--height, 60px));
}

& .is-link-to-top {
display: none;

& a {
text-decoration-line: none;

&:hover {
text-decoration-line: underline;
}
}
}
}

/* Slot the search & table of contents into a floating sidebar on large screens. */
@media (min-width: 1200px) {
.wp-block-wporg-sidebar-container {
/* Slot the search & table of contents into a floating sidebar on large screens. */
&.is-floating-sidebar {
--local--block-end-sidebar--width: 340px;
--local--offset-top: calc(var(--wp-admin--admin-bar--height, 0px) + var(--wp--custom--local-navigation-bar--spacing--height, 60px));
--local--padding-top: var(--wp--preset--spacing--20);

width: var(--local--block-end-sidebar--width);

Expand Down Expand Up @@ -60,15 +73,16 @@
border-top: 1px solid var(--wp--preset--color--light-grey-1);
}
}
}

main .wp-block-wporg-sidebar-container {
position: absolute;
top: calc(var(--wp-global-header-offset, 90px) + var(--wp--custom--local-navigation-bar--spacing--height, 60px));
margin-top: var(--wp--custom--wporg-sidebar-container--spacing--margin--top);
main .wp-block-wporg-sidebar-container.is-floating-sidebar {
position: absolute;
top: calc(var(--wp-global-header-offset, 90px) + var(--wp--custom--local-navigation-bar--spacing--height, 60px));
margin-top: var(--wp--custom--wporg-sidebar-container--spacing--margin--top);

/* Right offset should be "edge spacing" at minimum, otherwise calculate it to be centered. */
right: max(var(--wp--preset--spacing--edge-space), calc((100% - var(--wp--style--global--wide-size)) / 2));

/* Right offset should be "edge spacing" at minimum, otherwise calculate it to be centered. */
right: max(var(--wp--preset--spacing--edge-space), calc((100% - var(--wp--style--global--wide-size)) / 2));
}
}

/* Set up the custom properties. These can be overridden by settings in theme.json. */
Expand Down
4 changes: 4 additions & 0 deletions mu-plugins/blocks/sidebar-container/src/block.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@
"hasBackToTop": {
"type": "boolean",
"default": true
},
"inlineBreakpoint": {
"type": "string",
"default": "1200px"
}
},
"supports": {
Expand Down
75 changes: 47 additions & 28 deletions mu-plugins/blocks/sidebar-container/src/view.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,13 @@
* Fallback values for custom properties match CSS defaults.
*/

const GLOBAL_NAV_HEIGHT = getCustomPropValue( '--wp-global-header-height' ) || 90;
const ADMIN_BAR_HEIGHT = parseInt(
window.getComputedStyle( document.documentElement ).getPropertyValue( 'margin-top' ),
10
);
const SPACE_TO_TOP = getCustomPropValue( '--wp--custom--wporg-sidebar-container--spacing--margin--top' ) || 80;
const SCROLL_POSITION_TO_FIX = GLOBAL_NAV_HEIGHT + SPACE_TO_TOP - ADMIN_BAR_HEIGHT;

let containers;
let mainEl;
let adminBarHeight = getCustomPropValue( '--wp-admin--admin-bar--height' ) || 32;
let globalNavHeight = getCustomPropValue( '--wp-global-header-height' ) || 90;
const scrollHandlers = [];

/**
* Get the value of a CSS custom property.
Expand All @@ -33,47 +30,69 @@ function getCustomPropValue( name, element = document.body ) {
* Check the position of each sidebar relative to the scroll position,
* and toggle the "fixed" class at a certain point.
* Reduce the height of each sidebar to stop them overlapping the footer.
*
* @param {HTMLElement} container
*/
function onScroll() {
// Only run the scroll code if the sidebar is floating on a wide screen.
if ( ! window.matchMedia( '(min-width: 1200px)' ).matches ) {
return;
}
function createScrollHandler( container ) {
return function onWindowChange() {
// Only run the scroll code if the sidebar is floating.
if ( ! container.classList.contains( 'is-floating-sidebar' ) ) {
return false;
}

const { scrollY, innerHeight: windowHeight } = window;
const scrollPosition = scrollY - ADMIN_BAR_HEIGHT;
const { scrollY, innerHeight: windowHeight } = window;
const scrollPosition = scrollY - adminBarHeight;
const localNavOffset = getCustomPropValue( '--local--nav--offset', container );
const paddingTop = getCustomPropValue( '--local--padding-top', container );

// Toggle the fixed position based on whether the scrollPosition is greater than the initial gap from the top.
containers.forEach( ( container ) => {
// Toggle the fixed position based on whether the scrollPosition is greater than the
// initial gap from the top minus the padding applied when fixed.
container.classList.toggle(
'is-fixed-sidebar',
scrollPosition > SCROLL_POSITION_TO_FIX - getCustomPropValue( '--local--padding-top', container )
scrollPosition > SPACE_TO_TOP + globalNavHeight + localNavOffset - adminBarHeight - paddingTop
);
} );

const footerStart = mainEl.offsetTop + mainEl.offsetHeight;
const footerStart = mainEl.offsetTop + mainEl.offsetHeight;

// Is the footer visible in the viewport?
if ( footerStart < scrollPosition + windowHeight ) {
containers.forEach( ( container ) => {
// Is the footer visible in the viewport?
if ( footerStart < scrollPosition + windowHeight ) {
container.style.setProperty( 'height', `${ footerStart - scrollPosition - container.offsetTop }px` );
} );
} else {
containers.forEach( ( container ) => {
} else {
container.style.removeProperty( 'height' );
} );
}
}
};
}

function init() {
containers = document.querySelectorAll( '.wp-block-wporg-sidebar-container' );
mainEl = document.getElementById( 'wp--skip-link--target' );

if ( mainEl && containers.length ) {
onScroll(); // Run once to avoid footer collisions on load (ex, when linked to #reply-title).
window.addEventListener( 'scroll', onScroll );
containers.forEach( ( container ) => {
const scrollHandler = createScrollHandler( container );
scrollHandlers.push( scrollHandler );
scrollHandler(); // Run once to avoid footer collisions on load (ex, when linked to #reply-title).
window.addEventListener( 'scroll', scrollHandler );
} );
}

function onResize() {
adminBarHeight = getCustomPropValue( '--wp-admin--admin-bar--height' ) || 32;
globalNavHeight = getCustomPropValue( '--wp-global-header-height' ) || 90;

containers.forEach( ( container ) => {
const breakpoint = container.dataset.breakpoint;
const shouldFloat = window.matchMedia( `(min-width: ${ breakpoint })` ).matches;
// Toggle the floating class if the window is wider than the configured breakpoint.
container.classList.toggle( 'is-floating-sidebar', shouldFloat );
} );

scrollHandlers.forEach( ( handler ) => handler() );
}

onResize();
window.addEventListener( 'resize', onResize() );

// If there is no table of contents, hide the heading.
if ( ! document.querySelector( '.wp-block-wporg-table-of-contents' ) ) {
const heading = document.querySelector( '.wp-block-wporg-sidebar-container h2' );
Expand Down

0 comments on commit 1f0b446

Please sign in to comment.