From aa8d628e13f361418715b449bbdcc35343185c0d Mon Sep 17 00:00:00 2001 From: Jon Robson Date: Thu, 1 Sep 2022 14:22:31 -0700 Subject: [PATCH] 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 --- .../skins.vector.js/sidebarPersistence.js | 87 ++++++++++++++++++- 1 file changed, 83 insertions(+), 4 deletions(-) diff --git a/resources/skins.vector.js/sidebarPersistence.js b/resources/skins.vector.js/sidebarPersistence.js index 8014d017f..4f637c0f4 100644 --- a/resources/skins.vector.js/sidebarPersistence.js +++ b/resources/skins.vector.js/sidebarPersistence.js @@ -11,19 +11,36 @@ var /** @type {MwApi} */api, 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 ) { +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' ) { @@ -34,7 +51,7 @@ function saveSidebarState( checkbox ) { event.initUIEvent( 'resize', true, false, window, 0 ); } window.dispatchEvent( event ); - }, 1000 ); + }, timeout ); } /** @@ -46,17 +63,79 @@ function saveSidebarState( checkbox ) { */ function bindSidebarClickEvent( checkbox, button ) { if ( checkbox instanceof HTMLInputElement && button ) { - checkbox.addEventListener( 'input', saveSidebarState( checkbox ) ); + 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 = window.document.getElementById( SIDEBAR_CHECKBOX_ID ), + 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 = {