Skip to content

Commit 9fb3dd4

Browse files
erikaraujobrendt
andauthored
fix(view): dynamic components with slots (#1171)
Co-authored-by: brendt <[email protected]>
1 parent 22dbe07 commit 9fb3dd4

File tree

6 files changed

+67
-33
lines changed

6 files changed

+67
-33
lines changed

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
"symfony/uid": "^7.1",
3535
"symfony/var-dumper": "^7.1",
3636
"symfony/var-exporter": "^7.1",
37-
"tempest/highlight": "^2.11.2",
37+
"tempest/highlight": "^2.11.4",
3838
"vlucas/phpdotenv": "^5.6",
3939
"voku/portable-ascii": "^2.0.3"
4040
},

src/Tempest/View/src/Components/DynamicViewComponent.php

Lines changed: 41 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,26 +2,23 @@
22

33
namespace Tempest\View\Components;
44

5-
use Tempest\Container\Container;
5+
use Stringable;
66
use Tempest\Core\AppConfig;
7-
use Tempest\View\Elements\ElementFactory;
7+
use Tempest\Support\Str\ImmutableString;
8+
use Tempest\View\Elements\CollectionElement;
89
use Tempest\View\Elements\ViewComponentElement;
9-
use Tempest\View\GenericView;
1010
use Tempest\View\Parser\TempestViewCompiler;
1111
use Tempest\View\Parser\Token;
1212
use Tempest\View\ViewComponent;
1313
use Tempest\View\ViewConfig;
14-
use Tempest\View\ViewRenderer;
14+
15+
use function Tempest\Support\arr;
1516

