mirror of
https://github.com/StarCitizenTools/mediawiki-extensions-TabberNeue.git
synced 2024-11-23 16:06:45 +00:00
feat: add experimental support of Codex
It can be enabled by setting `$wgTabberNeueEnableLegacyMode` to `true`. It is the first implementation and a WIP with numerous caveats: - Nested Tabber does not work - Tabber Transclue does not work
This commit is contained in:
parent
75ce280665
commit
89e90af034
|
@ -27,11 +27,25 @@
|
|||
"tabberneue-tabbertransclude-category"
|
||||
],
|
||||
"ResourceModules": {
|
||||
"ext.tabberNeue": {
|
||||
"ext.tabberNeue.codex": {
|
||||
"packageFiles": [
|
||||
"ext.tabberNeue.js",
|
||||
"ext.tabberNeue.codex/ext.tabberNeue.codex.js",
|
||||
"ext.tabberNeue.codex/App.vue"
|
||||
],
|
||||
"dependencies": [
|
||||
"@wikimedia/codex",
|
||||
"mediawiki.util"
|
||||
],
|
||||
"targets": [
|
||||
"desktop",
|
||||
"mobile"
|
||||
]
|
||||
},
|
||||
"ext.tabberNeue.legacy": {
|
||||
"packageFiles": [
|
||||
"ext.tabberNeue.legacy/ext.tabberNeue.legacy.js",
|
||||
{
|
||||
"name": "config.json",
|
||||
"name": "ext.tabberNeue.legacy/config.json",
|
||||
"config": {
|
||||
"enableAnimation": "TabberNeueEnableAnimation",
|
||||
"updateLocationOnTabChange": "TabberNeueUpdateLocationOnTabChange"
|
||||
|
@ -42,7 +56,7 @@
|
|||
"error"
|
||||
],
|
||||
"styles": [
|
||||
"ext.tabberNeue.less"
|
||||
"ext.tabberNeue.legacy/ext.tabberNeue.legacy.less"
|
||||
],
|
||||
"dependencies": [
|
||||
"mediawiki.Uri",
|
||||
|
@ -117,6 +131,11 @@
|
|||
},
|
||||
"config_prefix": "wg",
|
||||
"config": {
|
||||
"TabberNeueEnableLegacyMode": {
|
||||
"value": true,
|
||||
"description": "Use legacy mode to render Tabber. It is required for nested Tabber.",
|
||||
"public": true
|
||||
},
|
||||
"TabberNeueEnableAnimation": {
|
||||
"value": false,
|
||||
"description": "Enable or disable smooth scroll animation",
|
||||
|
|
|
@ -14,6 +14,7 @@ declare( strict_types=1 );
|
|||
|
||||
namespace MediaWiki\Extension\TabberNeue;
|
||||
|
||||
use MediaWiki\MediaWikiServices;
|
||||
use Parser;
|
||||
use PPFrame;
|
||||
|
||||
|
@ -31,17 +32,26 @@ class Tabber {
|
|||
public static function parserHook( string $input, array $args, Parser $parser, PPFrame $frame ) {
|
||||
$tabber = new Tabber();
|
||||
$html = $tabber->render( $input, $parser, $frame );
|
||||
|
||||
if ( $input === null ) {
|
||||
return;
|
||||
}
|
||||
// Critial rendering styles
|
||||
// See ext.tabberNeue.inline.less
|
||||
$style = sprintf(
|
||||
'<style id="tabber-style">%s</style>',
|
||||
'.client-js .tabber__header{height:2.6em;box-shadow:inset 0-1px 0 0;opacity:0.1}.client-js .tabber__header:after{position:absolute;width:16ch;height:0.5em;margin-top:1em;margin-left:0.75em;background:#000;border-radius:40px;content:""}.client-js .tabber__panel:not(:first-child){display:none}'
|
||||
);
|
||||
$parser->getOutput()->addHeadItem( $style, true );
|
||||
$parser->getOutput()->addModules( [ 'ext.tabberNeue' ] );
|
||||
|
||||
$isLegacy = MediaWikiServices::getInstance()->getMainConfig()->get( 'TabberNeueEnableLegacyMode' );
|
||||
|
||||
if ( $isLegacy === true ) {
|
||||
// Critial rendering styles
|
||||
// See ext.tabberNeue.inline.less
|
||||
$style = sprintf(
|
||||
'<style id="tabber-style">%s</style>',
|
||||
'.client-js .tabber__header{height:2.6em;box-shadow:inset 0-1px 0 0;opacity:0.1}.client-js .tabber__header:after{position:absolute;width:16ch;height:0.5em;margin-top:1em;margin-left:0.75em;background:#000;border-radius:40px;content:""}.client-js .tabber__panel:not(:first-child){display:none}'
|
||||
);
|
||||
$parser->getOutput()->addHeadItem( $style, true );
|
||||
$parser->getOutput()->addModules( [ 'ext.tabberNeue.legacy' ] );
|
||||
} else {
|
||||
$parser->getOutput()->addModules( [ 'ext.tabberNeue.codex' ] );
|
||||
}
|
||||
|
||||
$parser->addTrackingCategory( 'tabberneue-tabber-category' );
|
||||
return $html;
|
||||
}
|
||||
|
|
|
@ -34,17 +34,26 @@ class TabberTransclude {
|
|||
public static function parserHook( string $input, array $args, Parser $parser, PPFrame $frame ) {
|
||||
$tabberTransclude = new TabberTransclude();
|
||||
$html = $tabberTransclude->render( $input, $parser, $frame );
|
||||
|
||||
if ( $input === null ) {
|
||||
return;
|
||||
}
|
||||
// Critial rendering styles
|
||||
// See ext.tabberNeue.inline.less
|
||||
$style = sprintf(
|
||||
'<style id="tabber-style">%s</style>',
|
||||
'.tabber__header{height:2.6em;box-shadow:inset 0-1px 0 0;opacity:0.1}.tabber__header:after{position:absolute;width:16ch;height:0.5em;margin-top:1em;margin-left:0.75em;background:#000;border-radius:40px;content:""}.tabber__panel:not(:first-child){display:none}'
|
||||
);
|
||||
$parser->getOutput()->addHeadItem( $style, true );
|
||||
$parser->getOutput()->addModules( [ 'ext.tabberNeue' ] );
|
||||
|
||||
$isLegacy = MediaWikiServices::getInstance()->getMainConfig()->get( 'TabberNeueEnableLegacyMode' );
|
||||
|
||||
if ( $isLegacy === true ) {
|
||||
// Critial rendering styles
|
||||
// See ext.tabberNeue.inline.less
|
||||
$style = sprintf(
|
||||
'<style id="tabber-style">%s</style>',
|
||||
'.client-js .tabber__header{height:2.6em;box-shadow:inset 0-1px 0 0;opacity:0.1}.client-js .tabber__header:after{position:absolute;width:16ch;height:0.5em;margin-top:1em;margin-left:0.75em;background:#000;border-radius:40px;content:""}.client-js .tabber__panel:not(:first-child){display:none}'
|
||||
);
|
||||
$parser->getOutput()->addHeadItem( $style, true );
|
||||
$parser->getOutput()->addModules( [ 'ext.tabberNeue.legacy' ] );
|
||||
} else {
|
||||
$parser->getOutput()->addModules( [ 'ext.tabberNeue.codex' ] );
|
||||
}
|
||||
|
||||
$parser->addTrackingCategory( 'tabberneue-tabbertransclude-category' );
|
||||
return $html;
|
||||
}
|
||||
|
|
54
modules/ext.tabberNeue.codex/App.vue
Normal file
54
modules/ext.tabberNeue.codex/App.vue
Normal file
|
@ -0,0 +1,54 @@
|
|||
<template>
|
||||
<cdx-tabs v-model:active="currentTab" :framed="framed">
|
||||
<cdx-tab
|
||||
v-for="( tab, index ) in tabsData"
|
||||
:key="index"
|
||||
:name="tab.name"
|
||||
:label="tab.label"
|
||||
:disabled="tab.disabled"
|
||||
v-html = "tab.content"
|
||||
>
|
||||
</cdx-tab>
|
||||
</cdx-tabs>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
const { defineComponent } = require('vue');
|
||||
// Codex is available from ResourceLoader at runtime and is available without needing a build step.
|
||||
const { CdxTabs, CdxTab } = require('@wikimedia/codex');
|
||||
|
||||
// @vue/component
|
||||
module.exports = exports = defineComponent( {
|
||||
name: 'App',
|
||||
compatConfig: {
|
||||
MODE: 3
|
||||
},
|
||||
compilerOptions: {
|
||||
whitespace: 'condense'
|
||||
},
|
||||
components: {
|
||||
CdxTabs,
|
||||
CdxTab
|
||||
},
|
||||
props: {
|
||||
tabberData: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
framed: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
tabsData: this.tabberData.tabsData,
|
||||
currentTab: this.tabberData.currentTab
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
console.log( this.$el );
|
||||
this.$el.parentElement.classList.add( 'tabber--live' );
|
||||
}
|
||||
} );
|
||||
</script>
|
62
modules/ext.tabberNeue.codex/ext.tabberNeue.codex.js
Normal file
62
modules/ext.tabberNeue.codex/ext.tabberNeue.codex.js
Normal file
|
@ -0,0 +1,62 @@
|
|||
const
|
||||
Vue = require( 'vue' ),
|
||||
App = require( './App.vue' );
|
||||
|
||||
/**
|
||||
* @param {Element} tabber
|
||||
* @return {void}
|
||||
*/
|
||||
function initApp( tabber ) {
|
||||
const tabs = tabber.querySelectorAll( ':scope > .tabber__section > .tabber__panel' );
|
||||
|
||||
const tabberData = {
|
||||
tabsData: [],
|
||||
currentTab: ''
|
||||
};
|
||||
|
||||
tabs.forEach( tab => {
|
||||
const label = tab.getAttribute( 'data-title' );
|
||||
|
||||
if ( tab.querySelector( '.tabber' ) ) {
|
||||
throw new Error( 'Nested Tabber is not supported in Codex mode, please use legacy mode instead.' );
|
||||
}
|
||||
|
||||
tabberData.tabsData.push( {
|
||||
name: mw.util.escapeIdForAttribute( label ),
|
||||
label: label,
|
||||
content: tab.innerHTML
|
||||
} );
|
||||
} );
|
||||
|
||||
tabberData.currentTab = tabberData.tabsData[0].name;
|
||||
|
||||
// @ts-ignore MediaWiki-specific function
|
||||
Vue.createMwApp(
|
||||
App, Object.assign( {
|
||||
tabberData: tabberData
|
||||
} )
|
||||
)
|
||||
.mount( tabber );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Document} document
|
||||
* @return {void}
|
||||
*/
|
||||
function main( document ) {
|
||||
const tabbers = document.querySelectorAll( '.tabber:not( .tabber--live )' );
|
||||
let sortedTabbers = [];
|
||||
|
||||
/* Nested Tabber children needed to be rendered before parents */
|
||||
tabbers.forEach( tabber => {
|
||||
if ( tabber.querySelector( '.tabber:not( .tabber--live )' ) ) {
|
||||
sortedTabbers.push( tabber );
|
||||
} else {
|
||||
sortedTabbers.unshift( tabber );
|
||||
}
|
||||
} );
|
||||
|
||||
sortedTabbers.forEach( initApp );
|
||||
}
|
||||
|
||||
main( document );
|
Loading…
Reference in a new issue