feat: implement theme toggle (#201)
这个提交包含在:
父节点
bf7f458b79
当前提交
e30e6ce52c
|
@ -7,4 +7,5 @@
|
|||
# ES6 files
|
||||
resources/skins.citizen.scripts.search/typeahead-init.js
|
||||
resources/skins.citizen.scripts.search/underscore.partial.js
|
||||
resources/skins.citizen.scripts.search/wm-typeahead.js
|
||||
resources/skins.citizen.scripts.search/wm-typeahead.js
|
||||
resources/skins.citizen.scripts.theme-switcher/inline.js
|
||||
|
|
53
i18n/en.json
53
i18n/en.json
|
@ -1,26 +1,27 @@
|
|||
{
|
||||
"@metadata": {
|
||||
"authors": [
|
||||
"[https://www.mediawiki.org/wiki/User:Alistair3149 Alistair3149]",
|
||||
"[https://www.mediawiki.org/wiki/User:Octfx Octfx]"
|
||||
]
|
||||
},
|
||||
"skinname-citizen": "Citizen",
|
||||
"citizen-skin-desc": "A responsive skin developed for the Star Citizen Wiki",
|
||||
|
||||
"citizen.css": "/* All CSS here will be loaded for users of the Citizen skin */",
|
||||
"citizen.js": "/* All JavaScript here will be loaded for users of the Citizen skin */",
|
||||
|
||||
"citizen-drawer-toggle": "Toggle menu",
|
||||
"citizen-search-toggle": "Toggle search",
|
||||
|
||||
"citizen-footer-desc": "Edit this text on MediaWiki:Citizen-footer-desc",
|
||||
"citizen-footer-tagline": "Edit this text on MediaWiki:Citizen-footer-tagline",
|
||||
|
||||
"citizen-search-fulltext": "Search pages containing",
|
||||
|
||||
"prefs-citizen-theme-label": "Theme",
|
||||
"prefs-citizen-theme-option-auto": "Auto",
|
||||
"prefs-citizen-theme-option-light": "Light",
|
||||
"prefs-citizen-theme-option-dark": "Dark"
|
||||
}
|
||||
{
|
||||
"@metadata": {
|
||||
"authors": [
|
||||
"[https://www.mediawiki.org/wiki/User:Alistair3149 Alistair3149]",
|
||||
"[https://www.mediawiki.org/wiki/User:Octfx Octfx]"
|
||||
]
|
||||
},
|
||||
"skinname-citizen": "Citizen",
|
||||
"citizen-skin-desc": "A responsive skin developed for the Star Citizen Wiki",
|
||||
|
||||
"citizen.css": "/* All CSS here will be loaded for users of the Citizen skin */",
|
||||
"citizen.js": "/* All JavaScript here will be loaded for users of the Citizen skin */",
|
||||
|
||||
"citizen-drawer-toggle": "Toggle menu",
|
||||
"citizen-search-toggle": "Toggle search",
|
||||
"citizen-theme-toggle": "Toggle theme color",
|
||||
|
||||
"citizen-footer-desc": "Edit this text on MediaWiki:Citizen-footer-desc",
|
||||
"citizen-footer-tagline": "Edit this text on MediaWiki:Citizen-footer-tagline",
|
||||
|
||||
"citizen-search-fulltext": "Search pages containing",
|
||||
|
||||
"prefs-citizen-theme-label": "Theme",
|
||||
"prefs-citizen-theme-option-auto": "Auto",
|
||||
"prefs-citizen-theme-option-light": "Light",
|
||||
"prefs-citizen-theme-option-dark": "Dark"
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
"citizen.js": "{{optional}}",
|
||||
"citizen-drawer-toggle": "Tooltip of drawer menu toggle",
|
||||
"citizen-search-toggle": "Tooltip of search box toggle",
|
||||
"citizen-theme-toggle": "Tooltip of theme color toggle",
|
||||
"citizen-footer-desc": "Edit this text on MediaWiki:Citizen-footer-desc",
|
||||
"citizen-footer-tagline": "Edit this text on MediaWiki:Citizen-footer-tagline",
|
||||
"citizen-search-fulltext": "Fulltext search suggestion",
|
||||
|
|
|
@ -196,4 +196,21 @@ class CitizenHooks {
|
|||
$prefs += $citizenPrefs;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the override cookie if the theme was changed through the user preferences
|
||||
*
|
||||
* @param array $formData Array of user submitted data
|
||||
* @param \HTMLForm $form HTMLForm object, also a ContextSource
|
||||
* @param User $user User with preferences to be saved
|
||||
* @param bool &$result Boolean indicating success
|
||||
* @param array $oldUserOptions Array with user's old options (before save)
|
||||
* @return bool|void True or no return value to continue or false to abort
|
||||
*/
|
||||
public static function onPreferencesFormPreSave( $formData, $form, $user, &$result, $oldUserOptions ) {
|
||||
if (isset($formData['CitizenThemeUser']) && $formData['CitizenThemeUser'] !== 'auto') {
|
||||
// Reset override cookie from theme toggle
|
||||
$form->getOutput()->getRequest()->response()->setCookie('skin-citizen-theme-override', null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,7 +45,7 @@ class SkinCitizen extends SkinMustache {
|
|||
$out = $skin->getOutput();
|
||||
|
||||
// Theme handler
|
||||
$skin->setSkinTheme( $out, $options );
|
||||
$skin->setSkinTheme( $out );
|
||||
|
||||
// Responsive layout
|
||||
// Replace with core responsive option if it is implemented in 1.36+
|
||||
|
@ -153,6 +153,7 @@ class SkinCitizen extends SkinMustache {
|
|||
'data-drawer' => $this->buildDrawer(),
|
||||
'data-extratools' => $this->getExtraTools(),
|
||||
'data-search-box' => $this->buildSearchProps(),
|
||||
'data-theme-toggle' => $this->buildThemeToggleProps(),
|
||||
],
|
||||
|
||||
'data-pagetools' => $this->buildPageTools(),
|
||||
|
@ -452,6 +453,22 @@ class SkinCitizen extends SkinMustache {
|
|||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the theme toggle
|
||||
*
|
||||
* @return array
|
||||
* @throws MWException
|
||||
*/
|
||||
private function buildThemeToggleProps() : array {
|
||||
$skin = $this->getSkin();
|
||||
|
||||
$toggleMsg = $skin->msg( 'citizen-theme-toggle' )->text();
|
||||
|
||||
return [
|
||||
'msg-citizen-theme-toggle-shortcut' => $toggleMsg,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Render page-related tools
|
||||
* Possible visibility conditions:
|
||||
|
@ -742,26 +759,31 @@ class SkinCitizen extends SkinMustache {
|
|||
* If the theme is set to auto, the theme switcher script will be added
|
||||
*
|
||||
* @param OutputPage $out
|
||||
* @param array &$skinOptions
|
||||
*/
|
||||
private function setSkinTheme( OutputPage $out, array &$skinOptions ) {
|
||||
private function setSkinTheme( OutputPage $out ) {
|
||||
// Set theme to site theme
|
||||
$theme = $this->getConfigValue( 'CitizenThemeDefault' );
|
||||
$theme = $this->getConfigValue( 'CitizenThemeDefault' ) ?? 'auto';
|
||||
|
||||
// Set theme to user theme if registered
|
||||
if ( $this->getUser()->isRegistered() ) {
|
||||
$theme = $this->getUser()->getOption( 'CitizenThemeUser' );
|
||||
$theme = MediaWikiServices::getInstance()->getUserOptionsLookup()->getOption(
|
||||
$this->getUser(),
|
||||
'CitizenThemeUser',
|
||||
'auto'
|
||||
);
|
||||
}
|
||||
|
||||
// Add HTML class based on theme set
|
||||
$out->addHtmlClasses( 'skin-citizen-' . $theme );
|
||||
|
||||
// Load theme switcher script if auto
|
||||
if ( $theme === 'auto' ) {
|
||||
$skinOptions['scripts'] = array_merge(
|
||||
$skinOptions['scripts'],
|
||||
[ 'skins.citizen.scripts.theme-switcher' ]
|
||||
);
|
||||
if ($this->getRequest()->getCookie('skin-citizen-theme-override') === null) {
|
||||
// Only set the theme cookie if the theme wasn't overridden by the user through the button
|
||||
$this->getRequest()->response()->setCookie('skin-citizen-theme', $theme, 0, [
|
||||
'httpOnly' => false,
|
||||
]);
|
||||
}
|
||||
|
||||
// Script content at 'skins.citizen.scripts.theme-switcher/inline.js
|
||||
$this->getOutput()->addHeadItem('theme-switcher', '<script>(()=>{try{const t=document.cookie.match(/skin-citizen-theme=(dark|light|auto)/),e=null!==t?t.pop():null;null!==e&&(document.documentElement.classList.remove(...["auto","dark","light"].map(t=>"skin-citizen-"+t)),document.documentElement.classList.add("skin-citizen-"+e))}catch(t){}})();</script>');
|
||||
$this->getOutput()->addModules('skins.citizen.scripts.theme-switcher');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,5 +8,6 @@
|
|||
<div class="mw-header-tools">
|
||||
{{#data-extratools}}{{>Menu}}{{/data-extratools}}
|
||||
{{#data-search-box}}{{>SearchBox}}{{/data-search-box}}
|
||||
{{#data-theme-toggle}}{{>ThemeToggle}}{{/data-theme-toggle}}
|
||||
</div>
|
||||
</header>
|
||||
</header>
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
{{!
|
||||
string msg-citizen-theme-toggle-shortcut message with shortcut key for search toggle
|
||||
}}
|
||||
<button
|
||||
role="button"
|
||||
id="theme-toggle"
|
||||
class="mw-theme-toggle"
|
||||
title="{{msg-citizen-theme-toggle-shortcut}}">X</button>
|
||||
|
|
@ -50,4 +50,4 @@
|
|||
{{#data-footer}}{{>Footer}}{{/data-footer}}
|
||||
<aside class="mw-sidebar-sitename">
|
||||
<a class="mw-wiki-title" {{{html-mainpage-attributes}}}>{{{msg-sitetitle}}}</a>
|
||||
</aside>
|
||||
</aside>
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" viewBox="0 0 24 24" fill="black" width="20px"
|
||||
height="20px">
|
||||
<rect fill="none" height="24" width="24"/>
|
||||
<path
|
||||
d="M12,3c-4.97,0-9,4.03-9,9s4.03,9,9,9s9-4.03,9-9c0-0.46-0.04-0.92-0.1-1.36c-0.98,1.37-2.58,2.26-4.4,2.26 c-2.98,0-5.4-2.42-5.4-5.4c0-1.81,0.89-3.42,2.26-4.4C12.92,3.04,12.46,3,12,3L12,3z"/>
|
||||
</svg>
|
之后 宽度: | 高度: | 大小: 388 B |
|
@ -0,0 +1,6 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" viewBox="0 0 24 24" fill="black" width="20px"
|
||||
height="20px">
|
||||
<rect fill="none" height="24" width="24"/>
|
||||
<path
|
||||
d="M12,7c-2.76,0-5,2.24-5,5s2.24,5,5,5s5-2.24,5-5S14.76,7,12,7L12,7z M2,13l2,0c0.55,0,1-0.45,1-1s-0.45-1-1-1l-2,0 c-0.55,0-1,0.45-1,1S1.45,13,2,13z M20,13l2,0c0.55,0,1-0.45,1-1s-0.45-1-1-1l-2,0c-0.55,0-1,0.45-1,1S19.45,13,20,13z M11,2v2 c0,0.55,0.45,1,1,1s1-0.45,1-1V2c0-0.55-0.45-1-1-1S11,1.45,11,2z M11,20v2c0,0.55,0.45,1,1,1s1-0.45,1-1v-2c0-0.55-0.45-1-1-1 C11.45,19,11,19.45,11,20z M5.99,4.58c-0.39-0.39-1.03-0.39-1.41,0c-0.39,0.39-0.39,1.03,0,1.41l1.06,1.06 c0.39,0.39,1.03,0.39,1.41,0s0.39-1.03,0-1.41L5.99,4.58z M18.36,16.95c-0.39-0.39-1.03-0.39-1.41,0c-0.39,0.39-0.39,1.03,0,1.41 l1.06,1.06c0.39,0.39,1.03,0.39,1.41,0c0.39-0.39,0.39-1.03,0-1.41L18.36,16.95z M19.42,5.99c0.39-0.39,0.39-1.03,0-1.41 c-0.39-0.39-1.03-0.39-1.41,0l-1.06,1.06c-0.39,0.39-0.39,1.03,0,1.41s1.03,0.39,1.41,0L19.42,5.99z M7.05,18.36 c0.39-0.39,0.39-1.03,0-1.41c-0.39-0.39-1.03-0.39-1.41,0l-1.06,1.06c-0.39,0.39-0.39,1.03,0,1.41s1.03,0.39,1.41,0L7.05,18.36z"/>
|
||||
</svg>
|
之后 宽度: | 高度: | 大小: 1.1 KiB |
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* Citizen - Inline script used in SkinCitizen.php
|
||||
*
|
||||
* https://starcitizen.tools
|
||||
*/
|
||||
(() => {
|
||||
try {
|
||||
const cookieTheme = document.cookie.match(/skin-citizen-theme=(dark|light|auto)/);
|
||||
const theme = cookieTheme !== null ? cookieTheme.pop() : null;
|
||||
|
||||
if (theme !== null) {
|
||||
document.documentElement.classList.remove(...['auto', 'dark', 'light'].map(theme => {
|
||||
return 'skin-citizen-' + theme;
|
||||
}));
|
||||
document.documentElement.classList.add('skin-citizen-' + theme);
|
||||
}
|
||||
} catch (e) {
|
||||
}
|
||||
})();
|
|
@ -4,53 +4,50 @@
|
|||
*/
|
||||
|
||||
( function () {
|
||||
var isGlobalAutoSet,
|
||||
isUserPreferenceAuto,
|
||||
enableAutoSwitcher,
|
||||
switchColorScheme,
|
||||
useDarkTheme,
|
||||
prefersColorSchemeDarkQuery;
|
||||
var prefersColorSchemeDarkQuery,
|
||||
userTheme,
|
||||
theme;
|
||||
|
||||
if ( typeof window.mw === 'undefined' ) {
|
||||
return;
|
||||
}
|
||||
|
||||
isGlobalAutoSet = window.mw.config.get( 'wgCitizenThemeDefault' ) === 'auto' ||
|
||||
window.mw.config.get( 'wgCitizenThemeDefault' ) === null;
|
||||
|
||||
isUserPreferenceAuto = window.mw.user.options.get( 'CitizenThemeUser' ) === 'auto';
|
||||
|
||||
enableAutoSwitcher = isGlobalAutoSet || isUserPreferenceAuto;
|
||||
|
||||
if ( !enableAutoSwitcher ) {
|
||||
return;
|
||||
theme = window.mw.config.get( 'wgCitizenThemeDefault' );
|
||||
if ( theme === null ) {
|
||||
theme = 'auto';
|
||||
}
|
||||
|
||||
switchColorScheme = function ( useDark ) {
|
||||
var dark;
|
||||
userTheme = window.mw.user.options.get( 'CitizenThemeUser' );
|
||||
|
||||
if ( useDark ) {
|
||||
document.documentElement.classList.add( 'skin-citizen-dark' );
|
||||
document.documentElement.classList.remove( 'skin-citizen-light' );
|
||||
dark = true;
|
||||
} else {
|
||||
document.documentElement.classList.add( 'skin-citizen-light' );
|
||||
document.documentElement.classList.remove( 'skin-citizen-dark' );
|
||||
dark = false;
|
||||
}
|
||||
|
||||
try {
|
||||
localStorage.setItem( 'skin-citizen-dark', dark );
|
||||
} catch ( e ) {}
|
||||
};
|
||||
if ( userTheme !== null ) {
|
||||
theme = userTheme;
|
||||
}
|
||||
|
||||
if ( theme !== 'auto' ) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
useDarkTheme = localStorage.getItem( 'skin-citizen-dark' );
|
||||
if ( window.mw.cookie.get( 'skin-citizen-theme-override' ) === '1' ) {
|
||||
return;
|
||||
}
|
||||
} catch ( e ) {}
|
||||
|
||||
prefersColorSchemeDarkQuery = window.matchMedia( '(prefers-color-scheme: dark)' );
|
||||
if ( prefersColorSchemeDarkQuery.matches ) {
|
||||
theme = 'dark';
|
||||
}
|
||||
|
||||
if ( useDarkTheme || prefersColorSchemeDarkQuery.matches ) {
|
||||
switchColorScheme( true );
|
||||
prefersColorSchemeDarkQuery.addEventListener( 'change', function ( e ) {
|
||||
if ( e.matches ) {
|
||||
theme = 'dark';
|
||||
} else {
|
||||
theme = 'light';
|
||||
}
|
||||
} );
|
||||
|
||||
try {
|
||||
window.mw.cookie.set( 'skin-citizen-theme', null );
|
||||
window.mw.cookie.set( 'skin-citizen-theme', theme );
|
||||
} catch ( e ) {
|
||||
}
|
||||
}() );
|
||||
|
|
|
@ -40,3 +40,48 @@ function main() {
|
|||
}
|
||||
|
||||
main();
|
||||
|
||||
( function () {
|
||||
var theme = window.mw.cookie.get( 'skin-citizen-theme' );
|
||||
var toggleBtn = document.getElementById( 'theme-toggle' );
|
||||
|
||||
// * theme-toggle-light
|
||||
// * theme-toggle-dark
|
||||
toggleBtn.classList.add( 'theme-toggle-' + theme );
|
||||
|
||||
toggleBtn.addEventListener( 'click', function ( clickEvent ) {
|
||||
try {
|
||||
|
||||
theme = theme === 'dark' ? 'light' : 'dark';
|
||||
|
||||
clickEvent.target.classList.remove( 'theme-toggle-light', 'theme-toggle-dark' );
|
||||
// * theme-toggle-light
|
||||
// * theme-toggle-dark
|
||||
clickEvent.target.classList.add( 'theme-toggle-' + theme );
|
||||
|
||||
try {
|
||||
window.mw.cookie.set( 'skin-citizen-theme', null );
|
||||
window.mw.cookie.set( 'skin-citizen-theme', theme );
|
||||
window.mw.cookie.set( 'skin-citizen-theme-override', '1' );
|
||||
} catch ( e ) {
|
||||
}
|
||||
|
||||
[ 'auto', 'dark', 'light' ].map( function ( themeSuffix ) {
|
||||
// * skin-citizen-auto
|
||||
// * skin-citizen-dark
|
||||
// * skin-citizen-light
|
||||
return 'skin-citizen-' + themeSuffix;
|
||||
} ).forEach( function ( cssClass ) {
|
||||
// * skin-citizen-auto
|
||||
// * skin-citizen-dark
|
||||
// * skin-citizen-light
|
||||
document.documentElement.classList.remove( cssClass );
|
||||
} );
|
||||
// * skin-citizen-auto
|
||||
// * skin-citizen-dark
|
||||
// * skin-citizen-light
|
||||
document.documentElement.classList.add( 'skin-citizen-' + theme );
|
||||
} catch ( e ) {
|
||||
}
|
||||
} );
|
||||
}() );
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
.search-container {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#search {
|
||||
&form {
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
.mw-theme-toggle {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
font-size: 0;
|
||||
opacity: 0.4;
|
||||
width: 39px;
|
||||
height: 56px;
|
||||
}
|
||||
|
||||
.skin-citizen-dark {
|
||||
.mw-theme-toggle {
|
||||
filter: invert(1);
|
||||
}
|
||||
}
|
|
@ -23,6 +23,7 @@
|
|||
@import 'Catlinks.less';
|
||||
@import 'Pagelinks.less';
|
||||
@import 'Footer.less';
|
||||
@import 'ThemeToggle.less';
|
||||
}
|
||||
|
||||
@media print {
|
||||
|
|
正在加载...
在新工单中引用
屏蔽一个用户