Skip to content

Commit cf6e155

Browse files
authored
Merge pull request #1316 from wouterj/tabs-directive-to-rst-package
Move tabs/tab directive to rst package
2 parents 685c945 + cf23efe commit cf6e155

20 files changed

Lines changed: 447 additions & 192 deletions

File tree

packages/guides-restructured-text/resources/config/guides-restructured-text.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,9 @@
4848
use phpDocumentor\Guides\RestructuredText\Directives\SeeAlsoDirective;
4949
use phpDocumentor\Guides\RestructuredText\Directives\SidebarDirective;
5050
use phpDocumentor\Guides\RestructuredText\Directives\SubDirective;
51+
use phpDocumentor\Guides\RestructuredText\Directives\TabDirective;
5152
use phpDocumentor\Guides\RestructuredText\Directives\TableDirective;
53+
use phpDocumentor\Guides\RestructuredText\Directives\TabsDirective;
5254
use phpDocumentor\Guides\RestructuredText\Directives\TestLoggerDirective;
5355
use phpDocumentor\Guides\RestructuredText\Directives\TipDirective;
5456
use phpDocumentor\Guides\RestructuredText\Directives\TitleDirective;
@@ -232,6 +234,8 @@
232234
->set(TableDirective::class)
233235
->set(TestLoggerDirective::class)
234236
->set(TipDirective::class)
237+
->set(TabDirective::class)
238+
->set(TabsDirective::class)
235239
->set(TitleDirective::class)
236240
->set(ToctreeDirective::class)
237241
->bind('$startingRule', service(InlineMarkupRule::class))
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{{ renderNode(tab.value) }}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<div class="tabs">
2+
<ul>
3+
{% for tab in node.tabs -%}
4+
<li><button type="button" data-tabs="{{ node.key }}" data-target="{{ node.key }}-{{ tab.key }}" class="{{ tab.active ? 'active' }}">{{ renderNode(tab.content) }}</button></li>
5+
{%- endfor %}
6+
</ul>
7+
<div class="tab-content" id="tab-content-{{ node.key }}">
8+
{% for tab in node.tabs -%}
9+
<div class="tab {{- tab.active ? ' active' }}" id="{{ node.key }}-{{ tab.key }}">
10+
{{- renderNode(tab.value) -}}
11+
</div>
12+
{%- endfor %}
13+
</div>
14+
</div>

