Redesign Special:Preferences for mobile
- Added a hook that checks if the preferences should have a mobile or desktop layout - Added descriptions to preference tabs, which now display as a stack layout in mobile - Added a new mobile JS file to control Special:Preferences when in mobile view - Built the mobile interface in the preferences form Bug: T311717 Change-Id: I468481b66bf96880d1779cd11a46e18745e2c894
This commit is contained in:
parent
fc88070dbb
commit
0ffdf80425
9 changed files with 416 additions and 129 deletions
25
includes/Hook/PreferencesGetLayoutHook.php
Normal file
25
includes/Hook/PreferencesGetLayoutHook.php
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
|
||||
namespace MediaWiki\Hook;
|
||||
|
||||
use Skin;
|
||||
|
||||
/**
|
||||
* This is a hook handler interface, see docs/Hooks.md.
|
||||
* Use the hook name "PreferencesGetLayout" to register handlers implementing this interface.
|
||||
*
|
||||
* @stable to implement
|
||||
* @ingroup Hooks
|
||||
*/
|
||||
interface PreferencesGetLayoutHook {
|
||||
/**
|
||||
* Use the hook to check if the preferences will have a mobile or desktop layout.
|
||||
*
|
||||
* @since 1.40
|
||||
* @param bool &$useMobileLayout a boolean which will indicate whether to use
|
||||
* a mobile layout or not
|
||||
* @param Skin $skin the skin being used
|
||||
* @return bool|void True or no return value to continue or false to abort
|
||||
*/
|
||||
public function onPreferencesGetLayout( &$useMobileLayout, $skin );
|
||||
}
|
||||
|
|
@ -304,6 +304,7 @@ class HookRunner implements
|
|||
\MediaWiki\Hook\ParserTestTablesHook,
|
||||
\MediaWiki\Hook\PasswordPoliciesForUserHook,
|
||||
\MediaWiki\Hook\PostLoginRedirectHook,
|
||||
\MediaWiki\Hook\PreferencesGetLayoutHook,
|
||||
\MediaWiki\Hook\PreferencesGetLegendHook,
|
||||
\MediaWiki\Hook\PrefsEmailAuditHook,
|
||||
\MediaWiki\Hook\ProtectionForm__buildFormHook,
|
||||
|
|
@ -3087,6 +3088,13 @@ class HookRunner implements
|
|||
);
|
||||
}
|
||||
|
||||
public function onPreferencesGetLayout( &$useMobileLayout, $skin ) {
|
||||
return $this->container->run(
|
||||
'PreferencesGetLayout',
|
||||
[ &$useMobileLayout, $skin ]
|
||||
);
|
||||
}
|
||||
|
||||
public function onPreferencesGetLegend( $form, $key, &$legend ) {
|
||||
return $this->container->run(
|
||||
'PreferencesGetLegend',
|
||||
|
|
|
|||
|
|
@ -155,6 +155,150 @@ class PreferencesFormOOUI extends OOUIHTMLForm {
|
|||
* @return string
|
||||
*/
|
||||
public function getBody() {
|
||||
$out = $this->getOutput();
|
||||
$this->getHookRunner()->onPreferencesGetLayout( $useMobileLayout, $out->getSkin() );
|
||||
$out->addJsConfigVars( [ 'wgSpecialPreferencesUseMobileLayout' => $useMobileLayout ] );
|
||||
|
||||
if ( $useMobileLayout ) {
|
||||
// Import the icons used in the mobile view
|
||||
$out->addModuleStyles(
|
||||
[
|
||||
'oojs-ui.styles.icons-user',
|
||||
'oojs-ui.styles.icons-editing-core',
|
||||
'oojs-ui.styles.icons-editing-advanced',
|
||||
'oojs-ui.styles.icons-wikimediaui',
|
||||
'oojs-ui.styles.icons-content',
|
||||
'oojs-ui.styles.icons-moderation',
|
||||
'oojs-ui.styles.icons-interactions',
|
||||
'oojs-ui.styles.icons-movement',
|
||||
'oojs-ui.styles.icons-wikimedia',
|
||||
'oojs-ui.styles.icons-media',
|
||||
'oojs-ui.styles.icons-accessibility',
|
||||
'oojs-ui.styles.icons-layout',
|
||||
]
|
||||
);
|
||||
$form = $this->createMobilePreferencesForm();
|
||||
} else {
|
||||
$form = $this->createDesktopPreferencesForm();
|
||||
}
|
||||
|
||||
$header = $this->formatFormHeader();
|
||||
|
||||
return $header . $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the "<legend>" for a given section key. Normally this is the
|
||||
* prefs-$key message but we'll allow extensions to override it.
|
||||
* @param string $key
|
||||
* @return string
|
||||
*/
|
||||
public function getLegend( $key ) {
|
||||
$legend = parent::getLegend( $key );
|
||||
$this->getHookRunner()->onPreferencesGetLegend( $this, $key, $legend );
|
||||
return $legend;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the keys of each top level preference section.
|
||||
* @return string[] List of section keys
|
||||
*/
|
||||
public function getPreferenceSections() {
|
||||
return array_keys( array_filter( $this->mFieldTree, 'is_array' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the preferences form for a mobile layout.
|
||||
* @return string
|
||||
*/
|
||||
private function createMobilePreferencesForm() {
|
||||
$prefPanels = [];
|
||||
|
||||
foreach ( $this->mFieldTree as $key => $val ) {
|
||||
if ( !is_array( $val ) ) {
|
||||
wfDebug( __METHOD__ . " encountered a field not attached to a section: '$key'" );
|
||||
continue;
|
||||
}
|
||||
$label = $this->getLegend( $key );
|
||||
$content =
|
||||
$this->getHeaderHtml( $key ) .
|
||||
$this->displaySection(
|
||||
$val,
|
||||
"",
|
||||
"mw-prefsection-$key-"
|
||||
) .
|
||||
$this->getFooterHtml( $key );
|
||||
|
||||
$prefPanel = new OOUI\PanelLayout( [
|
||||
'expanded' => false,
|
||||
'content' => [],
|
||||
'framed' => false,
|
||||
'classes' => [ 'mw-mobile-preferences-option' ]
|
||||
] );
|
||||
|
||||
$iconHeaderDiv = ( new OOUI\Tag( 'div' ) )
|
||||
->addClasses( [ 'mw-prefs-header-container' ] );
|
||||
|
||||
$prefTitle = ( new OOUI\Tag( 'h5' ) )->appendContent( $label )->addClasses( [ 'prefs-title' ] );
|
||||
$iconHeaderDiv->appendContent( $prefTitle );
|
||||
$prefPanel->appendContent( $iconHeaderDiv );
|
||||
$prefDescriptionMsg = $this->msg( "prefs-description-" . $key );
|
||||
$prefDescription = $prefDescriptionMsg->exists() ? $prefDescriptionMsg->text() : "";
|
||||
$prefPanel->appendContent( ( new OOUI\Tag( 'p' ) )
|
||||
->appendContent( $prefDescription )
|
||||
->addClasses( [ 'mw-prefs-description' ] )
|
||||
);
|
||||
$contentDiv = ( new OOUI\Tag( 'div' ) )->addClasses( [ 'mw-prefs-hidden' ] );
|
||||
$contentDiv->setAttributes( [
|
||||
'id' => 'mw-prefs-option-' . $key . '-content'
|
||||
] );
|
||||
$contentHeader = ( new OOUI\Tag( 'div' ) )->addClasses( [ 'mw-prefs-content-header' ] );
|
||||
$contentHeaderBackButton = new OOUI\IconWidget( [
|
||||
'icon' => 'previous',
|
||||
'label' => $this->msg( "prefs-back-label" ),
|
||||
'title' => $this->msg( "prefs-back-title" ),
|
||||
'classes' => [ 'mw-prefs-header-icon' ],
|
||||
] );
|
||||
$contentHeaderBackButton->setAttributes( [
|
||||
'id' => 'mw-prefs-option-' . $key . '-back-button',
|
||||
] );
|
||||
$contentHeaderTitle = ( new OOUI\Tag( 'h5' ) )
|
||||
->appendContent( $label )->addClasses( [ 'mw-prefs-header-title' ] );
|
||||
$formContent = new OOUI\Widget( [
|
||||
'content' => new OOUI\HtmlSnippet( $content )
|
||||
] );
|
||||
$hiddenForm = ( new OOUI\Tag( 'div' ) )->appendContent( $formContent );
|
||||
$contentHeader->appendContent( $contentHeaderBackButton );
|
||||
$contentHeader->appendContent( $contentHeaderTitle );
|
||||
$contentDiv->appendContent( $contentHeader );
|
||||
$contentDiv->appendContent( $hiddenForm );
|
||||
$prefPanel->appendContent( $contentDiv );
|
||||
$prefPanel->setAttributes( [
|
||||
'id' => 'mw-prefs-option-' . $key,
|
||||
] );
|
||||
$prefPanel->setInfusable( true );
|
||||
$prefPanels[] = $prefPanel;
|
||||
}
|
||||
|
||||
$form = new OOUI\StackLayout( [
|
||||
'items' => $prefPanels,
|
||||
'continuous' => true,
|
||||
'expanded' => false,
|
||||
'classes' => [ 'mw-mobile-preferences-container' ]
|
||||
] );
|
||||
$form->setAttributes( [
|
||||
'id' => 'mw-prefs-container',
|
||||
] );
|
||||
$form->setInfusable( true );
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the preferences form for a desktop layout.
|
||||
* @return string
|
||||
*/
|
||||
private function createDesktopPreferencesForm() {
|
||||
$tabPanels = [];
|
||||
foreach ( $this->mFieldTree as $key => $val ) {
|
||||
if ( !is_array( $val ) ) {
|
||||
|
|
@ -163,13 +307,13 @@ class PreferencesFormOOUI extends OOUIHTMLForm {
|
|||
}
|
||||
$label = $this->getLegend( $key );
|
||||
$content =
|
||||
$this->getHeaderText( $key ) .
|
||||
$this->getHeaderHtml( $key ) .
|
||||
$this->displaySection(
|
||||
$val,
|
||||
"",
|
||||
"mw-prefsection-$key-"
|
||||
) .
|
||||
$this->getFooterText( $key );
|
||||
$this->getFooterHtml( $key );
|
||||
|
||||
$tabPanels[] = new OOUI\TabPanelLayout( 'mw-prefsection-' . $key, [
|
||||
'classes' => [ 'mw-htmlform-autoinfuse-lazy' ],
|
||||
|
|
@ -197,7 +341,6 @@ class PreferencesFormOOUI extends OOUIHTMLForm {
|
|||
] );
|
||||
$indexLayout->addTabPanels( $tabPanels );
|
||||
|
||||
$header = $this->formatFormHeader();
|
||||
$form = new OOUI\PanelLayout( [
|
||||
'framed' => true,
|
||||
'expanded' => false,
|
||||
|
|
@ -205,26 +348,6 @@ class PreferencesFormOOUI extends OOUIHTMLForm {
|
|||
'content' => $indexLayout
|
||||
] );
|
||||
|
||||
return $header . $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the "<legend>" for a given section key. Normally this is the
|
||||
* prefs-$key message but we'll allow extensions to override it.
|
||||
* @param string $key
|
||||
* @return string
|
||||
*/
|
||||
public function getLegend( $key ) {
|
||||
$legend = parent::getLegend( $key );
|
||||
$this->getHookRunner()->onPreferencesGetLegend( $this, $key, $legend );
|
||||
return $legend;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the keys of each top level preference section.
|
||||
* @return string[] List of section keys
|
||||
*/
|
||||
public function getPreferenceSections() {
|
||||
return array_keys( array_filter( $this->mFieldTree, 'is_array' ) );
|
||||
return $form;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1108,6 +1108,15 @@
|
|||
"recentchangesdays": "Days to show in recent changes:",
|
||||
"recentchangesdays-max": "Maximum $1 {{PLURAL:$1|day|days}}",
|
||||
"recentchangescount": "Number of edits to show in recent changes, page histories, and in logs, by default:",
|
||||
"prefs-back-label": "Back",
|
||||
"prefs-back-title": "Back to preferences",
|
||||
"prefs-description-personal": "Control how you appear, connect, and communicate.",
|
||||
"prefs-description-rendering": "Configure skin, size, and reading options.",
|
||||
"prefs-description-editing": "Customize how you make, track, and review edits.",
|
||||
"prefs-description-rc": "Customise the recent changes feed.",
|
||||
"prefs-description-watchlist": "Manage and personalize the list of pages you track.",
|
||||
"prefs-description-searchoptions": "Choose how autocomplete and results work.",
|
||||
"prefs-description-misc": "Customize the table of contents.",
|
||||
"prefs-help-recentchangescount": "Maximum number: 1000",
|
||||
"prefs-help-watchlist-token2": "This is the secret key to the web feed of your watchlist.\nAnyone who knows it will be able to read your watchlist, so do not share it.\nIf you need to, [[Special:ResetTokens|you can reset it]].",
|
||||
"prefs-help-tokenmanagement": "You can see and reset the secret key for your account that can access the Web feed of your watchlist. Anyone who knows the key will be able to read your watchlist, so do not share it.",
|
||||
|
|
|
|||
|
|
@ -1349,6 +1349,15 @@
|
|||
"recentchangesdays": "Used in [[Special:Preferences]], tab \"Recent changes\".",
|
||||
"recentchangesdays-max": "Shown as hint in [[Special:Preferences]], tab \"Recent changes\". Parameters:\n* $1 - number of days\nSee also:\n* {{msg-mw|Prefs-watchlist-days-max}}",
|
||||
"recentchangescount": "Used in [[Special:Preferences]], tab \"Recent changes\".",
|
||||
"prefs-back-label": "Used in [[Special:Preferences]] as a label for a back button",
|
||||
"prefs-back-title": "Used in [[Special:Preferences]] as the title for a back button",
|
||||
"prefs-description-personal": "Used in [[Special:Preferences]] for mobile to describe the User Profile section. ",
|
||||
"prefs-description-rendering": "Used in [[Special:Preferences]] for mobile to describe the Appearance section. ",
|
||||
"prefs-description-editing": "Used in [[Special:Preferences]] for mobile to describe the Editing section. ",
|
||||
"prefs-description-rc": "Used in [[Special:Preferences]] for mobile to describe the Recent Changes section. ",
|
||||
"prefs-description-watchlist": "Used in [[Special:Preferences]] for mobile to describe the Watchlist section. ",
|
||||
"prefs-description-searchoptions": "Used in [[Special:Preferences]] for mobile to describe the Search section. ",
|
||||
"prefs-description-misc": "Used in [[Special:Preferences]] for mobile to describe the Misc section.",
|
||||
"prefs-help-recentchangescount": "Used in [[Special:Preferences]], tab \"Recent changes\".",
|
||||
"prefs-help-watchlist-token2": "Used in [[Special:Preferences]], tab Watchlist. (Formerly in {{msg-mw|prefs-help-watchlist-token}}.)",
|
||||
"prefs-help-tokenmanagement": "Used in [[Special:Preferences]], Watchlist tab.",
|
||||
|
|
|
|||
|
|
@ -2284,6 +2284,7 @@ return [
|
|||
'resources/src/mediawiki.special.preferences.ooui/confirmClose.js',
|
||||
'resources/src/mediawiki.special.preferences.ooui/convertmessagebox.js',
|
||||
'resources/src/mediawiki.special.preferences.ooui/editfont.js',
|
||||
'resources/src/mediawiki.special.preferences.ooui/mobile.js',
|
||||
'resources/src/mediawiki.special.preferences.ooui/skinPrefs.js',
|
||||
'resources/src/mediawiki.special.preferences.ooui/signature.js',
|
||||
'resources/src/mediawiki.special.preferences.ooui/tabs.js',
|
||||
|
|
|
|||
49
resources/src/mediawiki.special.preferences.ooui/mobile.js
Normal file
49
resources/src/mediawiki.special.preferences.ooui/mobile.js
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
/*!
|
||||
* JavaScript for Special:Preferences: Tab navigation.
|
||||
*/
|
||||
( function () {
|
||||
var useMobileLayout = mw.config.get( 'wgSpecialPreferencesUseMobileLayout' ) === null ? false : mw.config.get( 'wgSpecialPreferencesUseMobileLayout' );
|
||||
// New for T311717: Check if a user will display the mobile layout
|
||||
if ( useMobileLayout ) {
|
||||
$( function () {
|
||||
var $prefContent, prefContentId;
|
||||
var options = OO.ui.infuse( $( '.mw-mobile-preferences-container' ) );
|
||||
var $preferencesContainer = $( '#preferences' );
|
||||
var $prefOptionsContainer = $( '#mw-prefs-container' );
|
||||
function triggerPreferenceMenu( elementId ) {
|
||||
prefContentId = elementId + '-content';
|
||||
$prefContent = $( '#' + prefContentId );
|
||||
$prefContent.removeClass( 'mw-prefs-hidden' );
|
||||
$prefContent.attr( 'style', 'display:block;' );
|
||||
$prefOptionsContainer.addClass( 'mw-prefs-hidden' );
|
||||
$prefOptionsContainer.removeAttr( 'style' );
|
||||
$preferencesContainer.prepend( $prefContent );
|
||||
// Snippet based on https://stackoverflow.com/a/58944651/4612594
|
||||
// This prevents the page from scrolling down to where it was previously.
|
||||
if ( 'scrollRestoration' in history ) {
|
||||
history.scrollRestoration = 'manual';
|
||||
}
|
||||
window.scrollTo( 0, 0 );
|
||||
}
|
||||
function triggerBackToOptions( elementId ) {
|
||||
prefContentId = elementId + '-content';
|
||||
$prefContent = $( '#' + prefContentId );
|
||||
$prefOptionsContainer.removeClass( 'mw-prefs-hidden' );
|
||||
$prefOptionsContainer.attr( 'style', 'display:block;' );
|
||||
$prefContent.addClass( 'mw-prefs-hidden' );
|
||||
$prefContent.removeAttr( 'style' );
|
||||
$preferencesContainer.prepend( $prefOptionsContainer );
|
||||
}
|
||||
// Add a click event for each preference option
|
||||
options.items.forEach( function ( element ) {
|
||||
$( '#' + element.elementId ).on( 'click', function () {
|
||||
triggerPreferenceMenu( element.elementId );
|
||||
} );
|
||||
var backButtonId = '#' + element.elementId + '-back-button';
|
||||
$( backButtonId ).on( 'click', function () {
|
||||
triggerBackToOptions( element.elementId );
|
||||
} );
|
||||
} );
|
||||
} );
|
||||
}
|
||||
}() );
|
||||
|
|
@ -2,121 +2,124 @@
|
|||
* JavaScript for Special:Preferences: Tab navigation.
|
||||
*/
|
||||
( function () {
|
||||
$( function () {
|
||||
// Make sure the accessibility tip is focussable so that keyboard users take notice,
|
||||
// but hide it by default to reduce visual clutter.
|
||||
// Make sure it becomes visible when focused.
|
||||
$( '<div>' ).addClass( 'mw-navigation-hint' )
|
||||
.text( mw.msg( 'prefs-tabs-navigation-hint' ) )
|
||||
.attr( {
|
||||
tabIndex: 0
|
||||
} )
|
||||
.insertBefore( '.mw-htmlform-ooui-wrapper' );
|
||||
var useMobileLayout = mw.config.get( 'wgSpecialPreferencesUseMobileLayout', false );
|
||||
if ( !useMobileLayout ) {
|
||||
$( function () {
|
||||
// Make sure the accessibility tip is focussable so that keyboard users take notice,
|
||||
// but hide it by default to reduce visual clutter.
|
||||
// Make sure it becomes visible when focused.
|
||||
$( '<div>' ).addClass( 'mw-navigation-hint' )
|
||||
.text( mw.msg( 'prefs-tabs-navigation-hint' ) )
|
||||
.attr( {
|
||||
tabIndex: 0
|
||||
} )
|
||||
.insertBefore( '.mw-htmlform-ooui-wrapper' );
|
||||
|
||||
var tabs = OO.ui.infuse( $( '.mw-prefs-tabs' ) );
|
||||
var tabs = OO.ui.infuse( $( '.mw-prefs-tabs' ) );
|
||||
|
||||
// Support: Chrome
|
||||
// https://bugs.chromium.org/p/chromium/issues/detail?id=1252507
|
||||
//
|
||||
// Infusing the tabs above involves detaching all the tabs' content from the DOM momentarily,
|
||||
// which causes the :target selector (used in mediawiki.special.preferences.styles.ooui.less)
|
||||
// not to match anything inside the tabs in Chrome. Twiddling location.href makes it work.
|
||||
// Only do it when a fragment is present, otherwise the page would be reloaded.
|
||||
if ( location.href.indexOf( '#' ) !== -1 ) {
|
||||
// eslint-disable-next-line no-self-assign
|
||||
location.href = location.href;
|
||||
}
|
||||
|
||||
tabs.$element.addClass( 'mw-prefs-tabs-infused' );
|
||||
|
||||
function enhancePanel( panel ) {
|
||||
if ( !panel.$element.data( 'mw-section-infused' ) ) {
|
||||
panel.$element.removeClass( 'mw-htmlform-autoinfuse-lazy' );
|
||||
mw.hook( 'htmlform.enhance' ).fire( panel.$element );
|
||||
panel.$element.data( 'mw-section-infused', true );
|
||||
// Support: Chrome
|
||||
// https://bugs.chromium.org/p/chromium/issues/detail?id=1252507
|
||||
//
|
||||
// Infusing the tabs above involves detaching all the tabs' content from the DOM momentarily,
|
||||
// which causes the :target selector (used in mediawiki.special.preferences.styles.ooui.less)
|
||||
// not to match anything inside the tabs in Chrome. Twiddling location.href makes it work.
|
||||
// Only do it when a fragment is present, otherwise the page would be reloaded.
|
||||
if ( location.href.indexOf( '#' ) !== -1 ) {
|
||||
// eslint-disable-next-line no-self-assign
|
||||
location.href = location.href;
|
||||
}
|
||||
}
|
||||
|
||||
var switchingNoHash;
|
||||
tabs.$element.addClass( 'mw-prefs-tabs-infused' );
|
||||
|
||||
function onTabPanelSet( panel ) {
|
||||
if ( switchingNoHash ) {
|
||||
return;
|
||||
}
|
||||
// Handle hash manually to prevent jumping,
|
||||
// therefore save and restore scrollTop to prevent jumping.
|
||||
var scrollTop = $( window ).scrollTop();
|
||||
// Changing the hash apparently causes keyboard focus to be lost?
|
||||
// Save and restore it. This makes no sense though.
|
||||
var active = document.activeElement;
|
||||
location.hash = '#' + panel.getName();
|
||||
if ( active ) {
|
||||
active.focus();
|
||||
}
|
||||
$( window ).scrollTop( scrollTop );
|
||||
}
|
||||
|
||||
tabs.on( 'set', onTabPanelSet );
|
||||
|
||||
/**
|
||||
* @ignore
|
||||
* @param {string} name The name of a tab
|
||||
* @param {boolean} [noHash] A hash will be set according to the current
|
||||
* open section. Use this flag to suppress this.
|
||||
*/
|
||||
function switchPrefTab( name, noHash ) {
|
||||
if ( noHash ) {
|
||||
switchingNoHash = true;
|
||||
}
|
||||
tabs.setTabPanel( name );
|
||||
enhancePanel( tabs.getCurrentTabPanel() );
|
||||
if ( noHash ) {
|
||||
switchingNoHash = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Jump to correct section as indicated by the hash.
|
||||
// This function is called onload and onhashchange.
|
||||
function detectHash() {
|
||||
var hash = location.hash;
|
||||
if ( /^#mw-prefsection-[\w]+$/.test( hash ) ) {
|
||||
mw.storage.session.remove( 'mwpreferences-prevTab' );
|
||||
switchPrefTab( hash.slice( 1 ) );
|
||||
} else if ( /^#mw-[\w-]+$/.test( hash ) ) {
|
||||
var matchedElement = document.getElementById( hash.slice( 1 ) );
|
||||
var $parentSection = $( matchedElement ).closest( '.mw-prefs-section-fieldset' );
|
||||
if ( $parentSection.length ) {
|
||||
mw.storage.session.remove( 'mwpreferences-prevTab' );
|
||||
// Switch to proper tab and scroll to selected item.
|
||||
switchPrefTab( $parentSection.attr( 'id' ), true );
|
||||
matchedElement.scrollIntoView();
|
||||
function enhancePanel( panel ) {
|
||||
if ( !panel.$element.data( 'mw-section-infused' ) ) {
|
||||
panel.$element.removeClass( 'mw-htmlform-autoinfuse-lazy' );
|
||||
mw.hook( 'htmlform.enhance' ).fire( panel.$element );
|
||||
panel.$element.data( 'mw-section-infused', true );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$( window ).on( 'hashchange', function () {
|
||||
var hash = location.hash;
|
||||
if ( /^#mw-[\w-]+/.test( hash ) ) {
|
||||
detectHash();
|
||||
} else if ( hash === '' ) {
|
||||
switchPrefTab( 'mw-prefsection-personal', true );
|
||||
var switchingNoHash;
|
||||
|
||||
function onTabPanelSet( panel ) {
|
||||
if ( switchingNoHash ) {
|
||||
return;
|
||||
}
|
||||
// Handle hash manually to prevent jumping,
|
||||
// therefore save and restore scrollTop to prevent jumping.
|
||||
var scrollTop = $( window ).scrollTop();
|
||||
// Changing the hash apparently causes keyboard focus to be lost?
|
||||
// Save and restore it. This makes no sense though.
|
||||
var active = document.activeElement;
|
||||
location.hash = '#' + panel.getName();
|
||||
if ( active ) {
|
||||
active.focus();
|
||||
}
|
||||
$( window ).scrollTop( scrollTop );
|
||||
}
|
||||
} )
|
||||
// Run the function immediately to select the proper tab on startup.
|
||||
.trigger( 'hashchange' );
|
||||
|
||||
// Restore the active tab after saving the preferences
|
||||
var previousTab = mw.storage.session.get( 'mwpreferences-prevTab' );
|
||||
if ( previousTab ) {
|
||||
switchPrefTab( previousTab, true );
|
||||
// Deleting the key, the tab states should be reset until we press Save
|
||||
mw.storage.session.remove( 'mwpreferences-prevTab' );
|
||||
}
|
||||
tabs.on( 'set', onTabPanelSet );
|
||||
|
||||
/**
|
||||
* @ignore
|
||||
* @param {string} name The name of a tab
|
||||
* @param {boolean} [noHash] A hash will be set according to the current
|
||||
* open section. Use this flag to suppress this.
|
||||
*/
|
||||
function switchPrefTab( name, noHash ) {
|
||||
if ( noHash ) {
|
||||
switchingNoHash = true;
|
||||
}
|
||||
tabs.setTabPanel( name );
|
||||
enhancePanel( tabs.getCurrentTabPanel() );
|
||||
if ( noHash ) {
|
||||
switchingNoHash = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Jump to correct section as indicated by the hash.
|
||||
// This function is called onload and onhashchange.
|
||||
function detectHash() {
|
||||
var hash = location.hash;
|
||||
if ( /^#mw-prefsection-[\w]+$/.test( hash ) ) {
|
||||
mw.storage.session.remove( 'mwpreferences-prevTab' );
|
||||
switchPrefTab( hash.slice( 1 ) );
|
||||
} else if ( /^#mw-[\w-]+$/.test( hash ) ) {
|
||||
var matchedElement = document.getElementById( hash.slice( 1 ) );
|
||||
var $parentSection = $( matchedElement ).closest( '.mw-prefs-section-fieldset' );
|
||||
if ( $parentSection.length ) {
|
||||
mw.storage.session.remove( 'mwpreferences-prevTab' );
|
||||
// Switch to proper tab and scroll to selected item.
|
||||
switchPrefTab( $parentSection.attr( 'id' ), true );
|
||||
matchedElement.scrollIntoView();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$( window ).on( 'hashchange', function () {
|
||||
var hash = location.hash;
|
||||
if ( /^#mw-[\w-]+/.test( hash ) ) {
|
||||
detectHash();
|
||||
} else if ( hash === '' ) {
|
||||
switchPrefTab( 'mw-prefsection-personal', true );
|
||||
}
|
||||
} )
|
||||
// Run the function immediately to select the proper tab on startup.
|
||||
.trigger( 'hashchange' );
|
||||
|
||||
// Restore the active tab after saving the preferences
|
||||
var previousTab = mw.storage.session.get( 'mwpreferences-prevTab' );
|
||||
if ( previousTab ) {
|
||||
switchPrefTab( previousTab, true );
|
||||
// Deleting the key, the tab states should be reset until we press Save
|
||||
mw.storage.session.remove( 'mwpreferences-prevTab' );
|
||||
}
|
||||
|
||||
$( '#mw-prefs-form' ).on( 'submit', function () {
|
||||
var value = tabs.getCurrentTabPanelName();
|
||||
mw.storage.session.set( 'mwpreferences-prevTab', value );
|
||||
} );
|
||||
|
||||
$( '#mw-prefs-form' ).on( 'submit', function () {
|
||||
var value = tabs.getCurrentTabPanelName();
|
||||
mw.storage.session.set( 'mwpreferences-prevTab', value );
|
||||
} );
|
||||
|
||||
} );
|
||||
}
|
||||
}() );
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
@import 'mediawiki.skin.variables.less';
|
||||
@import 'mediawiki.mixins.less';
|
||||
@import 'mediawiki.ui/variables';
|
||||
|
||||
/* Uses standard message block colors, compare mediawiki.legacy/shared.css */
|
||||
.mw-email-not-authenticated .oo-ui-labelWidget,
|
||||
|
|
@ -185,3 +186,62 @@
|
|||
#wpTimeCorrection .oo-ui-textInputWidget {
|
||||
margin-top: 0.5em;
|
||||
}
|
||||
|
||||
/* T311717 - Styles for Special:Preferences on mobile
|
||||
These are used when users navigate to Special:Preferences
|
||||
with params ?useskin=vector&useformat=mobile
|
||||
*/
|
||||
.mw-mobile-preferences-option {
|
||||
cursor: pointer;
|
||||
padding-top: 0.3125em;
|
||||
border-bottom: 0.0625em solid @colorGray12;
|
||||
}
|
||||
|
||||
.mw-mobile-preferences-option:hover {
|
||||
background-color: @colorGray15;
|
||||
}
|
||||
|
||||
.mw-mobile-preferences-option:last-child {
|
||||
border-bottom: none; // stylelint-disable-line declaration-property-value-disallowed-list
|
||||
}
|
||||
|
||||
.mw-prefs-title {
|
||||
font-weight: normal;
|
||||
font-size: 1em;
|
||||
line-height: 1.25em;
|
||||
color: @colorGray2;
|
||||
}
|
||||
|
||||
.mw-prefs-description {
|
||||
font-weight: normal;
|
||||
font-size: 0.875em;
|
||||
line-height: 1.25em;
|
||||
color: @colorGray7;
|
||||
margin-top: 0 !important; /* stylelint-disable-line declaration-no-important */
|
||||
}
|
||||
|
||||
.mw-prefs-header-container {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.mw-prefs-hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.mw-prefs-content-header {
|
||||
width: 100%;
|
||||
height: 3.125em;
|
||||
display: block;
|
||||
border-bottom: 1px solid @colorGray12;
|
||||
box-shadow: 0 0.25em 0.125em -0.1875em rgba( 0, 0, 0, 0.25 );
|
||||
}
|
||||
|
||||
.oo-ui-iconWidget.mw-prefs-header-icon {
|
||||
color: @colorGray2;
|
||||
cursor: pointer;
|
||||
margin: 0.25em 0.5em;
|
||||
}
|
||||
|
||||
.mw-prefs-header-title {
|
||||
display: inline-flex;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue