Simplify TOC spacing styles/logic by reusing the visible sticky header class
- Remove the 'vector-scrolled-below-table-of-contents' class, reducing the number of classes added with JS and simplifying the scrollObserver logic - Move the 'vector-sticky-header-visible' class from the sticky header element to the body element. Hopefully, this is where other feature specific classes can go in the future - This approach means the TOC will not need JS to update it's spacing when the sticky header is not enabled Bug: T307345 Change-Id: I1084defc7025f5c946e22a36d373224fae6f8bd6
This commit is contained in:
parent
5f341fbb36
commit
91e2e55a10
|
@ -8,7 +8,7 @@
|
||||||
for more context.
|
for more context.
|
||||||
}}
|
}}
|
||||||
<header id="vector-sticky-header" aria-hidden="true"
|
<header id="vector-sticky-header" aria-hidden="true"
|
||||||
class="vector-sticky-header{{#is-visible}} vector-sticky-header-visible{{/is-visible}}">
|
class="vector-sticky-header">
|
||||||
<div class="vector-sticky-header-start">
|
<div class="vector-sticky-header-start">
|
||||||
<div class="vector-sticky-header-icon-start">
|
<div class="vector-sticky-header-icon-start">
|
||||||
{{#data-button-start}}
|
{{#data-button-start}}
|
||||||
|
|
|
@ -18,8 +18,7 @@ const
|
||||||
TOC_SCROLL_HOOK = 'table_of_contents',
|
TOC_SCROLL_HOOK = 'table_of_contents',
|
||||||
PAGE_TITLE_SCROLL_HOOK = 'page_title',
|
PAGE_TITLE_SCROLL_HOOK = 'page_title',
|
||||||
TOC_QUERY_PARAM = 'tableofcontents',
|
TOC_QUERY_PARAM = 'tableofcontents',
|
||||||
TOC_EXPERIMENT_NAME = 'skin-vector-toc-experiment',
|
TOC_EXPERIMENT_NAME = 'skin-vector-toc-experiment';
|
||||||
SCROLL_TOC_BELOW_CLASS = 'vector-scrolled-below-table-of-contents';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @callback OnIntersection
|
* @callback OnIntersection
|
||||||
|
@ -101,15 +100,13 @@ const main = () => {
|
||||||
const observer = scrollObserver.initScrollObserver(
|
const observer = scrollObserver.initScrollObserver(
|
||||||
() => {
|
() => {
|
||||||
if ( isStickyHeaderAllowed && isStickyHeaderEnabled ) {
|
if ( isStickyHeaderAllowed && isStickyHeaderEnabled ) {
|
||||||
stickyHeader.show( header );
|
stickyHeader.show();
|
||||||
document.body.classList.add( SCROLL_TOC_BELOW_CLASS );
|
|
||||||
}
|
}
|
||||||
scrollObserver.fireScrollHook( 'down', PAGE_TITLE_SCROLL_HOOK );
|
scrollObserver.fireScrollHook( 'down', PAGE_TITLE_SCROLL_HOOK );
|
||||||
},
|
},
|
||||||
() => {
|
() => {
|
||||||
if ( isStickyHeaderAllowed && isStickyHeaderEnabled ) {
|
if ( isStickyHeaderAllowed && isStickyHeaderEnabled ) {
|
||||||
stickyHeader.hide( header );
|
stickyHeader.hide();
|
||||||
document.body.classList.remove( SCROLL_TOC_BELOW_CLASS );
|
|
||||||
}
|
}
|
||||||
scrollObserver.fireScrollHook( 'up', PAGE_TITLE_SCROLL_HOOK );
|
scrollObserver.fireScrollHook( 'up', PAGE_TITLE_SCROLL_HOOK );
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,21 +31,17 @@ function copyAttribute( from, to, attribute ) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show the sticky header.
|
* Show the sticky header.
|
||||||
*
|
|
||||||
* @param {Element} header
|
|
||||||
*/
|
*/
|
||||||
function show( header ) {
|
function show() {
|
||||||
header.classList.add( STICKY_HEADER_VISIBLE_CLASS );
|
document.body.classList.add( STICKY_HEADER_VISIBLE_CLASS );
|
||||||
document.body.classList.remove( ULS_HIDE_CLASS );
|
document.body.classList.remove( ULS_HIDE_CLASS );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hide the sticky header.
|
* Hide the sticky header.
|
||||||
*
|
|
||||||
* @param {Element} header
|
|
||||||
*/
|
*/
|
||||||
function hide( header ) {
|
function hide() {
|
||||||
header.classList.remove( STICKY_HEADER_VISIBLE_CLASS );
|
document.body.classList.remove( STICKY_HEADER_VISIBLE_CLASS );
|
||||||
document.body.classList.add( ULS_HIDE_CLASS );
|
document.body.classList.add( ULS_HIDE_CLASS );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -284,14 +280,13 @@ function isInViewport( element ) {
|
||||||
/**
|
/**
|
||||||
* Add hooks for sticky header when Visual Editor is used.
|
* Add hooks for sticky header when Visual Editor is used.
|
||||||
*
|
*
|
||||||
* @param {Element} header
|
|
||||||
* @param {Element} stickyIntersection intersection element
|
* @param {Element} stickyIntersection intersection element
|
||||||
* @param {IntersectionObserver} observer
|
* @param {IntersectionObserver} observer
|
||||||
*/
|
*/
|
||||||
function addVisualEditorHooks( header, stickyIntersection, observer ) {
|
function addVisualEditorHooks( stickyIntersection, observer ) {
|
||||||
// When Visual Editor is activated, hide the sticky header.
|
// When Visual Editor is activated, hide the sticky header.
|
||||||
mw.hook( 've.activationStart' ).add( () => {
|
mw.hook( 've.activationStart' ).add( () => {
|
||||||
hide( header );
|
hide();
|
||||||
observer.unobserve( stickyIntersection );
|
observer.unobserve( stickyIntersection );
|
||||||
} );
|
} );
|
||||||
|
|
||||||
|
@ -308,7 +303,7 @@ function addVisualEditorHooks( header, stickyIntersection, observer ) {
|
||||||
// After saving edits, re-apply the sticky header if the target is not in the viewport.
|
// After saving edits, re-apply the sticky header if the target is not in the viewport.
|
||||||
mw.hook( 'postEdit.afterRemoval' ).add( () => {
|
mw.hook( 'postEdit.afterRemoval' ).add( () => {
|
||||||
if ( !isInViewport( stickyIntersection ) ) {
|
if ( !isInViewport( stickyIntersection ) ) {
|
||||||
show( header );
|
show();
|
||||||
observer.observe( stickyIntersection );
|
observer.observe( stickyIntersection );
|
||||||
}
|
}
|
||||||
} );
|
} );
|
||||||
|
@ -378,7 +373,7 @@ function makeStickyHeaderFunctional(
|
||||||
const primaryEdit = protectedEdit || ( veEdit || ceEdit );
|
const primaryEdit = protectedEdit || ( veEdit || ceEdit );
|
||||||
const secondaryEdit = veEdit ? ceEdit : null;
|
const secondaryEdit = veEdit ? ceEdit : null;
|
||||||
const disableStickyHeader = () => {
|
const disableStickyHeader = () => {
|
||||||
header.classList.remove( STICKY_HEADER_VISIBLE_CLASS );
|
document.body.classList.remove( STICKY_HEADER_VISIBLE_CLASS );
|
||||||
stickyObserver.unobserve( stickyIntersection );
|
stickyObserver.unobserve( stickyIntersection );
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -458,7 +453,7 @@ function initStickyHeader( props ) {
|
||||||
);
|
);
|
||||||
|
|
||||||
setupSearchIfNeeded( props.header );
|
setupSearchIfNeeded( props.header );
|
||||||
addVisualEditorHooks( props.header, props.stickyIntersection, props.observer );
|
addVisualEditorHooks( props.stickyIntersection, props.observer );
|
||||||
|
|
||||||
// Make sure ULS outside sticky header disables the sticky header behaviour.
|
// Make sure ULS outside sticky header disables the sticky header behaviour.
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
|
|
|
@ -74,9 +74,9 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// T305069 When scrolling down, override the top padding of the sticky TOC that is applied during menu toggling
|
// T305069 When scrolling down, override the top margin of the sticky TOC
|
||||||
// so that the top of sticky TOC remains at the top of the viewport with normal padding. See scrollObserver.js.
|
// so that the top of sticky TOC remains at the top of the viewport with normal padding.
|
||||||
&.vector-scrolled-below-table-of-contents .sidebar-toc {
|
&.vector-sticky-header-visible .sidebar-toc {
|
||||||
margin-top: @top-margin-sidebar-toc;
|
margin-top: @top-margin-sidebar-toc;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,6 @@
|
||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
z-index: @z-index-header;
|
z-index: @z-index-header;
|
||||||
transform: translateY( -100% );
|
|
||||||
transition: @transition-sticky-header;
|
transition: @transition-sticky-header;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
@ -32,20 +31,25 @@
|
||||||
max-width: @max-width-page-container + ( @padding-horizontal-page-container-wide * 2 );
|
max-width: @max-width-page-container + ( @padding-horizontal-page-container-wide * 2 );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Hide sticky header and it's children until visible class is applied to the body
|
||||||
|
.client-js.vector-sticky-header-enabled :not( .vector-sticky-header-visible ) & {
|
||||||
|
transform: translateY( -100% );
|
||||||
|
|
||||||
|
> div {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hide sticky header for no-js users
|
||||||
|
.client-nojs & {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
// T298836 Hide the sticky header at lower resolutions.
|
// T298836 Hide the sticky header at lower resolutions.
|
||||||
@media ( max-width: @width-breakpoint-desktop ) {
|
@media ( max-width: @width-breakpoint-desktop ) {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hide any open menus/search results unless sticky header is visible
|
|
||||||
&:not( .vector-sticky-header-visible ) > div {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-visible {
|
|
||||||
transform: translateY( 0% );
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Layout
|
// Layout
|
||||||
//
|
//
|
||||||
|
@ -120,10 +124,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.client-nojs .vector-sticky-header {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media ( min-width: @width-breakpoint-tablet ) {
|
@media ( min-width: @width-breakpoint-tablet ) {
|
||||||
.client-js.vector-sticky-header-enabled {
|
.client-js.vector-sticky-header-enabled {
|
||||||
// T290518: When the sticky header is enabled (feature flag is on, js is
|
// T290518: When the sticky header is enabled (feature flag is on, js is
|
||||||
|
|
|
@ -183,9 +183,10 @@ body {
|
||||||
position: sticky;
|
position: sticky;
|
||||||
float: left;
|
float: left;
|
||||||
clear: both;
|
clear: both;
|
||||||
// Defaults to the top of the viewport unless .mw-sticky-header-element
|
// Defaults to -2em to account for the TOC's top margin
|
||||||
// overrides it.
|
// ensuring the TOC is 1.5em (24px) from the top of the viewport
|
||||||
top: 0;
|
// unless .mw-sticky-header-element overrides it.
|
||||||
|
top: calc( 1.5em - @top-margin-sidebar-toc_title_inline );
|
||||||
}
|
}
|
||||||
|
|
||||||
.parsoid-body {
|
.parsoid-body {
|
||||||
|
|
|
@ -31,6 +31,6 @@
|
||||||
* and the notification bar is also open, the notification
|
* and the notification bar is also open, the notification
|
||||||
* should be moved so that it doesn't obstruct the sticky header
|
* should be moved so that it doesn't obstruct the sticky header
|
||||||
*/
|
*/
|
||||||
.vector-sticky-header-visible ~ .mw-notification-area-overlay > .mw-notification-area-floating {
|
.vector-sticky-header-visible .mw-notification-area-overlay > .mw-notification-area-floating {
|
||||||
transform: translateY( @height-sticky-header );
|
transform: translateY( @height-sticky-header );
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,6 @@ const HISTORY_ICON = {
|
||||||
const data = {
|
const data = {
|
||||||
title: 'Audre Lorde',
|
title: 'Audre Lorde',
|
||||||
heading: 'Introduction',
|
heading: 'Introduction',
|
||||||
'is-visible': true,
|
|
||||||
'data-primary-action': {
|
'data-primary-action': {
|
||||||
id: 'p-lang-btn-sticky-header',
|
id: 'p-lang-btn-sticky-header',
|
||||||
class: 'mw-interlanguage-selector',
|
class: 'mw-interlanguage-selector',
|
||||||
|
|
Loading…
Reference in New Issue