refactor: use Mustache to generate HTML

This commit is contained in:
alistair3149 2024-11-19 16:01:11 -05:00
parent dc1d16c276
commit 11eb2ac328
No known key found for this signature in database
5 changed files with 56 additions and 91 deletions

View file

@ -88,7 +88,13 @@ class Tabber {
*/ */
public static function render( string $input, int $count, Parser $parser, PPFrame $frame ): string { public static function render( string $input, int $count, Parser $parser, PPFrame $frame ): string {
$arr = explode( '|-|', $input ); $arr = explode( '|-|', $input );
$tabs = ''; $data = [
'count' => $count,
'array-tabs' => []
];
// For Codex use only
// TODO: Maybe we should redo the whole Codex implementation
$tabpanels = ''; $tabpanels = '';
foreach ( $arr as $tab ) { foreach ( $arr as $tab ) {
@ -97,13 +103,17 @@ class Tabber {
continue; continue;
} }
$data['array-tabs'][] = [
'content' => $tabData['content'],
'label' => $tabData['label'],
'tabId' => "tabber-tab-{$tabData['id']}",
'tabpanelId' => self::$useLegacyId ? $tabData['id'] : "tabber-tabpanel-{$tabData['id']}"
];
if ( self::$useCodex && self::$isNested ) { if ( self::$useCodex && self::$isNested ) {
$tabpanels .= self::getCodexNestedTabJSON( $tabData ); $tabpanels .= self::getCodexNestedTabJSON( $tabData );
continue; continue;
} }
$tabs .= self::getTabHTML( $tabData );
$tabpanels .= self::getTabpanelHTML( $tabData );
} }
if ( self::$useCodex && self::$isNested ) { if ( self::$useCodex && self::$isNested ) {
@ -115,11 +125,6 @@ class Tabber {
} }
$templateParser = new TemplateParser( __DIR__ . '/templates' ); $templateParser = new TemplateParser( __DIR__ . '/templates' );
$data = [
'count' => $count,
'html-tabs' => $tabs,
'html-tabpanels' => $tabpanels
];
return $templateParser->processTemplate( 'Tabber', $data ); return $templateParser->processTemplate( 'Tabber', $data );
} }
@ -212,6 +217,11 @@ class Tabber {
} }
$data['content'] = self::getTabContent( $content, $parser, $frame ); $data['content'] = self::getTabContent( $content, $parser, $frame );
$isContentHTML = strpos( $content, '<' ) === 0;
if ( $data['content'] && !$isContentHTML ) {
// If $content does not have any HTML element (i.e. just a text node), wrap it in <p/>
$data['content'] = Html::rawElement( 'p', [], $data['content'] );
}
$id = Sanitizer::escapeIdForAttribute( htmlspecialchars( $data['label'] ) ); $id = Sanitizer::escapeIdForAttribute( htmlspecialchars( $data['label'] ) );
if ( self::$useLegacyId === true ) { if ( self::$useLegacyId === true ) {
@ -231,47 +241,6 @@ class Tabber {
return $data; return $data;
} }
/**
* Get the HTML for a tab.
*
* @param array $tabData Tab data
*
* @return string HTML
*/
private static function getTabHTML( array $tabData ): string {
$tabpanelId = "tabber-tabpanel-{$tabData['id']}";
return Html::rawElement( 'a', [
'class' => 'tabber__tab',
'id' => "tabber-tab-{$tabData['id']}",
'href' => "#$tabpanelId",
'role' => 'tab',
'aria-controls' => $tabpanelId
], $tabData['label'] );
}
/**
* Get the HTML for a tabpanel.
*
* @param array $tabData Tab data
*
* @return string HTML
*/
private static function getTabpanelHTML( array $tabData ): string {
$content = $tabData['content'];
$isContentHTML = strpos( $content, '<' ) === 0;
if ( $content && !$isContentHTML ) {
// If $content does not have any HTML element (i.e. just a text node), wrap it in <p/>
$content = Html::rawElement( 'p', [], $content );
}
return Html::rawElement( 'article', [
'class' => 'tabber__panel',
'id' => "tabber-tabpanel-{$tabData['id']}",
'role' => 'tabpanel',
'tabindex' => 0,
'aria-labelledby' => "tabber-tab-{$tabData['id']}"
], $content );
}
/** /**
* Get JSON representation of a nested tab for Codex * Get JSON representation of a nested tab for Codex
* *

View file

@ -15,7 +15,6 @@ declare( strict_types=1 );
namespace MediaWiki\Extension\TabberNeue; namespace MediaWiki\Extension\TabberNeue;
use Exception; use Exception;
use Html;
use MediaWiki\MediaWikiServices; use MediaWiki\MediaWikiServices;
use Parser; use Parser;
use PPFrame; use PPFrame;
@ -24,6 +23,10 @@ use TemplateParser;
use Title; use Title;
class TabberTransclude { class TabberTransclude {
/** @var bool */
private static $useLegacyId = false;
/** /**
* Parser callback for <tabbertransclude> tag * Parser callback for <tabbertransclude> tag
* *
@ -39,8 +42,11 @@ class TabberTransclude {
return ''; return '';
} }
$config = MediaWikiServices::getInstance()->getMainConfig();
$parserOutput = $parser->getOutput(); $parserOutput = $parser->getOutput();
self::$useLegacyId = $config->get( 'TabberNeueUseLegacyTabIds' );
$count = count( $parserOutput->getExtensionData( 'tabber-count' ) ?? [] ); $count = count( $parserOutput->getExtensionData( 'tabber-count' ) ?? [] );
$html = self::render( $input, $count, $parser, $frame ); $html = self::render( $input, $count, $parser, $frame );
@ -65,29 +71,34 @@ class TabberTransclude {
public static function render( string $input, int $count, Parser $parser, PPFrame $frame ): string { public static function render( string $input, int $count, Parser $parser, PPFrame $frame ): string {
$selected = true; $selected = true;
$arr = explode( "\n", $input ); $arr = explode( "\n", $input );
$tabs = ''; $data = [
$tabpanels = ''; 'count' => $count,
'array-tabs' => []
];
foreach ( $arr as $tab ) { foreach ( $arr as $tab ) {
$tabData = self::getTabData( $tab ); $tabData = self::getTabData( $tab );
if ( $tabData === [] ) { if ( $tabData === [] ) {
continue; continue;
} }
$tabs .= self::getTabHTML( $tabData );
$tabpanelHtml = '';
try { try {
$tabpanels .= self::buildTabTransclude( $tabData, $parser, $frame, $selected ); $tabpanelHtml = self::buildTabTransclude( $tabData, $parser, $frame, $selected );
} catch ( Exception $e ) { } catch ( Exception $e ) {
// This can happen if a $currentTitle is null // This can happen if a $currentTitle is null
continue; continue;
} }
$data['array-tabs'][] = [
'html-tabpanel' => $tabpanelHtml,
'label' => $tabData['label'],
'tabId' => "tabber-tab-{$tabData['id']}",
'tabpanelId' => self::$useLegacyId ? $tabData['id'] : "tabber-tabpanel-{$tabData['id']}"
];
} }
$templateParser = new TemplateParser( __DIR__ . '/templates' ); $templateParser = new TemplateParser( __DIR__ . '/templates' );
$data = [
'count' => $count,
'html-tabs' => $tabs,
'html-tabpanels' => $tabpanels
];
return $templateParser->processTemplate( 'Tabber', $data ); return $templateParser->processTemplate( 'Tabber', $data );
} }
@ -117,24 +128,6 @@ class TabberTransclude {
return $data; return $data;
} }
/**
* Get the HTML for a tab.
*
* @param array $tabData Tab data
*
* @return string HTML
*/
private static function getTabHTML( array $tabData ): string {
$tabpanelId = "tabber-tabpanel-{$tabData['id']}";
return Html::element( 'a', [
'class' => 'tabber__tab',
'id' => "tabber-tab-{$tabData['id']}",
'href' => "#$tabpanelId",
'role' => 'tab',
'aria-controls' => $tabpanelId
], $tabData['label'] );
}
/** /**
* Build individual tab. * Build individual tab.
* *

View file

@ -1,13 +1,11 @@
{{! <div id="tabber-{{count}}" class='tabber tabber--init'>{{!
int count Current Tabber count }}<header class="tabber__header">{{!
string html-tabs }}<button class="tabber__header__prev" aria-hidden="true"></button>{{!
string html-tabpanels }}<nav class="tabber__tabs" role="tablist">{{!
}} }}{{#array-tabs}}{{>TabberTab}}{{/array-tabs}}{{!
<div id="tabber-{{count}}" class='tabber tabber--init'> }}{{#html-tabs}}{{{.}}}{{/html-tabs}}{{!
<header class="tabber__header"> }}</nav>{{!
<button class="tabber__header__prev" aria-hidden="true"></button> }}<button class="tabber__header__next" aria-hidden="true"></button>{{!
<nav class="tabber__tabs" role="tablist">{{{html-tabs}}}</nav> }}</header>{{!
<button class="tabber__header__next" aria-hidden="true"></button> }}<section class="tabber__section">{{>TabberTabpanel}}</section>{{!
</header> }}</div>
<section class="tabber__section">{{{html-tabpanels}}}</section>
</div>

View file

@ -0,0 +1 @@
<a class="tabber__tab" id="{{tabId}}" href="#{{tabpanelId}}" role="tab" aria-controls="{{tabpanelId}}">{{{label}}}</a>

View file

@ -0,0 +1,4 @@
{{#array-tabs}}
{{^html-tabpanel}}<article class="tabber__panel" id="{{tabpanelId}}" role="tabpanel" tabindex="0" aria-labelledby="{{tabId}}">{{{content}}}</article>{{/html-tabpanel}}
{{#html-tabpanel}}{{{.}}}{{/html-tabpanel}}
{{/array-tabs}}