Skip to content

Commit c6312ae

Browse files
committed
add slots variables
1 parent 71ae7ad commit c6312ae

File tree

9 files changed

+160
-56
lines changed

9 files changed

+160
-56
lines changed

src/TwigComponent/src/Twig/SlotNode.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ public function compile(Compiler $compiler): void
4343
->write('ob_start();')
4444
->subcompile($this->getNode('body'))
4545
->write('$body = ob_get_clean();'.\PHP_EOL)
46-
->write("\$slots['$name'] = new ".ComponentSlot::class.'($body, ');
46+
->write("\$slotsStack['$name'] = new ".ComponentSlot::class.'($body, ');
4747

4848
if ($this->hasNode('variables')) {
4949
$compiler->subcompile($this->getNode('variables'));

src/TwigComponent/src/Twig/TwigComponentNode.php

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -41,16 +41,8 @@ public function compile(Compiler $compiler): void
4141
{
4242
$compiler->addDebugInfo($this);
4343

44-
$template = $compiler->getVarName();
45-
$compiler->write(sprintf('$%s = ', $template));
46-
$this->addGetTemplate($compiler);
47-
4844
$compiler
49-
->raw(';')
50-
->write(sprintf("if ($%s) {\n", $template))
5145
->write('$slotsStack = $slotsStack ?? [];'.\PHP_EOL)
52-
->write('$slotsStack[] = $slots ?? [];'.\PHP_EOL)
53-
->write('$slots = [];'.\PHP_EOL)
5446
;
5547

5648
if ($this->getAttribute('componentMetadata') instanceof ComponentMetadata) {
@@ -63,11 +55,15 @@ public function compile(Compiler $compiler): void
6355
->write('$slot = ob_get_clean();'.\PHP_EOL)
6456
;
6557

66-
$compiler->raw(sprintf('$%s->display(', $template));
58+
$compiler
59+
->write("\$slotsStack['content'] = new ".ComponentSlot::class." (\$slot);\n")
60+
;
61+
62+
$this->addGetTemplate($compiler);
63+
$compiler->raw('->display(');
6764

6865
$this->addTemplateArguments($compiler);
6966
$compiler->raw(");\n");
70-
$compiler->write("}\n");
7167
}
7268

7369
protected function addTemplateArguments(Compiler $compiler)
@@ -76,7 +72,6 @@ protected function addTemplateArguments(Compiler $compiler)
7672
->indent(1)
7773
->write("\n")
7874
->write("array_merge(\n")
79-
->write('$slots,'.\PHP_EOL)
8075
;
8176

8277
if ($this->getAttribute('componentMetadata') instanceof ComponentMetadata) {
@@ -85,8 +80,9 @@ protected function addTemplateArguments(Compiler $compiler)
8580

8681
$compiler
8782
->write('$context,[')
88-
->write("'slot' => new ".ComponentSlot::class." (\$slot),\n")
89-
->write("'attributes' => new ".AttributeBag::class.'(');
83+
->write("'slots' => \$slotsStack,")
84+
->write("'attributes' => new ".AttributeBag::class.'(')
85+
;
9086

9187
if ($this->hasNode('variables')) {
9288
$compiler->subcompile($this->getNode('variables'));

src/TwigComponent/src/Twig/TwigPreLexer.php

Lines changed: 85 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,12 @@ public function preLexComponents(string $input): string
7676
continue;
7777
}
7878

79+
if ('slot' === $componentName) {
80+
$output .= $this->consumeSlot($componentName);
81+
82+
continue;
83+
}
84+
7985
// if we're already inside a component,
8086
// *and* we've just found a new component, then we should try to
8187
// open the default block
@@ -95,9 +101,9 @@ public function preLexComponents(string $input): string
95101
// use the simpler component() format, so that the system doesn't think
96102
// this is an "embedded" component with blocks
97103
// see https://github.com/symfony/ux/issues/810
98-
$output .= "{{ component('{$componentName}'".($attributes ? ", { {$attributes} }" : '').') }}';
104+
$output .= "{% twig_component '{$componentName}'".($attributes ? " with { {$attributes} }" : '').' %}{% end_twig_component %}';
99105
} else {
100-
$output .= "{% component '{$componentName}'".($attributes ? " with { {$attributes} }" : '').' %}';
106+
$output .= "{% twig_component '{$componentName}'".($attributes ? " with { {$attributes} }" : '').' %}';
101107
}
102108

103109
continue;
@@ -121,7 +127,7 @@ public function preLexComponents(string $input): string
121127
$output .= '{% endblock %}';
122128
}
123129

124-
$output .= '{% endcomponent %}';
130+
$output .= '{% end_twig_component %}';
125131

126132
continue;
127133
}
@@ -410,6 +416,82 @@ private function consumeUntilEndBlock(): string
410416
return substr($this->input, $start, $this->position - $start);
411417
}
412418

