mediawiki-skins-Vector/resources/skins.vector.js/sidebarPersistence.js
Jon Robson aa8d628e13 Sidebar: Collapses at lower resolutions, expands when resized
On resize, or when booted in a small window the sidebar will collapse
and remain collapsed for subsequent page views

Bug: T316191
Change-Id: I6625fc3b3f1015d74b484a2a3643def13467ddf5
2022-09-15 10:27:12 -07:00

144 lines
4.2 KiB
JavaScript

/**
* JavaScript enhancement to persist the sidebar state for logged-in users.
*
*/
/** @interface MwApi */
var /** @type {MwApi} */api,
SIDEBAR_BUTTON_ID = 'mw-sidebar-button',
debounce = require( /** @type {string} */ ( 'mediawiki.util' ) ).debounce,
SIDEBAR_CHECKBOX_ID = 'mw-sidebar-checkbox',
SIDEBAR_PREFERENCE_NAME = 'VectorSidebarVisible';
/**
* Checks if persistent is enabled at current time.
* When a user is using a browser with a screen resolution of < 1000 it is assumed
* that it is preferred that the sidebar remains closed across page views, as otherwise
* it gets in the way of reading. More context at T316191.
*
* @return {boolean}
*/
function isPersistentEnabled() {
return window.innerWidth >= 1000;
}
/**
* Execute a debounced API request to save the sidebar user preference.
* The request is meant to fire 1000 milliseconds after the last click on
* the sidebar button.
*
* @param {HTMLInputElement} checkbox
* @param {number} timeout duration
* @param {boolean} shouldTriggerResize whether a resize event is needed.
* @return {any}
*/
function saveSidebarState( checkbox, timeout, shouldTriggerResize ) {
return debounce( function () {
api = api || new mw.Api();
api.saveOption( SIDEBAR_PREFERENCE_NAME, checkbox.checked ? 1 : 0 );
if ( !shouldTriggerResize ) {
return;
}
// Trigger a resize event so other parts of the page can adapt:
var event;
if ( typeof Event === 'function' ) {
event = new Event( 'resize' );
} else {
// IE11
event = window.document.createEvent( 'UIEvents' );
event.initUIEvent( 'resize', true, false, window, 0 );
}
window.dispatchEvent( event );
}, timeout );
}
/**
* Bind the event handler that saves the sidebar state to the click event
* on the sidebar button.
*
* @param {HTMLElement|null} checkbox
* @param {HTMLElement|null} button
*/
function bindSidebarClickEvent( checkbox, button ) {
if ( checkbox instanceof HTMLInputElement && button ) {
var handler = saveSidebarState( checkbox, 1000, true );
checkbox.addEventListener( 'input', function () {
if ( isPersistentEnabled() ) {
handler();
}
} );
}
}
var /** @type {boolean} */ wasCollapsedDuringResize = false;
/**
* Collapses the sidebar if screen resolution too small.
*
* @param {HTMLInputElement} checkbox
*/
function collapseSidebar( checkbox ) {
if ( checkbox.checked ) {
wasCollapsedDuringResize = true;
checkbox.checked = false;
saveSidebarState( checkbox, 0, false )();
}
}
/**
* Expands the sidebar when the window is resized if it was previously collapsed.
*
* @param {HTMLInputElement} checkbox
*/
function expandSidebar( checkbox ) {
if ( wasCollapsedDuringResize && !checkbox.checked ) {
wasCollapsedDuringResize = false;
checkbox.checked = true;
saveSidebarState( checkbox, 0, false )();
}
}
function init() {
var checkbox = /** @type {HTMLInputElement|null} */ (
window.document.getElementById( SIDEBAR_CHECKBOX_ID )
),
button = window.document.getElementById( SIDEBAR_BUTTON_ID );
if ( mw.config.get( 'wgUserName' ) && !mw.config.get( 'wgVectorDisableSidebarPersistence' ) ) {
bindSidebarClickEvent( checkbox, button );
}
// If the user has resized their window, an open sidebar will be taking up lots of space
// so we should disable it.
// When this happens the user must expand it again manually, to avoid conflicts with multiple
// open windows (for example when an editor is viewing 2 articles side by side).
if ( checkbox ) {
var mediaQuery = window.matchMedia( '(max-width: 1000px)' );
var onMediaQueryChangeCollapse = function ( /** @type {MediaQueryListEvent} */ event ) {
if ( event.matches ) {
// @ts-ignore we checked it already.
collapseSidebar( checkbox );
} else {
// @ts-ignore we checked it already.
expandSidebar( checkbox );
}
};
if ( mediaQuery.matches ) {
collapseSidebar( checkbox );
}
if ( mediaQuery.addEventListener ) {
mediaQuery.addEventListener( 'change', onMediaQueryChangeCollapse );
} else {
// Before Safari 14, MediaQueryList is based on EventTarget,
// so you must use addListener() and removeListener() to observe media query lists.
mediaQuery.addListener( onMediaQueryChangeCollapse );
}
}
}
module.exports = {
init: init
};