Skip to content

Commit 3b6a11b

Browse files
committed
[Site] Rename CodePreview_Tabs to Toolkit_Tabs, extract Toolkit's documentation tabs rendering into ToolkitService
1 parent 06b78be commit 3b6a11b

File tree

5 files changed

+70
-48
lines changed

5 files changed

+70
-48
lines changed

ux.symfony.com/assets/styles/app.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,6 @@ $utilities: map-remove(
128128
@import "components/Button";
129129
@import "components/Browser";
130130
@import "components/Changelog";
131-
@import "components/CodePreview_Tabs";
132131
@import "components/DataList";
133132
@import "components/DemoContainer";
134133
@import "components/DemoCard";
@@ -153,6 +152,7 @@ $utilities: map-remove(
153152
@import "components/Terminal";
154153
@import "components/TerminalCommand";
155154
@import "components/ThemeSwitcher";
155+
@import "components/Toolkit_Tabs";
156156
@import "components/Wysiwyg";
157157

158158
// Utilities

ux.symfony.com/assets/styles/components/_CodePreview_Tabs.scss renamed to ux.symfony.com/assets/styles/components/_Toolkit_Tabs.scss

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
1-
.CodePreview_Tabs {
1+
.Toolkit_Tabs {
22
}
33

4-
.CodePreview_TabHead {
4+
.Toolkit_TabHead {
55
display: flex;
66
flex-direction: row;
77
margin-bottom: 1rem
88
}
99

10-
.CodePreview_TabControl {
10+
.Toolkit_TabControl {
1111
border-bottom: 3px solid transparent;
1212
color: var(--bs-primary-color);
1313
padding: 0 1rem;
@@ -18,24 +18,24 @@
1818
margin-bottom: -1px;
1919
}
2020

21-
.CodePreview_TabControl.active {
21+
.Toolkit_TabControl.active {
2222
border-color: var(--bs-secondary-color);
2323
}
2424

25-
.CodePreview_TabPanel {
25+
.Toolkit_TabPanel {
2626
position: relative;
2727
}
2828

29-
.CodePreview_TabPanel:not(.active) {
29+
.Toolkit_TabPanel:not(.active) {
3030
display: none;
3131
}
3232

33-
.CodePreview_TabPanel:has(.CodePreview_Preview) {
33+
.Toolkit_TabPanel:has(.Toolkit_Preview) {
3434
border: 1px solid var(--bs-border-color);
3535
border-radius: .75rem
3636
}
3737

38-
.CodePreview_Loader {
38+
.Toolkit_Loader {
3939
width: 100%;
4040
display: flex;
4141
justify-content: center;
@@ -48,7 +48,7 @@
4848
}
4949
}
5050

51-
.CodePreview_Preview {
51+
.Toolkit_Preview {
5252
width: 100%;
5353
transition: opacity .250s linear;
5454
border-radius: .75rem;

ux.symfony.com/src/Service/CommonMark/ConverterFactory.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace App\Service\CommonMark;
1313

1414
use App\Service\CommonMark\Extension\CodeBlockRenderer\CodeBlockRenderer;
15+
use App\Service\Toolkit\ToolkitService;
1516
use League\CommonMark\CommonMarkConverter;
1617
use League\CommonMark\Extension\CommonMark\Node\Block\FencedCode;
1718
use League\CommonMark\Extension\ExternalLink\ExternalLinkExtension;
@@ -28,8 +29,7 @@
2829
final class ConverterFactory
2930
{
3031
public function __construct(
31-
private readonly UrlGeneratorInterface $urlGenerator,
32-
private readonly UriSigner $uriSigner,
32+
private readonly ToolkitService $toolkitService,
3333
) {
3434
}
3535

@@ -57,7 +57,7 @@ public function __invoke(): CommonMarkConverter
5757
->addExtension(new ExternalLinkExtension())
5858
->addExtension(new MentionExtension())
5959
->addExtension(new FrontMatterExtension())
60-
->addRenderer(FencedCode::class, new CodeBlockRenderer($this->urlGenerator, $this->uriSigner))
60+
->addRenderer(FencedCode::class, new CodeBlockRenderer($this->toolkitService))
6161
;
6262

6363
return $converter;

ux.symfony.com/src/Service/CommonMark/Extension/CodeBlockRenderer/CodeBlockRenderer.php

Lines changed: 7 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -11,20 +11,19 @@
1111

1212
namespace App\Service\CommonMark\Extension\CodeBlockRenderer;
1313

14+
use App\Enum\ToolkitKitId;
15+
use App\Service\Toolkit\ToolkitService;
1416
use League\CommonMark\Extension\CommonMark\Node\Block\FencedCode;
1517
use League\CommonMark\Node\Node;
1618
use League\CommonMark\Renderer\ChildNodeRendererInterface;
1719
use League\CommonMark\Renderer\NodeRendererInterface;
18-
use Symfony\Component\HttpFoundation\UriSigner;
19-
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
2020
use Tempest\Highlight\Highlighter;
2121
use Tempest\Highlight\WebTheme;
2222

2323
final readonly class CodeBlockRenderer implements NodeRendererInterface
2424
{
2525
public function __construct(
26-
private UrlGeneratorInterface $urlGenerator,
27-
private UriSigner $uriSigner,
26+
private ToolkitService $toolkitService,
2827
) {
2928
}
3029

@@ -38,39 +37,13 @@ public function render(Node $node, ChildNodeRendererInterface $childRenderer): \
3837
$infoWords = $node->getInfoWords();
3938
$language = $infoWords[0] ?? 'txt';
4039
$options = isset($infoWords[1]) && json_validate($infoWords[1]) ? json_decode($infoWords[1], true) : [];
40+
$kitId = ToolkitKitId::tryFrom($options['kit'] ?? null);
4141
$preview = $options['preview'] ?? false;
42-
$kit = $options['kit'] ?? null;
43-
$height = $options['height'] ?? '150px';
4442

45-
$code = $node->getLiteral();
43+
$output = $this->highlightCode($code = $node->getLiteral(), $language);
4644

47-
$output = $this->highlightCode($code, $language);
48-
49-
if ($preview && $kit) {
50-
$previewUrl = $this->uriSigner->sign($this->urlGenerator->generate('app_toolkit_component_preview', [
51-
'kitId' => $kit,
52-
'code' => $code,
53-
'height' => $height,
54-
], UrlGeneratorInterface::ABSOLUTE_URL));
55-
56-
$output = <<<HTML
57-
<div class="CodePreview_Tabs" data-controller="tabs" data-tabs-tab-value="preview" data-tabs-active-class="active">
58-
<nav class="CodePreview_TabHead" role="tablist" style="border-bottom: 1px solid var(--bs-border-color)">
59-
<button class="CodePreview_TabControl" data-action="tabs#show" data-tabs-target="control" data-tabs-tab-param="preview" role="tab" aria-selected="true">Preview</button>
60-
<button class="CodePreview_TabControl" data-action="tabs#show" data-tabs-target="control" data-tabs-tab-param="code" role="tab" aria-selected="false">Code</button>
61-
</nav>
62-
<div class="CodePreview_TabBody">
63-
<div class="CodePreview_TabPanel active" data-tabs-target="tab" data-tab="preview" role="tabpanel">
64-
<div class="CodePreview_Loader" style="height: {$height};">
65-
<svg width="18" height="18" viewBox="0 0 24 24"><path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 12a9 9 0 1 1-6.219-8.56"/></svg>
66-
<span>Loading...</span>
67-
</div>
68-
<iframe class="CodePreview_Preview loading" src="{$previewUrl}" style="height: {$height};" loading="lazy" onload="this.previousElementSibling.style.display = 'none'; this.classList.remove('loading')"></iframe>
69-
</div>
70-
<div class="CodePreview_TabPanel" data-tabs-target="tab" data-tab="code" role="tabpanel">{$output}</div>
71-
</div>
72-
</div>
73-
HTML;
45+
if ($kitId && $preview) {
46+
$output = $this->toolkitService->renderComponentPreviewCodeTabs($kitId, $code, $output, $options['height'] ?? '150px');
7447
}
7548

7649
return $output;

ux.symfony.com/src/Service/Toolkit/ToolkitService.php

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,20 @@
1313

1414
use App\Enum\ToolkitKitId;
1515
use Symfony\Component\DependencyInjection\Attribute\Autowire;
16+
use Symfony\Component\HttpFoundation\UriSigner;
17+
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
1618
use Symfony\UX\Toolkit\Asset\Component;
1719
use Symfony\UX\Toolkit\Kit\Kit;
1820
use Symfony\UX\Toolkit\Registry\RegistryFactory;
21+
use function Symfony\Component\String\s;
1922

2023
class ToolkitService
2124
{
2225
public function __construct(
2326
#[Autowire(service: 'ux_toolkit.registry.registry_factory')]
24-
private RegistryFactory $registryFactory,
27+
private readonly RegistryFactory $registryFactory,
28+
private readonly UriSigner $uriSigner,
29+
private readonly UrlGeneratorInterface $urlGenerator
2530
) {
2631
}
2732

@@ -54,4 +59,48 @@ public function getDocumentableComponents(Kit $kit): array
5459
{
5560
return array_filter($kit->getComponents(), fn (Component $component) => $component->doc);
5661
}
62+
63+
public function renderComponentPreviewCodeTabs(ToolkitKitId $kitId, string $code, string $highlightedCode, string $height): string
64+
{
65+
$previewUrl = $this->urlGenerator->generate('app_toolkit_component_preview', ['kitId' => $kitId->value, 'code' => $code, 'height' => $height], UrlGeneratorInterface::ABSOLUTE_URL);
66+
$previewUrl = $this->uriSigner->sign($previewUrl);
67+
68+
return self::generateTabs([
69+
'Preview' => <<<HTML
70+
<div class="Toolkit_Loader" style="height: {$height};">
71+
<svg width="18" height="18" viewBox="0 0 24 24"><path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 12a9 9 0 1 1-6.219-8.56"/></svg>
72+
<span>Loading...</span>
73+
</div>
74+
<iframe class="Toolkit_Preview loading" src="{$previewUrl}" style="height: {$height};" loading="lazy" onload="this.previousElementSibling.style.display = 'none'; this.classList.remove('loading')"></iframe>
75+
HTML,
76+
'Code' => $highlightedCode
77+
]);
78+
}
79+
80+
81+
/**
82+
* @param non-empty-array<string, string> $tabs
83+
*/
84+
private static function generateTabs(array $tabs): string
85+
{
86+
$activeTabId = null;
87+
$tabsControls = '';
88+
$tabsPanels = '';
89+
90+
foreach ($tabs as $tabText => $tabContent) {
91+
$tabId = hash('xxh3', $tabText);
92+
$activeTabId ??= $tabId;
93+
$isActive = $activeTabId === $tabId;
94+
95+
$tabsControls .= sprintf('<button class="Toolkit_TabControl" data-action="tabs#show" data-tabs-target="control" data-tabs-tab-param="%s" role="tab" aria-selected="%s">%s</button>', $tabId, $isActive ? 'true' : 'false', trim($tabText));
96+
$tabsPanels .= sprintf('<div class="Toolkit_TabPanel %s" data-tabs-target="tab" data-tab="%s" role="tabpanel">%s</div>', $isActive ? 'active' : '', $tabId, $tabContent);
97+
}
98+
99+
return <<<HTML
100+
<div class="Toolkit_Tabs" data-controller="tabs" data-tabs-tab-value="{$activeTabId}" data-tabs-active-class="active">
101+
<nav class="Toolkit_TabHead" role="tablist" style="border-bottom: 1px solid var(--bs-border-color)">{$tabsControls}</nav>
102+
<div class="Toolkit_TabBody">{$tabsPanels}</div>
103+
</div>
104+
HTML;
105+
}
57106
}

0 commit comments

Comments
 (0)