From f95db29d6bbefde1d9effc942f3db137af9da6a9 Mon Sep 17 00:00:00 2001 From: Kelly Dwan Date: Thu, 27 Apr 2023 17:09:30 -0400 Subject: [PATCH] Add new Sidebar Container block to handle sticky sidebar logic This pulls out the sticky-related JS from the table of contents block, and ports over some CSS from the child themes, so that the sidebar pattern with table of contents can be reused. --- mu-plugins/blocks/sidebar-container/index.php | 54 +++++++++++++++++++ .../sidebar-container/postcss/style.pcss | 38 +++++++++++++ .../blocks/sidebar-container/src/block.json | 19 +++++++ .../blocks/sidebar-container/src/index.js | 25 +++++++++ .../src/view.js | 41 +++++++------- mu-plugins/blocks/table-of-contents/index.php | 17 ++++-- .../table-of-contents/postcss/style.pcss | 30 ++++++++--- .../blocks/table-of-contents/src/block.json | 1 - mu-plugins/loader.php | 1 + 9 files changed, 197 insertions(+), 29 deletions(-) create mode 100644 mu-plugins/blocks/sidebar-container/index.php create mode 100644 mu-plugins/blocks/sidebar-container/postcss/style.pcss create mode 100644 mu-plugins/blocks/sidebar-container/src/block.json create mode 100644 mu-plugins/blocks/sidebar-container/src/index.js rename mu-plugins/blocks/{table-of-contents => sidebar-container}/src/view.js (72%) diff --git a/mu-plugins/blocks/sidebar-container/index.php b/mu-plugins/blocks/sidebar-container/index.php new file mode 100644 index 000000000..248c386b2 --- /dev/null +++ b/mu-plugins/blocks/sidebar-container/index.php @@ -0,0 +1,54 @@ + __NAMESPACE__ . '\render', + ) + ); +} + +/** + * Render the block content. + * + * @param array $attributes Block attributes. + * @param string $content Block default content. + * @param WP_Block $block Block instance. + * + * @return string Returns the block markup. + */ +function render( $attributes, $content, $block ) { + $back_to_top = sprintf( + '', + esc_html__( '↑ Back to top', 'wporg' ) + ); + + $wrapper_attributes = get_block_wrapper_attributes(); + return sprintf( + '
%2$s%3$s
', + $wrapper_attributes, + $content, + $back_to_top + ); +} diff --git a/mu-plugins/blocks/sidebar-container/postcss/style.pcss b/mu-plugins/blocks/sidebar-container/postcss/style.pcss new file mode 100644 index 000000000..8e5a6ae66 --- /dev/null +++ b/mu-plugins/blocks/sidebar-container/postcss/style.pcss @@ -0,0 +1,38 @@ +.wp-block-wporg-sidebar-container .is-link-to-top { + display: none; +} + +/* Slot the search & table of contents into a floating sidebar on large screens. */ +@media (min-width: 1200px) { + .wp-block-wporg-sidebar-container { + --local--block-end-sidebar--width: 340px; + + position: absolute; + top: calc(var(--wp-global-header-offset, 0px) + var(--wp-local-header-offset, 0px)); + + /* 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)); + width: var(--local--block-end-sidebar--width); + margin-top: var(--wp--preset--spacing--edge-space) !important; + + &.is-fixed-sidebar { + position: fixed; + } + + &.is-bottom-sidebar { + position: absolute; + } + + &.is-fixed-sidebar .is-link-to-top, + &.is-bottom-sidebar .is-link-to-top { + display: block; + } + } +} + +@media (min-width: 890px) { + /* stylelint-disable selector-id-pattern */ + #wp--skip-link--target { + scroll-margin-top: var(--wp-local-header-offset, 0); + } +} diff --git a/mu-plugins/blocks/sidebar-container/src/block.json b/mu-plugins/blocks/sidebar-container/src/block.json new file mode 100644 index 000000000..25d44897c --- /dev/null +++ b/mu-plugins/blocks/sidebar-container/src/block.json @@ -0,0 +1,19 @@ +{ + "$schema": "https://schemas.wp.org/trunk/block.json", + "apiVersion": 2, + "name": "wporg/sidebar-container", + "title": "Sidebar Container", + "icon": "align-pull-right", + "category": "layout", + "description": "A sticky container to be used in 2-column layouts.", + "textdomain": "wporg", + "attributes": {}, + "supports": { + "inserter": false, + "__experimentalLayout": true + }, + "editorScript": "file:./index.js", + "editorStyle": "file:./editor-style.css", + "style": "file:./style.css", + "viewScript": "file:./view.js" +} diff --git a/mu-plugins/blocks/sidebar-container/src/index.js b/mu-plugins/blocks/sidebar-container/src/index.js new file mode 100644 index 000000000..1992c1568 --- /dev/null +++ b/mu-plugins/blocks/sidebar-container/src/index.js @@ -0,0 +1,25 @@ +/** + * WordPress dependencies + */ +import { registerBlockType } from '@wordpress/blocks'; +import { InnerBlocks, useBlockProps } from '@wordpress/block-editor'; + +/** + * Internal dependencies + */ +import metadata from './block.json'; + +function Edit() { + return ( +
+ +
+ ); +} + +registerBlockType( metadata.name, { + edit: Edit, + save: () => { + return ; + }, +} ); diff --git a/mu-plugins/blocks/table-of-contents/src/view.js b/mu-plugins/blocks/sidebar-container/src/view.js similarity index 72% rename from mu-plugins/blocks/table-of-contents/src/view.js rename to mu-plugins/blocks/sidebar-container/src/view.js index 20cbed900..759ad8a2b 100644 --- a/mu-plugins/blocks/table-of-contents/src/view.js +++ b/mu-plugins/blocks/sidebar-container/src/view.js @@ -20,13 +20,8 @@ function getCustomPropValue( name, element = document.body ) { } function onScroll() { - const container = document.querySelector( '.wp-block-wporg-table-of-contents' ); - if ( ! container ) { - return; - } - // Only run the scroll code if the sidebar is fixed. - const sidebarContainer = container.parentNode; + const sidebarContainer = document.querySelector( '.wp-block-wporg-sidebar-container' ); if ( ! sidebarContainer || ! sidebarContainer.classList.contains( 'is-fixed-sidebar' ) ) { return; } @@ -65,26 +60,36 @@ function onScroll() { } } -function init() { - const container = document.querySelector( '.wp-block-wporg-table-of-contents' ); +function isSidebarWithinViewport( container ) { // Margin offset from the top of the sidebar. const gap = getCustomPropValue( '--wp--preset--spacing--edge-space' ); + // Usable viewport height. + const viewHeight = window.innerHeight - FIXED_HEADER_HEIGHT; + // Get the height of the sidebar, plus the top margin and 50px for the + // "Back to top" link, which isn't visible until `is-fixed-sidebar` is + // added, therefore not included in the offsetHeight value. + const sidebarHeight = container.offsetHeight + gap + 50; + // If the sidebar is shorter than the view area, apply the class so + // that it's fixed and scrolls with the page content. + return sidebarHeight < viewHeight; +} + +function init() { + const container = document.querySelector( '.wp-block-wporg-sidebar-container' ); if ( container ) { - // Usable viewport height. - const viewHeight = window.innerHeight - FIXED_HEADER_HEIGHT; - // Get the height of the sidebar, plus the top margin and 50px for the - // "Back to top" link, which isn't visible until `is-fixed-sidebar` is - // added, therefore not included in the parentNode.offsetHeight value. - const sidebarHeight = container.parentNode?.offsetHeight + gap + 50; - // If the table of contents sidebar is shorter than the view area, apply the - // class so that it's fixed and scrolls with the page content. - if ( sidebarHeight < viewHeight ) { - container.parentNode.classList.add( 'is-fixed-sidebar' ); + if ( isSidebarWithinViewport( container ) ) { + container.classList.add( 'is-fixed-sidebar' ); onScroll(); // Run once to avoid footer collisions on load (ex, when linked to #reply-title). window.addEventListener( 'scroll', onScroll ); } } + + // 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' ); + heading?.style.setProperty( 'display', 'none' ); + } } window.addEventListener( 'load', init ); diff --git a/mu-plugins/blocks/table-of-contents/index.php b/mu-plugins/blocks/table-of-contents/index.php index 81884a9ec..b78424a44 100644 --- a/mu-plugins/blocks/table-of-contents/index.php +++ b/mu-plugins/blocks/table-of-contents/index.php @@ -47,10 +47,19 @@ function render( $attributes, $content, $block ) { return ''; } - $content = '

'; - $content .= __( 'In this article', 'wporg' ); - $content .= '

'; - $content .= '