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"?>
|
<?xml version="1.0"?>
|
||||||
<ruleset name="MediaWiki">
|
<ruleset>
|
||||||
<rule ref="./vendor/hydrawiki/hydrawiki-codesniffer/HydraWiki" />
|
<rule ref="./vendor/mediawiki/mediawiki-codesniffer/MediaWiki" />
|
||||||
<file>.</file>
|
<file>.</file>
|
||||||
<arg name="encoding" value="utf8"/>
|
|
||||||
<arg name="extensions" value="php"/>
|
<arg name="extensions" value="php"/>
|
||||||
|
<arg name="encoding" value="UTF-8"/>
|
||||||
|
<exclude-pattern>*/vendor/*</exclude-pattern>
|
||||||
|
<exclude-pattern>*/out/*</exclude-pattern>
|
||||||
</ruleset>
|
</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",
|
"name": "TabberNeue",
|
||||||
"version": "2.4.5",
|
"version": "0.0.1",
|
||||||
"author": [
|
"author": [
|
||||||
|
"alistair3149",
|
||||||
"Eric Fortin",
|
"Eric Fortin",
|
||||||
"Alexia E. Smith"
|
"Alexia E. Smith"
|
||||||
],
|
],
|
||||||
"url": "https://www.mediawiki.org/wiki/Extension:Tabber",
|
"url": "https://www.mediawiki.org/wiki/Extension:TabberNeue",
|
||||||
"descriptionmsg": "tabber-desc",
|
"descriptionmsg": "tabberneue-desc",
|
||||||
"type": "parserhook",
|
"type": "parserhook",
|
||||||
"license-name": "GPL-3.0-only",
|
"license-name": "GPL-3.0-or-later",
|
||||||
"requires": {
|
"requires": {
|
||||||
"MediaWiki": ">= 1.29.0"
|
"MediaWiki": ">= 1.35.0"
|
||||||
},
|
},
|
||||||
"MessagesDirs": {
|
"MessagesDirs": {
|
||||||
"Tabber": [
|
"TabberNeue": [
|
||||||
"/i18n"
|
"/i18n"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"AutoloadClasses": {
|
"AutoloadClasses": {
|
||||||
"Tabber\\TabberHooks": "TabberHooks.php"
|
"TabberNeue\\TabberNeueHooks": "includes/TabberNeueHooks.php"
|
||||||
},
|
},
|
||||||
"ResourceModules": {
|
"ResourceModules": {
|
||||||
"ext.Tabber": {
|
"ext.tabberNeue": {
|
||||||
"styles": [
|
"styles": [
|
||||||
"css/tabber.css"
|
"ext.tabberNeue.less"
|
||||||
],
|
],
|
||||||
"scripts": [
|
"scripts": [
|
||||||
"js/tabber.js"
|
"ext.tabberNeue.js"
|
||||||
],
|
],
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
"mediawiki.Uri",
|
"mediawiki.Uri",
|
||||||
|
@ -36,15 +37,33 @@
|
||||||
"desktop",
|
"desktop",
|
||||||
"mobile"
|
"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": {
|
"ResourceFileModulePaths": {
|
||||||
"localBasePath": "",
|
"localBasePath": "modules",
|
||||||
"remoteExtPath": "Tabber"
|
"remoteExtPath": "TabberNeue/modules"
|
||||||
},
|
},
|
||||||
"Hooks": {
|
"Hooks": {
|
||||||
"ParserFirstCallInit": [
|
"ParserFirstCallInit": [
|
||||||
"Tabber\\TabberHooks::onParserFirstCallInit"
|
"TabberNeue\\TabberNeueHooks::onParserFirstCallInit"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"manifest_version": 1
|
"manifest_version": 1
|
||||||
|
|
|
@ -4,5 +4,5 @@
|
||||||
"Xuacu"
|
"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"
|
"Wizardist"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"tabber-desc": "Дазваляе ствараць закладкі на старонцы"
|
"tabberneue-desc": "Дазваляе ствараць закладкі на старонцы"
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,5 +4,5 @@
|
||||||
"DCLXVI"
|
"DCLXVI"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"tabber-desc": "Позволява създаването на табове в страниците"
|
"tabberneue-desc": "Позволява създаването на табове в страниците"
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,5 +4,5 @@
|
||||||
"Y-M D"
|
"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"
|
"BroOk"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"tabber-desc": "Permet crear tabs dins una pàgina"
|
"tabberneue-desc": "Permet crear tabs dins una pàgina"
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,5 +4,5 @@
|
||||||
"Kghbln"
|
"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"
|
"Michawiki"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"tabber-desc": "Zmóžnja rejtarki w boku napóraś"
|
"tabberneue-desc": "Zmóžnja rejtarki w boku napóraś"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
{
|
{
|
||||||
"@metadata": {
|
"@metadata": {
|
||||||
"authors": [
|
"authors": [
|
||||||
|
"alistair3149",
|
||||||
"Eric Fortin"
|
"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"
|
"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"
|
"Armin1392"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"tabber-desc": "اجازه برای ایجاد تبها درون یک صفحه"
|
"tabberneue-desc": "اجازه برای ایجاد تبها درون یک صفحه"
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,5 +5,5 @@
|
||||||
"Nike"
|
"Nike"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"tabber-desc": "Mahdollistaa välilehtien luonnin sivulla"
|
"tabberneue-desc": "Mahdollistaa välilehtien luonnin sivulla"
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,5 +4,5 @@
|
||||||
"Wyz"
|
"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"
|
"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"
|
"Amire80"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"tabber-desc": "אפשרות ליצור לשוניות בדף"
|
"tabberneue-desc": "אפשרות ליצור לשוניות בדף"
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,5 +4,5 @@
|
||||||
"Michawiki"
|
"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"
|
"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"
|
"Shirayuki"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"tabber-desc": "ページ内にタブを作成できるようにする"
|
"tabberneue-desc": "ページ内にタブを作成できるようにする"
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,5 +4,5 @@
|
||||||
"아라"
|
"아라"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"tabber-desc": "문서 내에서 탭 만들기 허용"
|
"tabberneue-desc": "문서 내에서 탭 만들기 허용"
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,5 +4,5 @@
|
||||||
"Purodha"
|
"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"
|
"Bjankuloski06"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"tabber-desc": "Овозможува создавање на јазичиња во рамките на една страница"
|
"tabberneue-desc": "Овозможува создавање на јазичиња во рамките на една страница"
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,5 +4,5 @@
|
||||||
"C.R."
|
"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"
|
"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"
|
"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"
|
"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"
|
"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"
|
"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"
|
"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"
|
"Lockal"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"tabber-desc": "Позволяет создавать вкладки внутри страницы"
|
"tabberneue-desc": "Позволяет создавать вкладки внутри страницы"
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,5 +4,5 @@
|
||||||
"පසිඳු කාවින්ද"
|
"පසිඳු කාවින්ද"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"tabber-desc": "පිටු අතර ටැබයන් තැනීමට ඉඩ දෙයි"
|
"tabberneue-desc": "පිටු අතර ටැබයන් තැනීමට ඉඩ දෙයි"
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,5 +4,5 @@
|
||||||
"WikiPhoenix"
|
"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"
|
"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"
|
"Yfdyh000"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"tabber-desc": "允许在页面内创建标签页"
|
"tabberneue-desc": "允许在页面内创建标签页"
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,5 +4,5 @@
|
||||||
"Justincheng12345"
|
"Justincheng12345"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"tabber-desc": "容許於頁面內創建分頁"
|
"tabberneue-desc": "容許於頁面內創建分頁"
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,12 +9,12 @@
|
||||||
* @link https://www.mediawiki.org/wiki/Extension:Tabber
|
* @link https://www.mediawiki.org/wiki/Extension:Tabber
|
||||||
**/
|
**/
|
||||||
|
|
||||||
namespace Tabber;
|
namespace TabberNeue;
|
||||||
|
|
||||||
use Parser;
|
use Parser;
|
||||||
use PPFrame;
|
use PPFrame;
|
||||||
|
|
||||||
class TabberHooks {
|
class TabberNeueHooks {
|
||||||
/**
|
/**
|
||||||
* Sets up this extension's parser functions.
|
* Sets up this extension's parser functions.
|
||||||
*
|
*
|
||||||
|
@ -23,8 +23,7 @@ class TabberHooks {
|
||||||
* @return boolean true
|
* @return boolean true
|
||||||
*/
|
*/
|
||||||
public static function onParserFirstCallInit(Parser &$parser) {
|
public static function onParserFirstCallInit(Parser &$parser) {
|
||||||
$parser->setHook("tabber", "Tabber\\TabberHooks::renderTabber");
|
$parser->setHook('tabber', 'TabberNeue\\TabberNeueHooks::renderTabber');
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,18 +38,19 @@ class TabberHooks {
|
||||||
* @return string HTML
|
* @return string HTML
|
||||||
*/
|
*/
|
||||||
public static function renderTabber($input, array $args, Parser $parser, PPFrame $frame) {
|
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);
|
$arr = explode("|-|", $input);
|
||||||
$htmlTabs = '';
|
$htmlTabs = '';
|
||||||
foreach ($arr as $tab) {
|
foreach ($arr as $tab) {
|
||||||
$htmlTabs .= self::buildTab($tab, $parser, $frame);
|
$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);
|
$tabBody = $parser->recursiveTagParse($tabBody, $frame);
|
||||||
|
|
||||||
$tab = '
|
$tab = '<article class="tabber__panel" title="' . htmlspecialchars($tabName) .
|
||||||
<div class="tabbertab" title="' . htmlspecialchars($tabName) . '">
|
'"><p>' . $tabBody . '</p></article>';
|
||||||
<p>' . $tabBody . '</p>
|
|
||||||
</div>';
|
|
||||||
|
|
||||||
return $tab;
|
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