mediawiki-skins-Vector/resources/skins.vector.es6/scrollObserver.js
Clare Ming f2c60983bb Update scroll instrument for TOC
- Leverage scrollObserver to use for TOC scroll events.
- Add new hooks to target legacy TOC intersection.
- See corresponding changes (not strict dependency) in related
WME patch 773628 for capturing scroll events from Vector TOC:
https://gerrit.wikimedia.org/r/c/mediawiki/extensions/WikimediaEvents/+/773628
- Dependency on https://github.com/wikimedia/typescript-types/pull/30
which updates mwHookInstance interface with fire property to prevent
TS error on fire method of mw.hook in scrollObserver.

Bug: T303297
Change-Id: I5c2dd5f3a25ffcb0ed03b76ae28e65eb18ad8d33
2022-03-29 09:45:30 -06:00

88 lines
2.7 KiB
JavaScript

const
SCROLL_TITLE_HOOK = 'vector.page_title_scroll',
SCROLL_TITLE_CONTEXT_ABOVE = 'scrolled-above-page-title',
SCROLL_TITLE_CONTEXT_BELOW = 'scrolled-below-page-title',
SCROLL_TITLE_ACTION = 'scroll-to-top',
SCROLL_TOC_HOOK = 'vector.table_of_contents_scroll',
SCROLL_TOC_CONTEXT_ABOVE = 'scrolled-above-table-of-contents',
SCROLL_TOC_CONTEXT_BELOW = 'scrolled-below-table-of-contents',
SCROLL_TOC_ACTION = 'scroll-to-toc';
/**
* @typedef {Object} scrollVariables
* @property {string} scrollHook
* @property {string} scrollContextBelow
* @property {string} scrollContextAbove
* @property {string} scrollAction
*/
/**
* Return the correct variables based on hook type.
*
* @param {string} hook the type of hook
* @return {scrollVariables}
*/
function getScrollVariables( hook ) {
const scrollVariables = {};
if ( hook === 'page_title' ) {
scrollVariables.scrollHook = SCROLL_TITLE_HOOK;
scrollVariables.scrollContextBelow = SCROLL_TITLE_CONTEXT_BELOW;
scrollVariables.scrollContextAbove = SCROLL_TITLE_CONTEXT_ABOVE;
scrollVariables.scrollAction = SCROLL_TITLE_ACTION;
} else if ( hook === 'table_of_contents' ) {
scrollVariables.scrollHook = SCROLL_TOC_HOOK;
scrollVariables.scrollContextBelow = SCROLL_TOC_CONTEXT_BELOW;
scrollVariables.scrollContextAbove = SCROLL_TOC_CONTEXT_ABOVE;
scrollVariables.scrollAction = SCROLL_TOC_ACTION;
}
return scrollVariables;
}
/**
* Fire a hook to be captured by WikimediaEvents for scroll event logging.
*
* @param {string} direction the scroll direction
* @param {string} hook the hook to fire
*/
function fireScrollHook( direction, hook ) {
const scrollVariables = getScrollVariables( hook );
if ( Object.keys( scrollVariables ).length === 0 && scrollVariables.constructor === Object ) {
return;
}
if ( direction === 'down' ) {
mw.hook( scrollVariables.scrollHook ).fire( {
context: scrollVariables.scrollContextBelow
} );
} else {
mw.hook( scrollVariables.scrollHook ).fire( {
context: scrollVariables.scrollContextAbove,
action: scrollVariables.scrollAction
} );
}
}
/**
* Create an observer for showing/hiding feature and for firing scroll event hooks.
*
* @param {Function} show functionality for when feature is visible
* @param {Function} hide functionality for when feature is hidden
* @return {IntersectionObserver}
*/
function initScrollObserver( show, hide ) {
/* eslint-disable-next-line compat/compat */
return new IntersectionObserver( function ( entries ) {
if ( !entries[ 0 ].isIntersecting && entries[ 0 ].boundingClientRect.top < 0 ) {
// Viewport has crossed the bottom edge of the target element.
show();
} else {
// Viewport is above the bottom edge of the target element.
hide();
}
} );
}
module.exports = {
initScrollObserver,
fireScrollHook
};