1617
final class DynamicViewComponent implements ViewComponent
1718
{
1819
private Token $token;
1920

20-
public function __construct(
21-
private AppConfig $appConfig,
22-
private TempestViewCompiler $compiler,
23-
private ViewConfig $viewConfig,
24-
) {}
21+
public function __construct() {}
2522

2623
public function setToken(Token $token): void
2724
{
@@ -36,27 +33,46 @@ public static function getName(): string
3633
public function compile(ViewComponentElement $element): string
3734
{
3835
$name = $this->token->getAttribute('is') ?? $this->token->getAttribute(':is');
36+
3937
$isExpression = $this->token->getAttribute(':is') !== null;
4038

41-
return sprintf(
42-
'<?php eval(\'?>\' . \Tempest\get(%s::class)->render(%s, %s)); ?>',
43-
self::class,
44-
$isExpression ? $name : "'{$name}'",
45-
var_export($element->getAttributes(), true), // @mago-expect best-practices/no-debug-symbols
46-
);
47-
}
39+
$collectionElement = new CollectionElement($element->getChildren());
4840

49-
public function render(string $name, array $attributes): string
50-
{
51-
$viewComponent = $this->viewConfig->viewComponents[$name] ?? null;
41+
$attributes = arr($element->getAttributes())
42+
->filter(fn (string $_value, string $key) => $key !== 'is' && $key !== ':is')
43+
->map(function (string $value, string $key) {
44+
return sprintf('%s="%s"', $key, trim($value));
45+
})
46+
->implode(' ')
47+
->when(
48+
fn (Stringable $string) => ((string) $string) !== '',
49+
fn (Stringable $string) => new ImmutableString(" {$string}"),
50+
);
5251

53-
$element = new ViewComponentElement(
54-
environment: $this->appConfig->environment,
55-
compiler: $this->compiler,
56-
viewComponent: $viewComponent,
57-
attributes: $attributes,
52+
$compiledChildren = sprintf(
53+
<<<'HTML'
54+
<%s%s>
55+
%s
56+
</%s>
57+
HTML,
58+
'%s',
59+
$attributes,
60+
$collectionElement->compile(),
61+
'%s',
5862
);
5963

60-
return $element->compile();
64+
return sprintf(
65+
'<?php
66+
$vars = get_defined_vars();
67+
unset($vars[\'_view\'], $vars[\'_path\'], $vars[\'_data\'], $vars[\'_propIsLocal\'], $vars[\'_isIsLocal\'], $vars[\'_previousAttributes\'], $vars[\'_previousSlots\'], $vars[\'slots\']);
68+
69+
echo \Tempest\get(' . \Tempest\View\Renderers\TempestViewRenderer::class . '::class)->render(\Tempest\view(sprintf(<<<\'HTML\'
70+
%s
71+
HTML, %s, %s), ...$vars)); ?>
72+
',
73+
$compiledChildren,
74+
$isExpression ? $name : "'{$name}'",
75+
$isExpression ? $name : "'{$name}'",
76+
);
6177
}
6278
}

src/Tempest/View/src/Parser/TempestViewCompiler.php

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@
99
use Tempest\Mapper\Exceptions\ViewNotFound;
1010
use Tempest\View\Attribute;
1111
use Tempest\View\Attributes\AttributeFactory;
12+
use Tempest\View\Components\DynamicViewComponent;
1213
use Tempest\View\Element;
1314
use Tempest\View\Elements\ElementFactory;
15+
use Tempest\View\Elements\ViewComponentElement;
1416
use Tempest\View\View;
1517

1618
use function Tempest\Support\arr;
@@ -130,13 +132,20 @@ private function applyAttributes(array $elements): array
130132
$previous = null;
131133

132134
foreach ($elements as $element) {
133-
$children = $this->applyAttributes($element->getChildren());
135+
$isDynamicViewComponent = $element instanceof ViewComponentElement && $element->getViewComponent() instanceof DynamicViewComponent;
134136

135-
$element
136-
->setPrevious($previous)
137-
->setChildren($children);
137+
if (! $isDynamicViewComponent) {
138+
$children = $this->applyAttributes($element->getChildren());
139+
$element->setChildren($children);
140+
}
141+
142+
$element->setPrevious($previous);
138143

139144
foreach ($element->getAttributes() as $name => $value) {
145+
if ($isDynamicViewComponent && $name !== ':is' && $name !== 'is') {
146+
continue;
147+
}
148+
140149
// TODO: possibly refactor attribute construction to ElementFactory?
141150
if ($value instanceof Attribute) {
142151
$attribute = $value;

src/Tempest/View/src/Renderers/TempestViewRenderer.php

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,6 @@
1717
use Tempest\View\ViewRenderer;
1818
use Throwable;
1919

20-
use function Tempest\Support\arr;
21-
use function Tempest\Support\str;
22-
2320
final class TempestViewRenderer implements ViewRenderer
2421
{
2522
private ?View $currentView = null;

src/Tempest/Vite/src/TagsResolver/ManifestTagsResolver.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,7 @@ function loadNext(assets, count) {
225225
setTimeout(() => loadNext({$assets}, {$this->viteConfig->prefetching->concurrent}))
226226
})
227227
JS,
228+
PrefetchStrategy::NONE => '',
228229
};
229230

230231
return $this->tagCompiler->compilePrefetchTag($script, $chunk);

tests/Integration/View/ViewComponentTest.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -825,6 +825,17 @@ public function test_dynamic_view_component_with_variable_attribute(): void
825825
$this->assertSame('<div>test</div>', $html);
826826
}
827827

828+
public function test_dynamic_view_component_with_slot(): void
829+
{
830+
$this->registerViewComponent('x-test', '<div><x-slot/></div>');
831+
832+
$html = $this->render(<<<'HTML'
833+
<x-dynamic-component :is="$name">test</x-dynamic-component>
834+
HTML, name: 'x-test');
835+
836+
$this->assertSnippetsMatch('<div>test</div>', $html);
837+
}
838+
828839
public function test_nested_slots(): void
829840
{
830841
$this->registerViewComponent('x-a', '<a><x-slot /></a>');

0 commit comments

Comments
 (0)