419+
private function consumeSlot(string $componentName): string
420+
{
421+
$attributes = $this->consumeAttributes($componentName);
422+
$this->consume('>');
423+
424+
$slotName = '';
425+
foreach (explode(', ', $attributes) as $attr) {
426+
[$key, $value] = explode(': ', $attr);
427+
if ('name' === $key) {
428+
$slotName = trim($value, "'");
429+
break;
430+
}
431+
}
432+
433+
if (empty($slotName)) {
434+
throw new SyntaxError('Expected block name.', $this->line);
435+
}
436+
437+
$output = "{% slot {$slotName} %}";
438+
439+
$closingTag = '</twig:slot>';
440+
if (!$this->doesStringEventuallyExist($closingTag)) {
441+
throw new SyntaxError("Expected closing tag '{$closingTag}' for block '{$slotName}'.", $this->line);
442+
}
443+
$slotContents = $this->consumeUntilEndSlot();
444+
445+
$subLexer = new self($this->line);
446+
$output .= $subLexer->preLexComponents($slotContents);
447+
448+
$this->consume($closingTag);
449+
$output .= '{% endslot %}';
450+
451+
return $output;
452+
}
453+
454+
private function consumeUntilEndSlot(): string
455+
{
456+
$start = $this->position;
457+
458+
$depth = 1;
459+
while ($this->position < $this->length) {
460+
if ('</twig:slot' === substr($this->input, $this->position, 11)) {
461+
if (1 === $depth) {
462+
break;
463+
} else {
464+
--$depth;
465+
}
466+
}
467+
468+
if ('{% endslot %}' === substr($this->input, $this->position, 13)) {
469+
if (1 === $depth) {
470+
// in this case, we want to advanced ALL the way beyond the endblock
471+
$this->position += 14;
472+
break;
473+
} else {
474+
--$depth;
475+
}
476+
}
477+
478+
if ('<twig:slot' === substr($this->input, $this->position, 10)) {
479+
++$depth;
480+
}
481+
482+
if ('{% slot' === substr($this->input, $this->position, 7)) {
483+
++$depth;
484+
}
485+
486+
if ("\n" === $this->input[$this->position]) {
487+
++$this->line;
488+
}
489+
++$this->position;
490+
}
491+
492+
return substr($this->input, $start, $this->position - $start);
493+
}
494+
413495
private function consumeAttributeValue(string $quote): string
414496
{
415497
$parts = [];
Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
<div class='container' style='background: {{ color|default('blue') }}'>
2+
{% block header %}
3+
<p>You have an alert!</p>
4+
{% endblock %}
25
<div class='content'>
3-
{{ slot }}
6+
{{ slots.content }}
47
</div>
5-
{{ footer|default('')|raw }}
8+
{{ slots.footer|default('')|raw }}
69
</div>
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
<button {{ attributes.merge({ class: 'btn' })|raw }}>{{ slot }}</button>
1+
<button {{ attributes.merge({ class: 'btn' })|raw }}>{{ slots.content }}</button>
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
<div class='container warning'><twig:Button>{{ slot }}</twig:Button><p>Danger Zone</p></div>
1+
<div class='container warning'><twig:Button>{{ slots.content }}</twig:Button><p>Danger Zone</p></div>
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<twig:Alarm>
2+
{% block header %}
3+
{{ parent() }}
4+
<p>A new message</p>
5+
{% endblock %}
6+
<p>Hey!</p>
7+
<twig:slot name="footer">
8+
from @bob
9+
</twig:slot>
10+
</twig:Alarm>

src/TwigComponent/tests/Integration/TwigComponentExtensionTest.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,4 +98,13 @@ public function testCanRenderEmbeddedComponent(): void
9898
$this->assertStringContainsString('custom th (key)', $output);
9999
$this->assertStringContainsString('custom td (1)', $output);
100100
}
101+
102+
public function testCanRenderMixOfBlockAndSlot(): void
103+
{
104+
$output = self::getContainer()->get(Environment::class)->render('slot/render_mix_of_slot_and_blocks.html.twig');
105+
106+
$this->assertStringContainsString('<p>You have an alert!</p>', $output);
107+
$this->assertStringContainsString('Hey!', $output);
108+
$this->assertStringContainsString('from @bob', $output);
109+
}
101110
}

0 commit comments

Comments
 (0)