<?php
|
namespace JVBase\ui;
|
|
if (!defined('ABSPATH')) {
|
exit;
|
}
|
|
/**
|
* Tabs UI component with fluent interface
|
*
|
* Usage:
|
* $tabs = new Tabs();
|
* $tabs->addTab('tab-slug')
|
* ->title('Tab Title')
|
* ->icon('iconName')
|
* ->description('Description text')
|
* ->content($content);
|
* echo $tabs->render();
|
*/
|
class Tabs {
|
private array $tabs = [];
|
private int $counter = 0;
|
|
/**
|
* Add a new tab and return it for chaining
|
*
|
* @param string $slug Unique identifier for the tab
|
* @return Tab
|
*/
|
public function addTab(string $slug = ''): Tab {
|
if (empty($slug)) {
|
$slug = 'tab-' . ++$this->counter;
|
}
|
|
$tab = new Tab($slug);
|
$this->tabs[$slug] = $tab;
|
return $tab;
|
}
|
|
/**
|
* Render all tabs as HTML
|
*
|
* @param bool $return Whether to return or echo the output
|
* @return string
|
*/
|
public function render(bool $return = true): string {
|
if (empty($this->tabs)) {
|
return '';
|
}
|
|
$header = '<nav class="tabs row start" role="tablist">';
|
$content = '';
|
$i = 0;
|
|
foreach ($this->tabs as $slug => $tab) {
|
if (!$tab->hasContent()) {
|
error_log('No content for tab: ' . $slug);
|
continue;
|
}
|
|
// Header
|
$active = ($i === 0) ? ' active' : '';
|
$selected = ($i === 0) ? 'true' : 'false';
|
$hidden = $tab->isHidden() ? ' hidden' : '';
|
|
$header .= '<button type="button" class="button tab' . $active . '" data-tab="' . $slug . '" role="tab" aria-selected="' . $selected . '"' . $hidden . '>
|
<h2 class="row">';
|
|
if ($tab->getIcon()) {
|
$header .= jvbIcon($tab->getIcon());
|
}
|
|
$header .= $tab->getTitle() . '</h2>
|
</button>';
|
|
// Content
|
$ariaHidden = ($i === 0) ? 'false' : 'true';
|
$content .= '<div class="tab-content' . $active . '" data-tab="' . $slug . '" role="tabpanel" aria-hidden="' . $ariaHidden . '"';
|
|
if ($i !== 0) {
|
$content .= ' hidden';
|
}
|
|
$content .= '>
|
<h2>' . $tab->getTitle() . '</h2>';
|
|
// Description
|
if ($tab->getDescription()) {
|
$description = $tab->getDescription();
|
if (!is_array($description)) {
|
$content .= jvb_filter_content( $description);
|
} else {
|
$content .= implode('', array_map(function ($paragraph) {
|
return jvb_filter_content( $paragraph);
|
}, $description));
|
}
|
}
|
|
$content .= $tab->getContent() . '
|
</div>';
|
$i++;
|
}
|
|
$header .= '</nav>';
|
$out = $header . $content;
|
|
if ($return) {
|
return $out;
|
}
|
|
echo $out;
|
return $out;
|
}
|
}
|
|
/**
|
* Individual tab with fluent interface for configuration
|
*/
|
class Tab {
|
private string $slug;
|
private string $title = '';
|
private ?string $icon = null;
|
private string|array|null $description = null;
|
private string $content = '';
|
private bool $hidden = false;
|
|
public function __construct(string $slug) {
|
$this->slug = $slug;
|
}
|
|
/**
|
* Set the tab title
|
*
|
* @param string $title
|
* @return self
|
*/
|
public function title(string $title): self {
|
$this->title = $title;
|
return $this;
|
}
|
|
/**
|
* Set the tab icon
|
*
|
* @param string $icon Icon name (used with jvbIcon())
|
* @return self
|
*/
|
public function icon(string $icon): self {
|
$this->icon = $icon;
|
return $this;
|
}
|
|
/**
|
* Set the tab description (can be string or array)
|
*
|
* @param string|array $description
|
* @return self
|
*/
|
public function description(string|array $description): self {
|
$this->description = $description;
|
return $this;
|
}
|
|
/**
|
* Set the tab content
|
*
|
* @param string $content
|
* @return self
|
*/
|
public function content(string $content): self {
|
$this->content = $content;
|
return $this;
|
}
|
|
/**
|
* Mark tab as hidden
|
*
|
* @param bool $hidden
|
* @return self
|
*/
|
public function hidden(bool $hidden = true): self {
|
$this->hidden = $hidden;
|
return $this;
|
}
|
|
// Getters
|
public function getTitle(): string {
|
return $this->title;
|
}
|
|
public function getIcon(): ?string {
|
return $this->icon;
|
}
|
|
public function getDescription(): string|array|null {
|
return $this->description;
|
}
|
|
public function getContent(): string {
|
return $this->content;
|
}
|
|
public function isHidden(): bool {
|
return $this->hidden;
|
}
|
|
public function hasContent(): bool {
|
return !empty($this->content);
|
}
|
}
|