packages/guides-restructured-text/src/RestructuredText/DependencyInjection/ReStructuredTextExtension.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
use phpDocumentor\Guides\RestructuredText\DependencyInjection\Compiler\TextRolePass;
1717
use phpDocumentor\Guides\RestructuredText\Nodes\ConfvalNode;
1818
use phpDocumentor\Guides\RestructuredText\Nodes\OptionNode;
19+
use phpDocumentor\Guides\RestructuredText\Nodes\TabNode;
20+
use phpDocumentor\Guides\RestructuredText\Nodes\TabsNode;
1921
use phpDocumentor\Guides\RestructuredText\Nodes\VersionChangeNode;
2022
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
2123
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
@@ -69,7 +71,8 @@ public function prepend(ContainerBuilder $container): void
6971
template(VersionChangeNode::class, 'body/version-change.html.twig'),
7072
template(ConfvalNode::class, 'body/directive/confval.tex.twig', 'tex'),
7173
template(OptionNode::class, 'body/directive/option.html.twig'),
72-
74+
template(TabNode::class, 'body/directive/tab.html.twig'),
75+
template(TabsNode::class, 'body/directive/tabs.html.twig'),
7376
],
7477
],
7578
);
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* This file is part of phpDocumentor.
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*
11+
* @link https://phpdoc.org
12+
*/
13+
14+
namespace phpDocumentor\Guides\RestructuredText\Directives;
15+
16+
use phpDocumentor\Guides\Nodes\CollectionNode;
17+
use phpDocumentor\Guides\Nodes\InlineCompoundNode;
18+
use phpDocumentor\Guides\Nodes\Node;
19+
use phpDocumentor\Guides\RestructuredText\Nodes\TabNode;
20+
use phpDocumentor\Guides\RestructuredText\Parser\BlockContext;
21+
use phpDocumentor\Guides\RestructuredText\Parser\Directive;
22+
23+
use function class_alias;
24+
use function class_exists;
25+
use function is_string;
26+
use function preg_replace;
27+
use function str_replace;
28+
use function strtolower;
29+
30+
final class TabDirective extends SubDirective
31+
{
32+
public function getName(): string
33+
{
34+
return 'tab';
35+
}
36+
37+
/** {@inheritDoc}
38+
*
39+
* @param Directive $directive
40+
*/
41+
protected function processSub(
42+
BlockContext $blockContext,
43+
CollectionNode $collectionNode,
44+
Directive $directive,
45+
): Node|null {
46+
if (is_string($directive->getOption('key')->getValue())) {
47+
$key = strtolower($directive->getOption('key')->getValue());
48+
} else {
49+
$key = strtolower($directive->getData());
50+
}
51+
52+
$key = str_replace(' ', '-', $key);
53+
$key = (string) (preg_replace('/[^a-zA-Z0-9\-_]/', '', $key));
54+
$active = $directive->hasOption('active');
55+
56+
return new TabNode(
57+
'tab',
58+
$directive->getData(),
59+
$directive->getDataNode() ?? new InlineCompoundNode(),
60+
$key,
61+
$active,
62+
$collectionNode->getChildren(),
63+
);
64+
}
65+
}
66+
67+
if (!class_exists(\phpDocumentor\Guides\Bootstrap\Directives\TabDirective::class, false)) {
68+
class_alias(TabDirective::class, \phpDocumentor\Guides\Bootstrap\Directives\TabDirective::class);
69+
}
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* This file is part of phpDocumentor.
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*
11+
* @link https://phpdoc.org
12+
*/
13+
14+
namespace phpDocumentor\Guides\RestructuredText\Directives;
15+
16+
use phpDocumentor\Guides\Nodes\CollectionNode;
17+
use phpDocumentor\Guides\Nodes\InlineCompoundNode;
18+
use phpDocumentor\Guides\Nodes\Node;
19+
use phpDocumentor\Guides\ReferenceResolvers\AnchorNormalizer;
20+
use phpDocumentor\Guides\RestructuredText\Nodes\AbstractTabNode;
21+
use phpDocumentor\Guides\RestructuredText\Nodes\TabsNode;
22+
use phpDocumentor\Guides\RestructuredText\Parser\BlockContext;
23+
use phpDocumentor\Guides\RestructuredText\Parser\Directive;
24+
use phpDocumentor\Guides\RestructuredText\Parser\Productions\Rule;
25+
use Psr\Log\LoggerInterface;
26+
27+
use function class_alias;
28+
use function class_exists;
29+
use function is_string;
30+
31+
final class TabsDirective extends SubDirective
32+
{
33+
private int $tabsCounter = 0;
34+
35+
/** @param Rule<CollectionNode> $startingRule */
36+
public function __construct(
37+
protected Rule $startingRule,
38+
private readonly LoggerInterface $logger,
39+
private readonly AnchorNormalizer $anchorReducer,
40+
) {
41+
parent::__construct($startingRule);
42+
}
43+
44+
public function getName(): string
45+
{
46+
return 'tabs';
47+
}
48+
49+
/** {@inheritDoc}
50+
*
51+
* @param Directive $directive
52+
*/
53+
protected function processSub(
54+
BlockContext $blockContext,
55+
CollectionNode $collectionNode,
56+
Directive $directive,
57+
): Node|null {
58+
$tabs = [];
59+
$hasActive = false;
60+
foreach ($collectionNode->getChildren() as $child) {
61+
if ($child instanceof AbstractTabNode) {
62+
if ($child->isActive()) {
63+
if (!$hasActive) {
64+
$hasActive = true;
65+
} else {
66+
// There may only be one active child, first wins
67+
$child->setActive(false);
68+
}
69+
}
70+
71+
$tabs[] = $child;
72+
} else {
73+
$this->logger->warning(
74+
'The "tabs" directive may only contain children of type "tab". The following node was found: ' . $child::class,
75+
$blockContext->getLoggerInformation(),
76+
);
77+
}
78+
}
79+
80+
if (!$hasActive && isset($tabs[0])) {
81+
$tabs[0]->setActive(true);
82+
}
83+
84+
if (is_string($directive->getOption('key')->getValue())) {
85+
$key = $this->anchorReducer->reduceAnchor($directive->getOption('key')->getValue());
86+
} else {
87+
$this->tabsCounter++;
88+
$key = 'tabs-' . $this->tabsCounter;
89+
}
90+
91+
return new TabsNode(
92+
'tabs',
93+
$directive->getData(),
94+
$directive->getDataNode() ?? new InlineCompoundNode(),
95+
$key,
96+
$tabs,
97+
);
98+
}
99+
}
100+
101+
if (!class_exists(\phpDocumentor\Guides\Bootstrap\Directives\TabsDirective::class, false)) {
102+
class_alias(TabsDirective::class, \phpDocumentor\Guides\Bootstrap\Directives\TabsDirective::class);
103+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* This file is part of phpDocumentor.
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*
11+
* @link https://phpdoc.org
12+
*/
13+
14+
namespace phpDocumentor\Guides\RestructuredText\Nodes;
15+
16+
use phpDocumentor\Guides\Nodes\InlineCompoundNode;
17+
use phpDocumentor\Guides\Nodes\Node;
18+
19+
use function class_alias;
20+
use function class_exists;
21+
22+
abstract class AbstractTabNode extends GeneralDirectiveNode
23+
{
24+
/** @param list<Node> $value */
25+
public function __construct(
26+
string $name,
27+
string $plainContent,
28+
InlineCompoundNode $content,
29+
private readonly string $key,
30+
private bool $active,
31+
array $value = [],
32+
) {
33+
parent::__construct($name, $plainContent, $content, $value);
34+
}
35+
36+
public function getKey(): string
37+
{
38+
return $this->key;
39+
}
40+
41+
public function isActive(): bool
42+
{
43+
return $this->active;
44+
}
45+
46+
public function setActive(bool $active): void
47+
{
48+
$this->active = $active;
49+
}
50+
}
51+
52+
if (!class_exists(\phpDocumentor\Guides\Bootstrap\Nodes\AbstractTabNode::class, false)) {
53+
class_alias(AbstractTabNode::class, \phpDocumentor\Guides\Bootstrap\Nodes\AbstractTabNode::class);
54+
}

packages/guides-restructured-text/src/RestructuredText/Nodes/GeneralDirectiveNode.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,12 @@
2020
/**
2121
* A catch-all directive Node containing all information about the original directive in rst.
2222
*
23-
* @extends CompoundNode<Node>
23+
* @template TValue of Node = Node
24+
* @extends CompoundNode<TValue>
2425
*/
2526
class GeneralDirectiveNode extends CompoundNode
2627
{
27-
/** @param list<Node> $value */
28+
/** @param list<TValue> $value */
2829
public function __construct(
2930
private readonly string $name,
3031
private readonly string $plainContent,
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* This file is part of phpDocumentor.
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*
11+
* @link https://phpdoc.org
12+
*/
13+
14+
namespace phpDocumentor\Guides\RestructuredText\Nodes;
15+
16+
use function class_alias;
17+
use function class_exists;
18+
19+
final class TabNode extends AbstractTabNode
20+
{
21+
}
22+
23+
24+
if (!class_exists(\phpDocumentor\Guides\Bootstrap\Nodes\TabNode::class, false)) {
25+
class_alias(TabNode::class, \phpDocumentor\Guides\Bootstrap\Nodes\TabNode::class);
26+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* This file is part of phpDocumentor.
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*
11+
* @link https://phpdoc.org
12+
*/
13+
14+
namespace phpDocumentor\Guides\RestructuredText\Nodes;
15+
16+
use phpDocumentor\Guides\Nodes\InlineCompoundNode;
17+
18+
use function class_alias;
19+
use function class_exists;
20+
21+
/** @extends GeneralDirectiveNode<AbstractTabNode> */
22+
final class TabsNode extends GeneralDirectiveNode
23+
{
24+
/** @param list<AbstractTabNode> $value */
25+
public function __construct(
26+
string $name,
27+
string $plainContent,
28+
InlineCompoundNode $content,
29+
private readonly string $key,
30+
array $value,
31+
) {
32+
parent::__construct($name, $plainContent, $content, $value);
33+
}
34+
35+
/** @return list<AbstractTabNode> */
36+
public function getTabs(): array
37+
{
38+
return $this->getChildren();
39+
}
40+
41+
public function getKey(): string
42+
{
43+
return $this->key;
44+
}
45+
}
46+
47+
if (!class_exists(\phpDocumentor\Guides\Bootstrap\Nodes\TabsNode::class, false)) {
48+
class_alias(TabsNode::class, \phpDocumentor\Guides\Bootstrap\Nodes\TabsNode::class);
49+
}

0 commit comments

Comments
 (0)