2022-12-07 22:15:31 +00:00
|
|
|
const features = require( './features.js' );
|
2022-10-19 20:43:40 +00:00
|
|
|
const PINNED_HEADER_CLASS = 'vector-pinnable-header-pinned';
|
|
|
|
const UNPINNED_HEADER_CLASS = 'vector-pinnable-header-unpinned';
|
|
|
|
|
|
|
|
/**
|
2022-12-16 19:15:56 +00:00
|
|
|
* Toggle classes on the body and pinnable element
|
|
|
|
*
|
|
|
|
* @param {HTMLElement} header pinnable element
|
|
|
|
*/
|
|
|
|
function togglePinnableClasses( header ) {
|
|
|
|
const { featureName, name } = header.dataset;
|
|
|
|
|
|
|
|
if ( featureName ) {
|
|
|
|
// Leverage features.js to toggle the body classes and persist the state
|
|
|
|
// for logged-in users.
|
|
|
|
features.toggle( featureName );
|
|
|
|
} else {
|
|
|
|
// Toggle body classes, assumes default pinned classes are initialized serverside
|
|
|
|
document.body.classList.toggle( `${name}-pinned` );
|
|
|
|
document.body.classList.toggle( `${name}-unpinned` );
|
|
|
|
}
|
|
|
|
|
|
|
|
// Toggle pinned class
|
|
|
|
header.classList.toggle( PINNED_HEADER_CLASS );
|
|
|
|
header.classList.toggle( UNPINNED_HEADER_CLASS );
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Event handler that toggles the pinnable elements pinned state.
|
|
|
|
* Also moves the pinned element when those params are provided
|
|
|
|
* (via data attributes).
|
|
|
|
*
|
|
|
|
* @param {HTMLElement} header PinnableHeader element.
|
|
|
|
*/
|
|
|
|
function togglePinnableElement( header ) {
|
|
|
|
const {
|
|
|
|
pinnableElementId,
|
|
|
|
pinnedContainerId,
|
|
|
|
unpinnedContainerId
|
|
|
|
} = header.dataset;
|
|
|
|
|
|
|
|
togglePinnableClasses( header );
|
|
|
|
|
|
|
|
// Optional functionality of moving the pinnable element in the DOM
|
|
|
|
// to different containers based on it's pinned status
|
|
|
|
if ( pinnableElementId && pinnedContainerId && unpinnedContainerId ) {
|
|
|
|
const newContainerId = isPinned( header ) ? pinnedContainerId : unpinnedContainerId;
|
|
|
|
movePinnableElement( pinnableElementId, newContainerId );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Binds all the toggle buttons in a pinnableElement
|
|
|
|
* to the click handler that enables pinnability.
|
|
|
|
*
|
2022-10-19 20:43:40 +00:00
|
|
|
* @param {HTMLElement} header
|
|
|
|
*/
|
|
|
|
function bindPinnableToggleButtons( header ) {
|
2022-12-16 19:15:56 +00:00
|
|
|
if ( !header.dataset.name ) {
|
2022-10-19 20:43:40 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const toggleButtons = header.querySelectorAll( '.vector-pinnable-header-toggle-button' );
|
|
|
|
|
|
|
|
toggleButtons.forEach( function ( button ) {
|
2022-12-16 19:15:56 +00:00
|
|
|
button.addEventListener( 'click', togglePinnableElement.bind( null, header ) );
|
2022-10-19 20:43:40 +00:00
|
|
|
} );
|
|
|
|
}
|
|
|
|
|
2022-12-07 22:15:31 +00:00
|
|
|
/**
|
|
|
|
* @param {HTMLElement} header
|
|
|
|
* @return {boolean} Returns true if the element is pinned and false otherwise.
|
|
|
|
*/
|
|
|
|
function isPinned( header ) {
|
2022-12-14 22:19:16 +00:00
|
|
|
// In the future, consider delegating to `features.isEnabled()` if and when
|
|
|
|
// TOC (T316060, T325032) and all pinnable elements use the
|
|
|
|
// `vector-feature-{name}-pinned-enabled` naming convention for the body
|
|
|
|
// class.
|
2022-12-07 22:15:31 +00:00
|
|
|
return header.classList.contains( PINNED_HEADER_CLASS );
|
|
|
|
}
|
|
|
|
|
2022-10-19 20:43:40 +00:00
|
|
|
/**
|
|
|
|
* @param {string} pinnableElementId
|
2022-11-21 22:16:07 +00:00
|
|
|
* @param {string} newContainerId
|
2022-10-19 20:43:40 +00:00
|
|
|
*/
|
2022-11-21 22:16:07 +00:00
|
|
|
function movePinnableElement( pinnableElementId, newContainerId ) {
|
2022-10-19 20:43:40 +00:00
|
|
|
const pinnableElem = document.getElementById( pinnableElementId );
|
2022-11-21 22:16:07 +00:00
|
|
|
const newContainer = document.getElementById( newContainerId );
|
2022-10-19 20:43:40 +00:00
|
|
|
const currContainer = /** @type {HTMLElement} */ ( pinnableElem && pinnableElem.parentElement );
|
|
|
|
|
2022-11-21 22:16:07 +00:00
|
|
|
if ( !pinnableElem || !newContainer || !currContainer ) {
|
2022-10-19 20:43:40 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Avoid moving element if unnecessary
|
2022-11-21 22:16:07 +00:00
|
|
|
if ( currContainer.id !== newContainerId ) {
|
2022-10-19 20:43:40 +00:00
|
|
|
newContainer.insertAdjacentElement( 'beforeend', pinnableElem );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-28 21:08:40 +00:00
|
|
|
function initPinnableElement() {
|
2022-10-19 20:43:40 +00:00
|
|
|
const pinnableHeader = /** @type {NodeListOf<HTMLElement>} */ ( document.querySelectorAll( '.vector-pinnable-header' ) );
|
|
|
|
pinnableHeader.forEach( bindPinnableToggleButtons );
|
|
|
|
}
|
|
|
|
|
|
|
|
module.exports = {
|
2022-11-28 21:08:40 +00:00
|
|
|
initPinnableElement,
|
2022-10-19 20:43:40 +00:00
|
|
|
movePinnableElement,
|
2022-12-07 22:15:31 +00:00
|
|
|
isPinned,
|
2022-10-19 20:43:40 +00:00
|
|
|
PINNED_HEADER_CLASS,
|
|
|
|
UNPINNED_HEADER_CLASS
|
|
|
|
};
|