mirror of
https://gerrit.wikimedia.org/r/mediawiki/skins/Vector.git
synced 2024-09-25 11:17:35 +00:00
52c7c2ee75
Given our use of constants for tracking classes this eslint rule is more an annoyance than helpful. Change-Id: I37570e3e851997d058f2d93777990dddb3d04089
98 lines
2.4 KiB
JavaScript
98 lines
2.4 KiB
JavaScript
const ACTIVE_SECTION_CLASS = 'sidebar-toc-list-item-active';
|
|
const PARENT_SECTION_CLASS = 'sidebar-toc-level-1';
|
|
const LINK_CLASS = 'sidebar-toc-link';
|
|
const LIST_ITEM_CLASS = 'sidebar-toc-list-item';
|
|
|
|
/**
|
|
* Sets an `ACTIVE_SECTION_CLASS` on the element with an id that matches `id`.
|
|
* If the element is not a top level heading (e.g. element with the
|
|
* `PARENT_SECTION_CLASS`), the top level heading will also have the
|
|
* `ACTIVE_SECTION_CLASS`;
|
|
*
|
|
* @callback ActivateSection
|
|
* @param {string} id The id of the element to be activated in the Table of Contents.
|
|
*/
|
|
|
|
/**
|
|
* Called when a list item is clicked.
|
|
*
|
|
* @callback OnSectionClick
|
|
* @param {string} id The id of the clicked list item.
|
|
*/
|
|
|
|
/**
|
|
* @typedef {Object} TableOfContents
|
|
* @property {ActivateSection} activateSection
|
|
*/
|
|
|
|
/**
|
|
* Initializes the sidebar's Table of Contents.
|
|
*
|
|
* @param {Object} props
|
|
* @param {HTMLElement} props.container
|
|
* @param {OnSectionClick} [props.onSectionClick]
|
|
* @return {TableOfContents}
|
|
*/
|
|
module.exports = function tableOfContents( props ) {
|
|
props = Object.assign( {
|
|
onSectionClick: () => {}
|
|
}, props );
|
|
|
|
let /** @type {HTMLElement | undefined} */ activeParentSection;
|
|
let /** @type {HTMLElement | undefined} */ activeChildSection;
|
|
|
|
/**
|
|
* @param {string} id
|
|
*/
|
|
function activateSection( id ) {
|
|
const tocSection = document.getElementById( id );
|
|
|
|
if ( !tocSection ) {
|
|
return;
|
|
}
|
|
|
|
const parentSection = /** @type {HTMLElement} */ ( tocSection.closest( `.${PARENT_SECTION_CLASS}` ) );
|
|
|
|
if ( activeChildSection ) {
|
|
activeChildSection.classList.remove( ACTIVE_SECTION_CLASS );
|
|
}
|
|
if ( activeParentSection ) {
|
|
activeParentSection.classList.remove( ACTIVE_SECTION_CLASS );
|
|
}
|
|
|
|
tocSection.classList.add( ACTIVE_SECTION_CLASS );
|
|
|
|
if ( parentSection ) {
|
|
parentSection.classList.add( ACTIVE_SECTION_CLASS );
|
|
}
|
|
|
|
activeChildSection = tocSection;
|
|
activeParentSection = parentSection || undefined;
|
|
}
|
|
|
|
function bindClickListener() {
|
|
props.container.addEventListener( 'click', function ( e ) {
|
|
if (
|
|
!( e.target instanceof HTMLElement && e.target.classList.contains( LINK_CLASS ) )
|
|
) {
|
|
return;
|
|
}
|
|
|
|
const tocSection =
|
|
/** @type {HTMLElement | null} */ ( e.target.closest( `.${LIST_ITEM_CLASS}` ) );
|
|
|
|
if ( tocSection && tocSection.id ) {
|
|
activateSection( tocSection.id );
|
|
// @ts-ignore
|
|
props.onSectionClick( tocSection.id );
|
|
}
|
|
} );
|
|
}
|
|
|
|
bindClickListener();
|
|
|
|
return {
|
|
activateSection
|
|
};
|
|
};
|