From e5336c46105d002807ff2e2074d3dd0382484b69 Mon Sep 17 00:00:00 2001 From: alistair3149 Date: Sun, 7 Jul 2024 17:58:02 -0400 Subject: [PATCH] =?UTF-8?q?fix(stickyHeader):=20=F0=9F=90=9B=20add=20a=20p?= =?UTF-8?q?laceholder=20element=20to=20page=20header=20to=20avoid=20layout?= =?UTF-8?q?=20shift=20when=20stickied?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes: #854 --- .../skins.citizen.scripts/stickyHeader.js | 32 ++++++++++++++++--- .../components/StickyHeader.less | 6 +++- resources/skins.citizen.styles/layout.less | 6 ++-- 3 files changed, 37 insertions(+), 7 deletions(-) diff --git a/resources/skins.citizen.scripts/stickyHeader.js b/resources/skins.citizen.scripts/stickyHeader.js index 783a27e2..10e37822 100644 --- a/resources/skins.citizen.scripts/stickyHeader.js +++ b/resources/skins.citizen.scripts/stickyHeader.js @@ -39,17 +39,41 @@ function init() { return; } - const toggleStickyClass = ( state ) => () => { + const placeholder = document.createElement( 'div' ); + placeholder.id = 'citizen-page-header-sticky-placeholder'; + header.insertAdjacentElement( 'afterend', placeholder ); + + let staticHeaderHeight = header.offsetHeight; + + const toggleStickyHeader = ( isSticky ) => { window.requestAnimationFrame( () => { - document.body.classList.toggle( STICKY_CLASS, state ); + document.body.classList.toggle( STICKY_CLASS, isSticky ); + placeholder.style.height = `${ staticHeaderHeight - header.offsetHeight }px`; } ); }; const observer = initIntersectionObserver( - toggleStickyClass( true ), - toggleStickyClass( false ) + () => { + toggleStickyHeader( true ); + }, + () => { + toggleStickyHeader( false ); + } ); observer.observe( sentinel ); + + const onResize = () => { + toggleStickyHeader( false ); + }; + + const onResizeEnd = () => { + // Refresh static header height after resize + staticHeaderHeight = header.offsetHeight; + toggleStickyHeader( true ); + }; + + window.addEventListener( 'resize', onResize ); + window.addEventListener( 'resize', mw.util.debounce( onResizeEnd, 250 ) ); } module.exports = { diff --git a/resources/skins.citizen.styles/components/StickyHeader.less b/resources/skins.citizen.styles/components/StickyHeader.less index e6d98689..4a85d72b 100644 --- a/resources/skins.citizen.styles/components/StickyHeader.less +++ b/resources/skins.citizen.styles/components/StickyHeader.less @@ -1,10 +1,14 @@ #citizen-page-header-sticky-sentinel { - grid-area: content; // align right above content + grid-area: afterHeader; // align right above content height: 1px; visibility: hidden; contain: strict; } +#citizen-page-header-sticky-placeholder { + grid-area: afterHeader; +} + .ve-activated, .action-edit { // HACK: So sticky header will never trigger in edit action diff --git a/resources/skins.citizen.styles/layout.less b/resources/skins.citizen.styles/layout.less index 5a66cb3e..833d530b 100644 --- a/resources/skins.citizen.styles/layout.less +++ b/resources/skins.citizen.styles/layout.less @@ -13,7 +13,8 @@ .citizen-body-container { display: grid; grid-template-areas: - 'header' + 'header' + 'afterHeader' 'content' 'footer'; // Using auto as min value will cause overflow @@ -59,7 +60,8 @@ .citizen-toc-enabled { .citizen-body-container { grid-template-areas: - 'header header' + 'header header' + 'afterHeader afterHeader' 'content sidebar' 'footer footer'; grid-template-columns: minmax( 0, var( --width-layout ) ) var( --width-toc );