Skip to content

Commit dfab472

Browse files
committed
[FEATURE] Introduce directives for bootstrap cards
1 parent 0860ac1 commit dfab472

File tree

30 files changed

+2000
-1
lines changed

30 files changed

+2000
-1
lines changed

packages/guides-theme-bootstrap/resources/config/guides-theme-bootstrap.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@
22

33
declare(strict_types=1);
44

5+
use phpDocumentor\Guides\Bootstrap\Directives\CardDirective;
6+
use phpDocumentor\Guides\Bootstrap\Directives\CardFooterDirective;
7+
use phpDocumentor\Guides\Bootstrap\Directives\CardGridDirective;
8+
use phpDocumentor\Guides\Bootstrap\Directives\CardGroupDirective;
9+
use phpDocumentor\Guides\Bootstrap\Directives\CardHeaderDirective;
10+
use phpDocumentor\Guides\Bootstrap\Directives\CardImageDirective;
511
use phpDocumentor\Guides\Bootstrap\Directives\TabDirective;
612
use phpDocumentor\Guides\Bootstrap\Directives\TabsDirective;
713
use phpDocumentor\Guides\RestructuredText\Directives\BaseDirective;
@@ -20,6 +26,12 @@
2026
->bind('$startingRule', service(DirectiveContentRule::class))
2127
->instanceof(BaseDirective::class)
2228
->tag('phpdoc.guides.directive')
29+
->set(CardDirective::class)
30+
->set(CardFooterDirective::class)
31+
->set(CardHeaderDirective::class)
32+
->set(CardImageDirective::class)
33+
->set(CardGroupDirective::class)
34+
->set(CardGridDirective::class)
2335
->set(TabDirective::class)
2436
->set(TabsDirective::class);
2537
};
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<div class="row card-group
2+
{%- if node.classes %} {{ node.classesString }}{% endif -%}
3+
{%- if node.columns > 0 %} row-cols-{{ node.columns }}{% endif -%}
4+
{%- if node.columnsSm > 0 %} row-cols-sm-{{ node.columnsSm }}{% endif -%}
5+
{%- if node.columnsMd > 0 %} row-cols-md-{{ node.columnsMd }}{% endif -%}
6+
{%- if node.columnsLg > 0 %} row-cols-lg-{{ node.columnsLg }}{% endif -%}
7+
{%- if node.columnsXl > 0 %} row-cols-xl-{{ node.columnsXl }}{% endif -%}
8+
{%- if node.gap > 0 %} g-{{ node.gap }}{% endif -%}
9+
">
10+
{% for card in node.value -%}
11+
<div class="col">
12+
{{ renderNode(card) }}
13+
</div>
14+
{%- endfor %}
15+
</div>
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<div class="card-group {%- if node.classes %} {{ node.classesString }}{% endif %}">
2+
{{ renderNode(node.value) }}
3+
</div>
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<div class="card {%- if node.cardHeight > 0 %} h-{{ node.cardHeight }}{% endif %} {%- if node.classes %} {{ node.classesString }}{% endif %}" {%- if node.isNoindex == false %} id="{{ node.anchor }}"{% endif %}>
2+
{% if node.cardHeader %}
3+
<div class="card-header">
4+
{% if node.cardHeader.content %}
5+
{{ renderNode(node.cardHeader.content) }}
6+
{% endif %}
7+
{% if node.cardHeader.value %}
8+
{{ renderNode(node.cardHeader.value) }}
9+
{% endif %}
10+
</div>
11+
{% endif %}
12+
{% if node.cardImage and node.cardImage.position != 'bottom' %}
13+
{% include "body/directive/card/card-image.html.twig" %}
14+
{% endif %}
15+
<div class="card-body">
16+
{{ renderNode(node.title) }}
17+
<div class="card-text">
18+
{{ renderNode(node.value) }}
19+
</div>
20+
</div>
21+
{% if node.cardImage and node.cardImage.position == 'bottom' %}
22+
{% include "body/directive/card/card-image.html.twig" %}
23+
{% endif %}
24+
{% if node.cardFooter %}
25+
<div class="card-footer">
26+
{% if node.cardFooter.content %}
27+
{{ renderNode(node.cardFooter.content) }}
28+
{% endif %}
29+
{% if node.cardFooter.value %}
30+
{{ renderNode(node.cardFooter.value) }}
31+
{% endif %}
32+
</div>
33+
{% endif %}
34+
</div>
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<img src="{{ asset(node.cardImage.plainContent) }}" class="card-img-{{ node.cardImage.position }}" alt="{{ node.cardImage.alt }}">
2+
{% if node.cardImage.value %}
3+
<div class="card-img-overlay">
4+
<div class="card-text">
5+
{{ renderNode(node.cardImage.value) }}
6+
</div>
7+
</div>
8+
{% endif %}
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
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\Bootstrap\Directives;
15+
16+
use phpDocumentor\Guides\Bootstrap\Nodes\Card\CardFooterNode;
17+
use phpDocumentor\Guides\Bootstrap\Nodes\Card\CardHeaderNode;
18+
use phpDocumentor\Guides\Bootstrap\Nodes\Card\CardImageNode;
19+
use phpDocumentor\Guides\Bootstrap\Nodes\CardNode;
20+
use phpDocumentor\Guides\Nodes\CollectionNode;
21+
use phpDocumentor\Guides\Nodes\InlineCompoundNode;
22+
use phpDocumentor\Guides\Nodes\Node;
23+
use phpDocumentor\Guides\Nodes\TitleNode;
24+
use phpDocumentor\Guides\ReferenceResolvers\AnchorNormalizer;
25+
use phpDocumentor\Guides\RestructuredText\Directives\SubDirective;
26+
use phpDocumentor\Guides\RestructuredText\Parser\BlockContext;
27+
use phpDocumentor\Guides\RestructuredText\Parser\Directive;
28+
use phpDocumentor\Guides\RestructuredText\Parser\Productions\Rule;
29+
use phpDocumentor\Guides\RestructuredText\TextRoles\GenericLinkProvider;
30+
31+
class CardDirective extends SubDirective
32+
{
33+
public const NAME = 'card';
34+
35+
public function __construct(
36+
protected Rule $startingRule,
37+
GenericLinkProvider $genericLinkProvider,
38+
private readonly AnchorNormalizer $anchorReducer,
39+
) {
40+
parent::__construct($startingRule);
41+
42+
$genericLinkProvider->addGenericLink(self::NAME, CardNode::LINK_TYPE, CardNode::LINK_PREFIX);
43+
}
44+
45+
public function getName(): string
46+
{
47+
return self::NAME;
48+
}
49+
50+
protected function processSub(
51+
BlockContext $blockContext,
52+
CollectionNode $collectionNode,
53+
Directive $directive,
54+
): Node|null {
55+
$title = null;
56+
if ($directive->getDataNode() !== null) {
57+
$title = new TitleNode($directive->getDataNode(), 3, $this->getName());
58+
$title->setClasses(['card-title']);
59+
}
60+
61+
$originalChildren = $collectionNode->getChildren();
62+
$children = [];
63+
$header = null;
64+
$image = null;
65+
$footer = null;
66+
foreach ($originalChildren as $child) {
67+
if ($child instanceof CardHeaderNode) {
68+
$header = $child;
69+
} elseif ($child instanceof CardImageNode) {
70+
$image = $child;
71+
} elseif ($child instanceof CardFooterNode) {
72+
$footer = $child;
73+
} else {
74+
$children[] = $child;
75+
}
76+
}
77+
78+
$id = '';
79+
if ($directive->hasOption('name')) {
80+
$id = $directive->getOption('name')->toString();
81+
}
82+
83+
$id = $this->anchorReducer->reduceAnchor($id);
84+
85+
return new CardNode(
86+
$this->getName(),
87+
$directive->getData(),
88+
$directive->getDataNode() ?? new InlineCompoundNode(),
89+
$title,
90+
$children,
91+
$header,
92+
$image,
93+
$footer,
94+
$id,
95+
);
96+
}
97+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
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\Bootstrap\Directives;
15+
16+
use phpDocumentor\Guides\Bootstrap\Nodes\Card\CardFooterNode;
17+
use phpDocumentor\Guides\Nodes\CollectionNode;
18+
use phpDocumentor\Guides\Nodes\InlineCompoundNode;
19+
use phpDocumentor\Guides\Nodes\Node;
20+
use phpDocumentor\Guides\RestructuredText\Directives\SubDirective;
21+
use phpDocumentor\Guides\RestructuredText\Parser\BlockContext;
22+
use phpDocumentor\Guides\RestructuredText\Parser\Directive;
23+
24+
class CardFooterDirective extends SubDirective
25+
{
26+
public function getName(): string
27+
{
28+
return 'card-footer';
29+
}
30+
31+
protected function processSub(
32+
BlockContext $blockContext,
33+
CollectionNode $collectionNode,
34+
Directive $directive,
35+
): Node|null {
36+
return new CardFooterNode(
37+
$this->getName(),
38+
$directive->getData(),
39+
$directive->getDataNode() ?? new InlineCompoundNode(),
40+
);
41+
}
42+
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
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\Bootstrap\Directives;
15+
16+
use phpDocumentor\Guides\Bootstrap\Nodes\CardGridNode;
17+
use phpDocumentor\Guides\Bootstrap\Nodes\CardNode;
18+
use phpDocumentor\Guides\Nodes\CollectionNode;
19+
use phpDocumentor\Guides\Nodes\InlineCompoundNode;
20+
use phpDocumentor\Guides\Nodes\Node;
21+
use phpDocumentor\Guides\RestructuredText\Directives\SubDirective;
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 intval;
28+
29+
class CardGridDirective extends SubDirective
30+
{
31+
public function __construct(
32+
protected Rule $startingRule,
33+
private readonly LoggerInterface $logger,
34+
) {
35+
parent::__construct($startingRule);
36+
}
37+
38+
public function getName(): string
39+
{
40+
return 'card-grid';
41+
}
42+
43+
protected function processSub(
44+
BlockContext $blockContext,
45+
CollectionNode $collectionNode,
46+
Directive $directive,
47+
): Node|null {
48+
$title = null;
49+
$originalChildren = $collectionNode->getChildren();
50+
$children = [];
51+
$cardHeight = intval($directive->getOption('card-height')->getValue());
52+
foreach ($originalChildren as $child) {
53+
if ($child instanceof CardNode) {
54+
$children[] = $child;
55+
if ($cardHeight > 0) {
56+
$child->setCardHeight($cardHeight);
57+
}
58+
} else {
59+
$this->logger->warning('A card-grid may only contain cards. ', $blockContext->getLoggerInformation());
60+
}
61+
}
62+
63+
return new CardGridNode(
64+
$this->getName(),
65+
$directive->getData(),
66+
$directive->getDataNode() ?? new InlineCompoundNode(),
67+
$children,
68+
intval($directive->getOption('columns')->getValue() ?? 0),
69+
intval($directive->getOption('columns-sm')->getValue() ?? 0),
70+
intval($directive->getOption('columns-md')->getValue() ?? 0),
71+
intval($directive->getOption('columns-lg')->getValue() ?? 0),
72+
intval($directive->getOption('columns-xl')->getValue() ?? 0),
73+
intval($directive->getOption('gap')->getValue() ?? 0),
74+
);
75+
}
76+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
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\Bootstrap\Directives;
15+
16+
use phpDocumentor\Guides\Bootstrap\Nodes\CardGroupNode;
17+
use phpDocumentor\Guides\Bootstrap\Nodes\CardNode;
18+
use phpDocumentor\Guides\Nodes\CollectionNode;
19+
use phpDocumentor\Guides\Nodes\InlineCompoundNode;
20+
use phpDocumentor\Guides\Nodes\Node;
21+
use phpDocumentor\Guides\RestructuredText\Directives\SubDirective;
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+
class CardGroupDirective extends SubDirective
28+
{
29+
public function __construct(
30+
protected Rule $startingRule,
31+
private readonly LoggerInterface $logger,
32+
) {
33+
parent::__construct($startingRule);
34+
}
35+
36+
public function getName(): string
37+
{
38+
return 'card-group';
39+
}
40+
41+
protected function processSub(
42+
BlockContext $blockContext,
43+
CollectionNode $collectionNode,
44+
Directive $directive,
45+
): Node|null {
46+
$title = null;
47+
$originalChildren = $collectionNode->getChildren();
48+
$children = [];
49+
foreach ($originalChildren as $child) {
50+
if ($child instanceof CardNode) {
51+
$children[] = $child;
52+
} else {
53+
$this->logger->warning('A card-group may only contain cards. ', $blockContext->getLoggerInformation());
54+
}
55+
}
56+
57+
return new CardGroupNode(
58+
$this->getName(),
59+
$directive->getData(),
60+
$directive->getDataNode() ?? new InlineCompoundNode(),
61+
$children,
62+
);
63+
}
64+
}

0 commit comments

Comments
 (0)