mirror of
https://github.com/StarCitizenTools/mediawiki-extensions-TabberNeue.git
synced 2024-11-23 16:06:45 +00:00
feat: initial refactor into TabberNeue
This commit is contained in:
parent
6c67baf4d1
commit
eb9564509b
|
@ -1,6 +0,0 @@
|
|||
[gerrit]
|
||||
host=gerrit.wikimedia.org
|
||||
port=29418
|
||||
project=mediawiki/extensions/Tabber
|
||||
defaultbranch=master
|
||||
defaultrebase=0
|
|
@ -1,7 +1,9 @@
|
|||
<?xml version="1.0"?>
|
||||
<ruleset name="MediaWiki">
|
||||
<rule ref="./vendor/hydrawiki/hydrawiki-codesniffer/HydraWiki" />
|
||||
<ruleset>
|
||||
<rule ref="./vendor/mediawiki/mediawiki-codesniffer/MediaWiki" />
|
||||
<file>.</file>
|
||||
<arg name="encoding" value="utf8"/>
|
||||
<arg name="extensions" value="php"/>
|
||||
<arg name="encoding" value="UTF-8"/>
|
||||
<exclude-pattern>*/vendor/*</exclude-pattern>
|
||||
<exclude-pattern>*/out/*</exclude-pattern>
|
||||
</ruleset>
|
52
README.md
Normal file
52
README.md
Normal file
|
@ -0,0 +1,52 @@
|
|||
# TabberNeue
|
||||
|
||||
The TabberNeue extension allows wikis to create tabs within a page. It is a rewritten and forked version of [Extension:Tabber](https://www.mediawiki.org/wiki/Extension:Tabber). It includes multiple improvements such as responsive layout support, ARIA support, and conform to Wikimedia UI.
|
||||
|
||||
[Extension:TabberNeue on MediaWiki](https://www.mediawiki.org/wiki/Extension:TabberNeue).
|
||||
|
||||
## Requirements
|
||||
* [MediaWiki](https://www.mediawiki.org) 1.35 or later
|
||||
|
||||
## Installation
|
||||
You can get the extension via Git (specifying TabberNeue as the destination directory):
|
||||
|
||||
git clone https://github.com/StarCitizenTools/mediawiki-extensions-TabberNeue.git TabberNeue
|
||||
|
||||
Or [download it as zip archive](https://github.com/StarCitizenTools/mediawiki-extensions-TabberNeue/archive/main.zip).
|
||||
|
||||
In either case, the "TabberNeue" extension should end up in the "extensions" directory
|
||||
of your MediaWiki installation. If you got the zip archive, you will need to put it
|
||||
into a directory called TabberNeue.
|
||||
|
||||
## Usage
|
||||
TabberNeue uses the exact same syntax as Tabber.
|
||||
Tabs are created with `tabName=tabBody`, and separated by `|-|`.
|
||||
```html
|
||||
<tabber>
|
||||
tab1=Some neat text here
|
||||
|-|
|
||||
tab2=
|
||||
[http://www.google.com Google]<br/>
|
||||
[http://www.cnn.com Cnn]<br/>
|
||||
|-|
|
||||
tab3={{Template:SomeTemplate}}
|
||||
</tabber>
|
||||
```
|
||||
|
||||
### Parser functions and conditionals
|
||||
```html
|
||||
<tabber>
|
||||
Tab1 = {{{1|}}}
|
||||
|-|
|
||||
Tab2 = {{{2|}}}
|
||||
</tabber>
|
||||
```
|
||||
Becomes:
|
||||
```
|
||||
{{#tag:tabber|
|
||||
Tab1={{{1|}}}
|
||||
{{!}}-{{!}}
|
||||
Tab2={{{2|}}}
|
||||
}}
|
||||
```
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
ul.tabbernav {
|
||||
margin: 0;
|
||||
padding: 3px 0;
|
||||
border-bottom: 1px solid #CCC;
|
||||
font: bold 12px Verdana, sans-serif;
|
||||
}
|
||||
|
||||
ul.tabbernav li {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
display: inline-block;
|
||||
padding-top: 1em;
|
||||
}
|
||||
|
||||
ul.tabbernav li a {
|
||||
padding: 3px .5em;
|
||||
margin-left: 3px;
|
||||
border: 1px solid #CCC;
|
||||
border-bottom: none;
|
||||
background: #F2F7FF;
|
||||
text-decoration: none;
|
||||
white-space: pre;
|
||||
}
|
||||
|
||||
ul.tabbernav li a:link {
|
||||
color: #448;
|
||||
}
|
||||
|
||||
ul.tabbernav li a:visited {
|
||||
color: #667;
|
||||
}
|
||||
|
||||
ul.tabbernav li a:hover {
|
||||
color: #000;
|
||||
background: #FFF9F2;
|
||||
border-color: #CCC;
|
||||
}
|
||||
|
||||
ul.tabbernav li.tabberactive a {
|
||||
background-color: #FFF;
|
||||
border-bottom: 1px solid #FFF;
|
||||
}
|
||||
|
||||
ul.tabbernav li.tabberactive a:hover {
|
||||
color: #000;
|
||||
background: #FFF;
|
||||
border-bottom: 1px solid #FFF;
|
||||
}
|
||||
|
||||
.tabber .tabbertab {
|
||||
padding: 5px;
|
||||
border: 1px solid #CCC;
|
||||
border-top: 0;
|
||||
}
|
|
@ -1,32 +1,33 @@
|
|||
{
|
||||
"name": "Tabber",
|
||||
"version": "2.4.5",
|
||||
"name": "TabberNeue",
|
||||
"version": "0.0.1",
|
||||
"author": [
|
||||
"alistair3149",
|
||||
"Eric Fortin",
|
||||
"Alexia E. Smith"
|
||||
],
|
||||
"url": "https://www.mediawiki.org/wiki/Extension:Tabber",
|
||||
"descriptionmsg": "tabber-desc",
|
||||
"url": "https://www.mediawiki.org/wiki/Extension:TabberNeue",
|
||||
"descriptionmsg": "tabberneue-desc",
|
||||
"type": "parserhook",
|
||||
"license-name": "GPL-3.0-only",
|
||||
"license-name": "GPL-3.0-or-later",
|
||||
"requires": {
|
||||
"MediaWiki": ">= 1.29.0"
|
||||
"MediaWiki": ">= 1.35.0"
|
||||
},
|
||||
"MessagesDirs": {
|
||||
"Tabber": [
|
||||
"TabberNeue": [
|
||||
"/i18n"
|
||||
]
|
||||
},
|
||||
"AutoloadClasses": {
|
||||
"Tabber\\TabberHooks": "TabberHooks.php"
|
||||
"TabberNeue\\TabberNeueHooks": "includes/TabberNeueHooks.php"
|
||||
},
|
||||
"ResourceModules": {
|
||||
"ext.Tabber": {
|
||||
"ext.tabberNeue": {
|
||||
"styles": [
|
||||
"css/tabber.css"
|
||||
"ext.tabberNeue.less"
|
||||
],
|
||||
"scripts": [
|
||||
"js/tabber.js"
|
||||
"ext.tabberNeue.js"
|
||||
],
|
||||
"dependencies": [
|
||||
"mediawiki.Uri",
|
||||
|
@ -36,15 +37,33 @@
|
|||
"desktop",
|
||||
"mobile"
|
||||
]
|
||||
},
|
||||
"ext.tabberNeue.icons": {
|
||||
"class": "ResourceLoaderImageModule",
|
||||
"selector": ".tabber__header__{name}:after",
|
||||
"images": {
|
||||
"next": {
|
||||
"file": {
|
||||
"ltr": "ext.tabberNeue.icons/next-ltr.svg",
|
||||
"rtl": "ext.tabberNeue.icons/next-rtl.svg"
|
||||
}
|
||||
},
|
||||
"prev": {
|
||||
"file": {
|
||||
"ltr": "ext.tabberNeue.icons/previous-ltr.svg",
|
||||
"rtl": "ext.tabberNeue.icons/previous-rtl.svg"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"ResourceFileModulePaths": {
|
||||
"localBasePath": "",
|
||||
"remoteExtPath": "Tabber"
|
||||
"localBasePath": "modules",
|
||||
"remoteExtPath": "TabberNeue/modules"
|
||||
},
|
||||
"Hooks": {
|
||||
"ParserFirstCallInit": [
|
||||
"Tabber\\TabberHooks::onParserFirstCallInit"
|
||||
"TabberNeue\\TabberNeueHooks::onParserFirstCallInit"
|
||||
]
|
||||
},
|
||||
"manifest_version": 1
|
||||
|
|
|
@ -4,5 +4,5 @@
|
|||
"Xuacu"
|
||||
]
|
||||
},
|
||||
"tabber-desc": "Permite crear llingüetes dientro d'una páxina"
|
||||
"tabberneue-desc": "Permite crear llingüetes dientro d'una páxina"
|
||||
}
|
||||
|
|
|
@ -4,5 +4,5 @@
|
|||
"Wizardist"
|
||||
]
|
||||
},
|
||||
"tabber-desc": "Дазваляе ствараць закладкі на старонцы"
|
||||
"tabberneue-desc": "Дазваляе ствараць закладкі на старонцы"
|
||||
}
|
||||
|
|
|
@ -4,5 +4,5 @@
|
|||
"DCLXVI"
|
||||
]
|
||||
},
|
||||
"tabber-desc": "Позволява създаването на табове в страниците"
|
||||
"tabberneue-desc": "Позволява създаването на табове в страниците"
|
||||
}
|
||||
|
|
|
@ -4,5 +4,5 @@
|
|||
"Y-M D"
|
||||
]
|
||||
},
|
||||
"tabber-desc": "Aotreañ a ra krouiñ ivinelloù war ur bajenn"
|
||||
"tabberneue-desc": "Aotreañ a ra krouiñ ivinelloù war ur bajenn"
|
||||
}
|
||||
|
|
|
@ -4,5 +4,5 @@
|
|||
"BroOk"
|
||||
]
|
||||
},
|
||||
"tabber-desc": "Permet crear tabs dins una pàgina"
|
||||
"tabberneue-desc": "Permet crear tabs dins una pàgina"
|
||||
}
|
||||
|
|
|
@ -4,5 +4,5 @@
|
|||
"Kghbln"
|
||||
]
|
||||
},
|
||||
"tabber-desc": "Ermöglicht das Erstellen von Reitern innerhalb einer Seite"
|
||||
"tabberneue-desc": "Ermöglicht das Erstellen von Reitern innerhalb einer Seite"
|
||||
}
|
||||
|
|
|
@ -4,5 +4,5 @@
|
|||
"Michawiki"
|
||||
]
|
||||
},
|
||||
"tabber-desc": "Zmóžnja rejtarki w boku napóraś"
|
||||
"tabberneue-desc": "Zmóžnja rejtarki w boku napóraś"
|
||||
}
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
{
|
||||
"@metadata": {
|
||||
"authors": [
|
||||
"alistair3149",
|
||||
"Eric Fortin"
|
||||
]
|
||||
},
|
||||
"tabber-desc": "Allows to create tabs within a page"
|
||||
"tabberneue-desc": "Allows to create tabs within a page. Forked from [https://www.mediawiki.org/wiki/Extension:Tabber Extension:Tabber]."
|
||||
}
|
||||
|
|
|
@ -4,5 +4,5 @@
|
|||
"Armando-Martin"
|
||||
]
|
||||
},
|
||||
"tabber-desc": "Permite para fichas dentro de una página"
|
||||
"tabberneue-desc": "Permite para fichas dentro de una página"
|
||||
}
|
||||
|
|
|
@ -4,5 +4,5 @@
|
|||
"Armin1392"
|
||||
]
|
||||
},
|
||||
"tabber-desc": "اجازه برای ایجاد تبها درون یک صفحه"
|
||||
"tabberneue-desc": "اجازه برای ایجاد تبها درون یک صفحه"
|
||||
}
|
||||
|
|
|
@ -5,5 +5,5 @@
|
|||
"Nike"
|
||||
]
|
||||
},
|
||||
"tabber-desc": "Mahdollistaa välilehtien luonnin sivulla"
|
||||
"tabberneue-desc": "Mahdollistaa välilehtien luonnin sivulla"
|
||||
}
|
||||
|
|
|
@ -4,5 +4,5 @@
|
|||
"Wyz"
|
||||
]
|
||||
},
|
||||
"tabber-desc": "Permet de créer des onglets sur une page"
|
||||
"tabberneue-desc": "Permet de créer des onglets sur une page"
|
||||
}
|
||||
|
|
|
@ -4,5 +4,5 @@
|
|||
"Toliño"
|
||||
]
|
||||
},
|
||||
"tabber-desc": "Permite a creación de lapelas dentro dunha páxina"
|
||||
"tabberneue-desc": "Permite a creación de lapelas dentro dunha páxina"
|
||||
}
|
||||
|
|
|
@ -4,5 +4,5 @@
|
|||
"Amire80"
|
||||
]
|
||||
},
|
||||
"tabber-desc": "אפשרות ליצור לשוניות בדף"
|
||||
"tabberneue-desc": "אפשרות ליצור לשוניות בדף"
|
||||
}
|
||||
|
|
|
@ -4,5 +4,5 @@
|
|||
"Michawiki"
|
||||
]
|
||||
},
|
||||
"tabber-desc": "Zmóžnja rajtarki znutřka strony wutworić"
|
||||
"tabberneue-desc": "Zmóžnja rajtarki znutřka strony wutworić"
|
||||
}
|
||||
|
|
|
@ -4,5 +4,5 @@
|
|||
"Darth Kule"
|
||||
]
|
||||
},
|
||||
"tabber-desc": "Permette di creare schede all'interno di una pagina"
|
||||
"tabberneue-desc": "Permette di creare schede all'interno di una pagina"
|
||||
}
|
||||
|
|
|
@ -4,5 +4,5 @@
|
|||
"Shirayuki"
|
||||
]
|
||||
},
|
||||
"tabber-desc": "ページ内にタブを作成できるようにする"
|
||||
"tabberneue-desc": "ページ内にタブを作成できるようにする"
|
||||
}
|
||||
|
|
|
@ -4,5 +4,5 @@
|
|||
"아라"
|
||||
]
|
||||
},
|
||||
"tabber-desc": "문서 내에서 탭 만들기 허용"
|
||||
"tabberneue-desc": "문서 내에서 탭 만들기 허용"
|
||||
}
|
||||
|
|
|
@ -4,5 +4,5 @@
|
|||
"Purodha"
|
||||
]
|
||||
},
|
||||
"tabber-desc": "Määd et müjjelesch, en Sigge Tabs erin ze maache."
|
||||
"tabberneue-desc": "Määd et müjjelesch, en Sigge Tabs erin ze maache."
|
||||
}
|
||||
|
|
|
@ -4,5 +4,5 @@
|
|||
"Bjankuloski06"
|
||||
]
|
||||
},
|
||||
"tabber-desc": "Овозможува создавање на јазичиња во рамките на една страница"
|
||||
"tabberneue-desc": "Овозможува создавање на јазичиња во рамките на една страница"
|
||||
}
|
||||
|
|
|
@ -4,5 +4,5 @@
|
|||
"C.R."
|
||||
]
|
||||
},
|
||||
"tabber-desc": "Permette 'e crià schede dint'a na paggena"
|
||||
"tabberneue-desc": "Permette 'e crià schede dint'a na paggena"
|
||||
}
|
||||
|
|
|
@ -4,5 +4,5 @@
|
|||
"SPQRobin"
|
||||
]
|
||||
},
|
||||
"tabber-desc": "Maakt het mogelijk om tabbladen binnen een pagina te maken"
|
||||
"tabberneue-desc": "Maakt het mogelijk om tabbladen binnen een pagina te maken"
|
||||
}
|
||||
|
|
|
@ -4,5 +4,5 @@
|
|||
"BeginaFelicysym"
|
||||
]
|
||||
},
|
||||
"tabber-desc": "Pozwala na tworzenie zakładek na stronie"
|
||||
"tabberneue-desc": "Pozwala na tworzenie zakładek na stronie"
|
||||
}
|
||||
|
|
|
@ -5,5 +5,5 @@
|
|||
"Dragonòt"
|
||||
]
|
||||
},
|
||||
"tabber-desc": "A përmët ëd creé dle schede drinta a na pàgina"
|
||||
"tabberneue-desc": "A përmët ëd creé dle schede drinta a na pàgina"
|
||||
}
|
||||
|
|
|
@ -4,5 +4,5 @@
|
|||
"Cainamarques"
|
||||
]
|
||||
},
|
||||
"tabber-desc": "Permite a criação de abas dentro de uma página"
|
||||
"tabberneue-desc": "Permite a criação de abas dentro de uma página"
|
||||
}
|
||||
|
|
|
@ -4,5 +4,5 @@
|
|||
"Shirayuki"
|
||||
]
|
||||
},
|
||||
"tabber-desc": "{{desc|name=Tabber|url=http://www.mediawiki.org/wiki/Extension:Tabber}}"
|
||||
"tabberneue-desc": "{{desc|name=TabberNeue|url=http://www.mediawiki.org/wiki/Extension:TabberNeue}}"
|
||||
}
|
||||
|
|
|
@ -4,5 +4,5 @@
|
|||
"Joetaras"
|
||||
]
|
||||
},
|
||||
"tabber-desc": "Permette de ccrejà le schede jndr'à 'na pàgene"
|
||||
"tabberneue-desc": "Permette de ccrejà le schede jndr'à 'na pàgene"
|
||||
}
|
||||
|
|
|
@ -4,5 +4,5 @@
|
|||
"Lockal"
|
||||
]
|
||||
},
|
||||
"tabber-desc": "Позволяет создавать вкладки внутри страницы"
|
||||
"tabberneue-desc": "Позволяет создавать вкладки внутри страницы"
|
||||
}
|
||||
|
|
|
@ -4,5 +4,5 @@
|
|||
"පසිඳු කාවින්ද"
|
||||
]
|
||||
},
|
||||
"tabber-desc": "පිටු අතර ටැබයන් තැනීමට ඉඩ දෙයි"
|
||||
"tabberneue-desc": "පිටු අතර ටැබයන් තැනීමට ඉඩ දෙයි"
|
||||
}
|
||||
|
|
|
@ -4,5 +4,5 @@
|
|||
"WikiPhoenix"
|
||||
]
|
||||
},
|
||||
"tabber-desc": "Gör det möjligt att skapa flikar på en sida"
|
||||
"tabberneue-desc": "Gör det möjligt att skapa flikar på en sida"
|
||||
}
|
||||
|
|
|
@ -4,5 +4,5 @@
|
|||
"AnakngAraw"
|
||||
]
|
||||
},
|
||||
"tabber-desc": "Nagpapahintulot upang makalikha ng mga laylay sa loob ng isang pahina"
|
||||
"tabberneue-desc": "Nagpapahintulot upang makalikha ng mga laylay sa loob ng isang pahina"
|
||||
}
|
||||
|
|
|
@ -4,5 +4,5 @@
|
|||
"Ата"
|
||||
]
|
||||
},
|
||||
"tabber-desc": "Дозволяє створювати вкладки всередині сторінки"
|
||||
"tabberneue-desc": "Дозволяє створювати вкладки всередині сторінки"
|
||||
}
|
||||
|
|
|
@ -5,5 +5,5 @@
|
|||
"Yfdyh000"
|
||||
]
|
||||
},
|
||||
"tabber-desc": "允许在页面内创建标签页"
|
||||
"tabberneue-desc": "允许在页面内创建标签页"
|
||||
}
|
||||
|
|
|
@ -4,5 +4,5 @@
|
|||
"Justincheng12345"
|
||||
]
|
||||
},
|
||||
"tabber-desc": "容許於頁面內創建分頁"
|
||||
"tabberneue-desc": "容許於頁面內創建分頁"
|
||||
}
|
||||
|
|
|
@ -9,12 +9,12 @@
|
|||
* @link https://www.mediawiki.org/wiki/Extension:Tabber
|
||||
**/
|
||||
|
||||
namespace Tabber;
|
||||
namespace TabberNeue;
|
||||
|
||||
use Parser;
|
||||
use PPFrame;
|
||||
|
||||
class TabberHooks {
|
||||
class TabberNeueHooks {
|
||||
/**
|
||||
* Sets up this extension's parser functions.
|
||||
*
|
||||
|
@ -23,8 +23,7 @@ class TabberHooks {
|
|||
* @return boolean true
|
||||
*/
|
||||
public static function onParserFirstCallInit(Parser &$parser) {
|
||||
$parser->setHook("tabber", "Tabber\\TabberHooks::renderTabber");
|
||||
|
||||
$parser->setHook('tabber', 'TabberNeue\\TabberNeueHooks::renderTabber');
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -39,18 +38,19 @@ class TabberHooks {
|
|||
* @return string HTML
|
||||
*/
|
||||
public static function renderTabber($input, array $args, Parser $parser, PPFrame $frame) {
|
||||
$parser->getOutput()->addModules('ext.Tabber');
|
||||
$parser->getOutput()->addModules('ext.tabberNeue');
|
||||
|
||||
$key = md5($input);
|
||||
$key = substr(md5($input), 0, 6);
|
||||
$arr = explode("|-|", $input);
|
||||
$htmlTabs = '';
|
||||
foreach ($arr as $tab) {
|
||||
$htmlTabs .= self::buildTab($tab, $parser, $frame);
|
||||
}
|
||||
|
||||
$HTML = '<div id="tabber-' . $key . '" class="tabber">' . $htmlTabs . "</div>";
|
||||
$html = '<div id="tabber-' . $key . '" class="tabber">' .
|
||||
'<section class="tabber__section">' . $htmlTabs . "</section></div>";
|
||||
|
||||
return $HTML;
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -73,10 +73,9 @@ class TabberHooks {
|
|||
|
||||
$tabBody = $parser->recursiveTagParse($tabBody, $frame);
|
||||
|
||||
$tab = '
|
||||
<div class="tabbertab" title="' . htmlspecialchars($tabName) . '">
|
||||
<p>' . $tabBody . '</p>
|
||||
</div>';
|
||||
$tab = '<article class="tabber__panel" title="' . htmlspecialchars($tabName) .
|
||||
'"><p>' . $tabBody . '</p></article>';
|
||||
|
||||
|
||||
return $tab;
|
||||
}
|
75
js/tabber.js
75
js/tabber.js
|
@ -1,75 +0,0 @@
|
|||
(function($) {
|
||||
$.fn.tabber = function() {
|
||||
return this.each(function() {
|
||||
// create tabs
|
||||
var $this = $(this),
|
||||
tabContent = $this.children('.tabbertab'),
|
||||
nav = $('<ul>').addClass('tabbernav'),
|
||||
loc;
|
||||
|
||||
tabContent.each(function() {
|
||||
$(this).attr('data-hash', mw.util.escapeIdForAttribute(this.title));
|
||||
var anchor = $('<a>').text(this.title).attr('title',this.title).attr('data-hash', $(this).attr('data-hash')).attr('href', '#');
|
||||
$('<li>').append(anchor).appendTo(nav);
|
||||
|
||||
// Append a manual word break point after each tab
|
||||
nav.append($('<wbr>'));
|
||||
});
|
||||
|
||||
$this.prepend(nav);
|
||||
|
||||
/**
|
||||
* Internal helper function for showing content
|
||||
* @param {string} title to show, matching only 1 tab
|
||||
* @return {bool} true if matching tab could be shown
|
||||
*/
|
||||
function showContent(title) {
|
||||
var content = tabContent.filter('[data-hash="' + title + '"]');
|
||||
if (content.length !== 1) { return false; }
|
||||
tabContent.hide();
|
||||
content.show();
|
||||
nav.find('.tabberactive').removeClass('tabberactive');
|
||||
nav.find('a[data-hash="' + title + '"]').parent().addClass('tabberactive');
|
||||
return true;
|
||||
}
|
||||
|
||||
// setup initial state
|
||||
var tab = new mw.Uri(location.href).fragment;
|
||||
if (tab === '' || !showContent(tab)) {
|
||||
showContent(tabContent.first().attr('data-hash'));
|
||||
}
|
||||
|
||||
// Respond to clicks on the nav tabs
|
||||
nav.on('click', 'a', function(e) {
|
||||
var title = $(this).attr('data-hash');
|
||||
e.preventDefault();
|
||||
if (history.pushState) {
|
||||
history.pushState(null, null, '#' + title);
|
||||
switchTab();
|
||||
} else {
|
||||
location.hash = '#' + title;
|
||||
}
|
||||
});
|
||||
|
||||
$(window).on('hashchange', function(event) {
|
||||
switchTab();
|
||||
});
|
||||
|
||||
function switchTab() {
|
||||
var tab = new mw.Uri(location.href).fragment;
|
||||
if (!tab.length) {
|
||||
showContent(tabContent.first().attr('data-hash'));
|
||||
}
|
||||
if (nav.find('a[data-hash="'+tab+'"]').length) {
|
||||
showContent(tab);
|
||||
}
|
||||
}
|
||||
|
||||
$this.addClass('tabberlive');
|
||||
});
|
||||
};
|
||||
}(jQuery));
|
||||
|
||||
$(document).ready(function() {
|
||||
$('.tabber').tabber();
|
||||
});
|
7
modules/ext.tabberNeue.icons/next-ltr.svg
Normal file
7
modules/ext.tabberNeue.icons/next-ltr.svg
Normal file
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
|
||||
<title>
|
||||
next
|
||||
</title>
|
||||
<path d="M7 1L5.6 2.5 13 10l-7.4 7.5L7 19l9-9z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 207 B |
7
modules/ext.tabberNeue.icons/next-rtl.svg
Normal file
7
modules/ext.tabberNeue.icons/next-rtl.svg
Normal file
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
|
||||
<title>
|
||||
next
|
||||
</title>
|
||||
<path d="M4 10l9 9 1.4-1.5L7 10l7.4-7.5L13 1z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 206 B |
7
modules/ext.tabberNeue.icons/previous-ltr.svg
Normal file
7
modules/ext.tabberNeue.icons/previous-ltr.svg
Normal file
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
|
||||
<title>
|
||||
previous
|
||||
</title>
|
||||
<path d="M4 10l9 9 1.4-1.5L7 10l7.4-7.5L13 1z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 210 B |
7
modules/ext.tabberNeue.icons/previous-rtl.svg
Normal file
7
modules/ext.tabberNeue.icons/previous-rtl.svg
Normal file
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
|
||||
<title>
|
||||
previous
|
||||
</title>
|
||||
<path d="M7 1L5.6 2.5 13 10l-7.4 7.5L7 19l9-9z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 211 B |
175
modules/ext.tabberNeue.js
Normal file
175
modules/ext.tabberNeue.js
Normal file
|
@ -0,0 +1,175 @@
|
|||
/**
|
||||
* This needs a full-rewrite dropping jQuery and using ES6.
|
||||
* But it will do for now.
|
||||
*/
|
||||
( function ( $ ) {
|
||||
$.fn.tabber = function () {
|
||||
// Load icons
|
||||
mw.loader.load( 'ext.tabberNeue.icons' );
|
||||
|
||||
return this.each( function () {
|
||||
// create tabs
|
||||
var $this = $( this ),
|
||||
key = $this.attr( 'id' ).substring( 7 ),
|
||||
tabSection = $this.children( '.tabber__section' ),
|
||||
tabPanel = tabSection.children( '.tabber__panel' ),
|
||||
nav = $( '<nav>' ).addClass( 'tabber__nav' ),
|
||||
header = $( '<header>' ).addClass( 'tabber__header' ),
|
||||
arrowLeft = $( '<div>' ).addClass( 'tabber__header__prev' ),
|
||||
arrowRight = $( '<div>' ).addClass( 'tabber__header__next' ),
|
||||
hash;
|
||||
|
||||
nav.attr( 'role', 'tablist' );
|
||||
|
||||
tabPanel.each( function () {
|
||||
hash = mw.util.escapeIdForAttribute( this.title ) + '-' + key;
|
||||
$( this ).attr( 'id', hash );
|
||||
$( this ).attr( 'role', 'tabpanel' );
|
||||
$( this ).attr( 'aria-labelledby', 'tab-' + hash );
|
||||
$( this ).attr( 'aria-hidden', 'true' );
|
||||
var anchor = $( '<a>' ).text( this.title ).attr( 'title', this.title );
|
||||
anchor.addClass( 'tabber__item' );
|
||||
anchor.attr( 'role', 'tab' );
|
||||
anchor.attr( 'href', '#' + hash );
|
||||
anchor.attr( 'id', 'tab-' + hash );
|
||||
anchor.attr( 'aria-controls', hash );
|
||||
anchor.appendTo( nav );
|
||||
} );
|
||||
|
||||
arrowLeft.appendTo( header );
|
||||
nav.appendTo( header );
|
||||
arrowRight.appendTo( header );
|
||||
|
||||
$this.prepend( header );
|
||||
|
||||
/**
|
||||
* Internal helper function for showing panel
|
||||
* @param {string} targetHash to show, matching only 1 tab
|
||||
* @return {bool} true if matching tab could be shown
|
||||
*/
|
||||
function showPanel( targetHash ) {
|
||||
const targetPanel = document.getElementById( targetHash ),
|
||||
section = targetPanel.parentElement,
|
||||
currentPanel = section.querySelector( '.tabber__panel--active' );
|
||||
|
||||
if ( currentPanel ) {
|
||||
// jQuery
|
||||
nav.find('.tabber__item--active').removeClass('tabber__item--active');
|
||||
currentPanel.classList.remove( 'tabber__panel--active' );
|
||||
currentPanel.setAttribute( 'aria-hidden', 'true' );
|
||||
section.style.height = currentPanel.offsetHeight + 'px';
|
||||
section.style.height = targetPanel.offsetHeight + 'px';
|
||||
} else {
|
||||
section.style.height = targetPanel.offsetHeight + 'px';
|
||||
}
|
||||
|
||||
// Add active class to the tab item
|
||||
nav.find( 'a[href="#' + targetHash + '"]' ).addClass( 'tabber__item--active' );
|
||||
targetPanel.classList.add( 'tabber__panel--active' );
|
||||
targetPanel.setAttribute( 'aria-hidden', 'false' );
|
||||
|
||||
// Scroll to tab
|
||||
section.scrollLeft = targetPanel.offsetLeft;
|
||||
}
|
||||
|
||||
function initButtons() {
|
||||
const header = tabber.querySelector( '.tabber__header' ),
|
||||
PREVCLASS = 'tabber__header--prev-visible',
|
||||
NEXTCLASS = 'tabber__header--next-visible';
|
||||
|
||||
const scrollTabs = ( offset ) => {
|
||||
const scrollLeft = tablist.scrollLeft + offset;
|
||||
|
||||
// Scroll to the start
|
||||
if ( scrollLeft <= 0 ) {
|
||||
tablist.scrollLeft = 0;
|
||||
header.classList.remove( PREVCLASS );
|
||||
header.classList.add( NEXTCLASS );
|
||||
} else {
|
||||
tablist.scrollLeft = scrollLeft;
|
||||
// Scroll to the end
|
||||
if ( scrollLeft + tablist.offsetWidth >= tablist.scrollWidth ) {
|
||||
header.classList.remove( NEXTCLASS );
|
||||
header.classList.add( PREVCLASS );
|
||||
} else {
|
||||
header.classList.add( NEXTCLASS );
|
||||
header.classList.add( PREVCLASS );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const setupButtons = () => {
|
||||
const isScrollable = ( tablist.scrollWidth > header.offsetWidth ) ? true : false;
|
||||
|
||||
if ( isScrollable ) {
|
||||
const prevButton = header.querySelector( '.tabber__header__prev' ),
|
||||
nextButton = header.querySelector( '.tabber__header__next' ),
|
||||
scrollOffset = header.offsetWidth / 2;
|
||||
|
||||
// Just to add the right classes
|
||||
scrollTabs( 0 );
|
||||
prevButton.addEventListener( "click", () => {
|
||||
scrollTabs( -scrollOffset );
|
||||
}, false );
|
||||
|
||||
nextButton.addEventListener( "click", () => {
|
||||
scrollTabs( scrollOffset );
|
||||
}, false );
|
||||
} else {
|
||||
header.classList.remove( NEXTCLASS );
|
||||
header.classList.remove( PREVCLASS );
|
||||
}
|
||||
}
|
||||
|
||||
setupButtons();
|
||||
|
||||
// Listen for window resize
|
||||
window.addEventListener( 'resize', () => {
|
||||
mw.util.debounce( 250, setupButtons() );
|
||||
} );
|
||||
}
|
||||
|
||||
function switchTab() {
|
||||
var targetHash = new mw.Uri( location.href ).fragment;
|
||||
|
||||
if ( targetHash ) {
|
||||
if ( nav.find( 'a[href="#' + targetHash + '"]' ).length ) {
|
||||
showPanel( targetHash );
|
||||
}
|
||||
} else {
|
||||
showPanel( tabPanel.first().attr( 'id' ) );
|
||||
}
|
||||
}
|
||||
|
||||
const tabber = document.getElementById( 'tabber-' + key ),
|
||||
tablist = tabber.querySelector( '.tabber__nav' );
|
||||
|
||||
switchTab();
|
||||
|
||||
// Only run if client is not a touch device
|
||||
if ( matchMedia( '(hover: hover)' ).matches ) {
|
||||
initButtons( tabber );
|
||||
}
|
||||
|
||||
$( window ).on( 'hashchange', function ( event ) {
|
||||
switchTab();
|
||||
} );
|
||||
|
||||
// Respond to clicks on the nav tabs
|
||||
nav.on( 'click', 'a', function ( e ) {
|
||||
var targetHash = $( this ).attr( 'href' ).substring( 1 );
|
||||
// Prevent vertical scroll while maintaining the anchor behavior
|
||||
e.preventDefault();
|
||||
// Add hash to the end of the URL
|
||||
history.pushState( null, null, '#' + targetHash );
|
||||
showPanel( targetHash );
|
||||
} );
|
||||
|
||||
$this.addClass( 'tabber--live' );
|
||||
} );
|
||||
};
|
||||
}( jQuery ) );
|
||||
|
||||
$( document ).ready( function () {
|
||||
$( '.tabber' ).tabber();
|
||||
} );
|
162
modules/ext.tabberNeue.less
Normal file
162
modules/ext.tabberNeue.less
Normal file
|
@ -0,0 +1,162 @@
|
|||
.tabber {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
/* establish primary containing box */
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
|
||||
&__header {
|
||||
/* defend against <section> needing 100% */
|
||||
flex-shrink: 0;
|
||||
/* fixes cross browser quarks */
|
||||
min-block-size: fit-content;
|
||||
display: flex;
|
||||
position: relative;
|
||||
|
||||
&__prev {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
&__next {
|
||||
right: 0;
|
||||
}
|
||||
|
||||
&__prev,
|
||||
&__next {
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
position: absolute;
|
||||
width: 20px;
|
||||
cursor: pointer;
|
||||
display: none;
|
||||
z-index: 1;
|
||||
|
||||
&:after {
|
||||
content: "";
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
position: absolute;
|
||||
width: inherit;
|
||||
background-size: 14px;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
}
|
||||
|
||||
&--prev-visible .tabber__nav {
|
||||
mask-image: linear-gradient( 90deg, transparent, #000 20% );
|
||||
-webkit-mask-image: linear-gradient( 90deg, transparent, #000 20% );
|
||||
}
|
||||
|
||||
&--next-visible .tabber__nav {
|
||||
mask-image: linear-gradient( 90deg, #000 80%, transparent );
|
||||
-webkit-mask-image: linear-gradient( 90deg, #000 80%, transparent );
|
||||
}
|
||||
|
||||
&--prev-visible.tabber__header--next-visible .tabber__nav {
|
||||
mask-image: linear-gradient( 90deg, transparent, #000 20%, #000 80%, transparent );
|
||||
-webkit-mask-image: linear-gradient( 90deg, transparent, #000 20%, #000 80%, transparent );
|
||||
}
|
||||
|
||||
&--prev-visible .tabber__header__prev,
|
||||
&--next-visible .tabber__header__next {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
&__header,
|
||||
&__section {
|
||||
/* prevent scroll chaining on x scroll */
|
||||
overscroll-behavior-x: contain;
|
||||
/* scrolling should snap children on x */
|
||||
scroll-snap-type: x mandatory;
|
||||
}
|
||||
|
||||
&__header,
|
||||
&__section,
|
||||
&__nav {
|
||||
scrollbar-width: none;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&__nav {
|
||||
display: flex;
|
||||
overflow: auto hidden;
|
||||
box-shadow: inset 0 -1px 0 0 #a2a9b1;
|
||||
}
|
||||
|
||||
&__item {
|
||||
scroll-snap-align: start;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
white-space: nowrap;
|
||||
padding: 5px 12px;
|
||||
color: #54595d;
|
||||
text-decoration: none;
|
||||
font-weight: bold;
|
||||
|
||||
&:visited {
|
||||
color: #54595d;
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&:active,
|
||||
&:focus {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
&--active,
|
||||
&--active:visited {
|
||||
color: #36c;
|
||||
box-shadow: inset 0 -2px 0 0 #36c;
|
||||
}
|
||||
}
|
||||
|
||||
&__section {
|
||||
overflow: hidden;
|
||||
block-size: 100%;
|
||||
display: grid;
|
||||
grid-auto-flow: column;
|
||||
grid-auto-columns: 100%;
|
||||
}
|
||||
|
||||
&__panel {
|
||||
/* be pushy about consuming all space */
|
||||
block-size: 100%;
|
||||
scroll-snap-align: start;
|
||||
overflow-y: auto;
|
||||
overscroll-behavior-y: contain;
|
||||
height: max-content;
|
||||
}
|
||||
}
|
||||
|
||||
@media (hover: hover) {
|
||||
.tabber {
|
||||
&__item {
|
||||
&:hover {
|
||||
color: #447ff5;
|
||||
box-shadow: inset 0 -2px 0 0 #447ff5;
|
||||
}
|
||||
|
||||
&:active {
|
||||
color: #2a4b8d;
|
||||
box-shadow: inset 0 -2px 0 0 #2a4b8d;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
.tabber {
|
||||
&__header,
|
||||
&__section,
|
||||
&__nav {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue