Accordion
Use <details> Element to toggle sections of content without JavaScript.
Live Example
Section #1
Markdown
This is Markdown!
Section #2
This is a Component
Section #3
# Markdown This is **NOT** *Markdown*!Button (primary)
Markdown
This is Markdown!
Button (contrast outline)
This is a Component
Button (secondary)
# Markdown This is **NOT** *Markdown*!h2
Markdown
This is Markdown!
h3
This is a Component
h4
# Markdown
This is **NOT** *Markdown*!Example Code
<?php
declare(strict_types=1);
namespace TempestPico\Components\Examples;
use TempestPico\Components\Accordion;
use TempestPico\Components\Messages;
use function TempestPico\Support\Html\VT;
$content = [
'Section #*1*' => "# Markdown\n\nThis is *Markdown*!",
'Section #*2*' => new Messages(['variant' => 'info', 'md' => 'This is a Component']),
'Section #*3*' => VT("# Markdown\n\nThis is **NOT** *Markdown*!"),
];
return VT(
new Accordion('acco', $content),
new Accordion(
'acco',
['Button (primary)' => $content['Section #*1*']],
Accordion::Options('button', 'primary'),
),
new Accordion(
'acco',
['Button (contrast outline)' => $content['Section #*2*']],
Accordion::Options('button-outline', 'contrast'),
),
new Accordion(
'acco',
['Button (secondary)' => $content['Section #*3*']],
Accordion::Options(btnColor: 'secondary'),
),
new Accordion(
'acco',
['h2' => $content['Section #*1*']],
Accordion::Options('h2'),
),
new Accordion(
'acco',
['h3' => $content['Section #*2*']],
Accordion::Options('h3'),
),
new Accordion(
'acco',
['h4' => $content['Section #*3*']],
Accordion::Options('h4'),
),
);
HTML Output
<details name="acco"><summary>Section #<em>1</em>
</summary><h1>Markdown</h1>
<p>This is <em>Markdown</em>!</p>
</details><details name="acco"><summary>Section #<em>2</em>
</summary><article style="--pico-card-background-color: light-dark(#9bccfd, #1343a0)"><p>This is a Component</p>
</article></details><details name="acco"><summary>Section #<em>3</em>
</summary># Markdown
This is **NOT** *Markdown*!</details><details name="acco"><summary role="button">Button (primary)
</summary><h1>Markdown</h1>
<p>This is <em>Markdown</em>!</p>
</details><details name="acco"><summary role="button">Button (contrast outline)
</summary><article style="--pico-card-background-color: light-dark(#9bccfd, #1343a0)"><p>This is a Component</p>
</article></details><details name="acco"><summary>Button (secondary)
</summary># Markdown
This is **NOT** *Markdown*!</details><details name="acco"><summary><h2>h2
</h2></summary><h1>Markdown</h1>
<p>This is <em>Markdown</em>!</p>
</details><details name="acco"><summary><h3>h3
</h3></summary><article style="--pico-card-background-color: light-dark(#9bccfd, #1343a0)"><p>This is a Component</p>
</article></details><details name="acco"><summary><h4>h4
</h4></summary># Markdown
This is **NOT** *Markdown*!</details>Code: Accordion.php
<?php
declare(strict_types=1);
namespace TempestPico\Components;
use TempestPico\Support\Html\HtmlViewTree;
use function Tempest\Support\Arr\map_iterable;
use function TempestPico\Support\Html\composeStr;
use function TempestPico\Support\Html\Html;
use function TempestPico\Support\Html\IMD;
use function TempestPico\Support\Html\MD;
use function TempestPico\Support\Html\VT;
/**
*
* @phpstan-type Opt = array{
* variant: 'default'|'button-outline'|'button'|'h6'|'h5'|'h4'|'h3'|'h2',
* btn-color: 'primary'|'secondary'|'contrast',
* open: false|IMD,
* }
*
*/
#[Doc('Use `<details>` Element to toggle sections of content without JavaScript.', ['Pico'])]
final class Accordion implements Component
{
use IsComponent;
/**
* @param Opt $options
* @param array<IDM, MD|Content> $content
*/
public function __construct(
protected string $name,
protected array $content,
protected ?array $options = null,
) {
$this->options ??= self::Options();
$this->setPaths();
}
/**
* Create an options array for the constructor.
*
* @param Opt['variant'] $variant
* @param Opt['btn-color'] $btnColor
* @param Opt['open'] $open The index (IMD) of the open section
*
* @return Opt
*
*/
static function Options(
string $variant = 'default',
string $btnColor = 'primary',
false|string $open = false,
): array {
return [
'variant' => $variant,
'btn-color' => $btnColor,
'open' => $open,
];
}
private function subViewSummary(string $summary): HtmlViewTree
{
return Html(
element: in_array(
$this->options['variant'],
['h6', 'h5', 'h4', 'h3', 'h2'],
true,
)
? $this->options['variant']
: null,
content: IMD($summary),
);
}
public function getViewTree(): HtmlViewTree
{
return VT(...map_iterable(
$this->content,
fn ($content, $summary) => Html(
element: 'details',
content: [
Html(
'summary',
[$this->subViewSummary((string) $summary)], // FIXME: PhpStan says $summary is `int|string`
[
'role' => in_array(
$this->options['variant'],
['button', 'button-outline'],
true,
)
? 'button'
: false,
'class' => composeStr([
'outline' => $this->options['variant'] === 'button-outline',
$this->options['btn-color'] => $this->options['variant'] !== 'default',
]),
],
),
is_string($content) ? MD($content) : $content,
],
attributes: [
'name' => $this->name,
'open' => $this->options['open'] !== false && $this->options['open'] === $summary,
],
),
));
}
}