From f559e5d397b501f90a54ef2fb66f042f2b571f16 Mon Sep 17 00:00:00 2001 From: Dan Harrin Date: Fri, 13 Aug 2021 06:56:11 +0100 Subject: [PATCH 01/12] Update slot pattern --- .../View/Compilers/ComponentTagCompiler.php | 37 ++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/src/Illuminate/View/Compilers/ComponentTagCompiler.php b/src/Illuminate/View/Compilers/ComponentTagCompiler.php index a69a704ec420..d91b12110403 100644 --- a/src/Illuminate/View/Compilers/ComponentTagCompiler.php +++ b/src/Illuminate/View/Compilers/ComponentTagCompiler.php @@ -395,7 +395,42 @@ protected function compileClosingTags(string $value) */ public function compileSlots(string $value) { - $value = preg_replace_callback('/<\s*x[\-\:]slot\s+(:?)name=(?(\"[^\"]+\"|\\\'[^\\\']+\\\'|[^\s>]+))\s*>/', function ($matches) { + $pattern = "/ + < + \s* + x[\-\:]slot + \s+ + (:?)name=(?(\"[^\"]+\"|\\\'[^\\\']+\\\'|[^\s>]+)) + (? + (?: + \s+ + (?: + (?: + \{\{\s*\\\$attributes(?:[^}]+?)?\s*\}\} + ) + | + (?: + [\w\-:.@]+ + ( + = + (?: + \\\"[^\\\"]*\\\" + | + \'[^\']*\' + | + [^\'\\\"=<>]+ + ) + )? + ) + ) + )* + \s* + ) + (? + /x"; + + $value = preg_replace_callback($pattern, function ($matches) { $name = $this->stripQuotes($matches['name']); if ($matches[1] !== ':') { From 0a7a97f2016402f33752d73a702cfd949f53891a Mon Sep 17 00:00:00 2001 From: Dan Harrin Date: Fri, 13 Aug 2021 08:50:19 +0100 Subject: [PATCH 02/12] Add attributes params to existing tests --- tests/View/Blade/BladeComponentTagCompilerTest.php | 4 ++-- tests/View/ViewFactoryTest.php | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/View/Blade/BladeComponentTagCompilerTest.php b/tests/View/Blade/BladeComponentTagCompilerTest.php index 1d65a715718d..586918b1de0e 100644 --- a/tests/View/Blade/BladeComponentTagCompilerTest.php +++ b/tests/View/Blade/BladeComponentTagCompilerTest.php @@ -23,7 +23,7 @@ public function testSlotsCanBeCompiled() $result = $this->compiler()->compileSlots(' '); - $this->assertSame("@slot('foo') \n".' @endslot', trim($result)); + $this->assertSame("@slot('foo', []) \n".' @endslot', trim($result)); } public function testDynamicSlotsCanBeCompiled() @@ -31,7 +31,7 @@ public function testDynamicSlotsCanBeCompiled() $result = $this->compiler()->compileSlots(' '); - $this->assertSame("@slot(\$foo) \n".' @endslot', trim($result)); + $this->assertSame("@slot(\$foo, []) \n".' @endslot', trim($result)); } public function testBasicComponentParsing() diff --git a/tests/View/ViewFactoryTest.php b/tests/View/ViewFactoryTest.php index 38c857573df6..c1c02599e6c1 100755 --- a/tests/View/ViewFactoryTest.php +++ b/tests/View/ViewFactoryTest.php @@ -355,7 +355,7 @@ public function testComponentHandling() $factory->getDispatcher()->shouldReceive('dispatch'); $factory->startComponent('component', ['name' => 'Taylor']); $factory->slot('title'); - $factory->slot('website', 'laravel.com'); + $factory->slot('website', [], 'laravel.com'); echo 'title
'; $factory->endSlot(); echo 'component'; @@ -371,7 +371,7 @@ public function testComponentHandlingUsingViewObject() $factory->getDispatcher()->shouldReceive('dispatch'); $factory->startComponent($factory->make('component'), ['name' => 'Taylor']); $factory->slot('title'); - $factory->slot('website', 'laravel.com'); + $factory->slot('website', [], 'laravel.com'); echo 'title
'; $factory->endSlot(); echo 'component'; @@ -392,7 +392,7 @@ public function testComponentHandlingUsingClosure() return $factory->make('component'); }, ['name' => 'Taylor']); $factory->slot('title'); - $factory->slot('website', 'laravel.com'); + $factory->slot('website', [], 'laravel.com'); echo 'title
'; $factory->endSlot(); echo 'component'; From bfe7655e3c1fc8350d7a3e2f5296050243aacd75 Mon Sep 17 00:00:00 2001 From: Dan Harrin Date: Fri, 13 Aug 2021 08:50:36 +0100 Subject: [PATCH 03/12] Create ComponentSlot class --- src/Illuminate/View/ComponentSlot.php | 88 +++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 src/Illuminate/View/ComponentSlot.php diff --git a/src/Illuminate/View/ComponentSlot.php b/src/Illuminate/View/ComponentSlot.php new file mode 100644 index 000000000000..b2c7684ca4a5 --- /dev/null +++ b/src/Illuminate/View/ComponentSlot.php @@ -0,0 +1,88 @@ +contents = $contents; + $this->withAttributes($attributes); + } + + /** + * Get the HTML string. + * + * @return string + */ + public function toHtml() + { + return $this->contents; + } + + /** + * Determine if the given slot is empty. + * + * @return bool + */ + public function isEmpty() + { + return $this->contents === ''; + } + + /** + * Determine if the given slot is not empty. + * + * @return bool + */ + public function isNotEmpty() + { + return ! $this->isEmpty(); + } + + /** + * Set the extra attributes that the slot should make available. + * + * @param array $attributes + * @return $this + */ + public function withAttributes(array $attributes) + { + $this->attributes = new ComponentAttributeBag($attributes); + + return $this; + } + + /** + * Get the HTML string. + * + * @return string + */ + public function __toString() + { + return $this->toHtml(); + } +} From 149f3d5722d022d79871065a3e666d2568926d88 Mon Sep 17 00:00:00 2001 From: Dan Harrin Date: Fri, 13 Aug 2021 08:50:59 +0100 Subject: [PATCH 04/12] Pass attributes from slot tag to Blade directive --- src/Illuminate/View/Compilers/ComponentTagCompiler.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Illuminate/View/Compilers/ComponentTagCompiler.php b/src/Illuminate/View/Compilers/ComponentTagCompiler.php index d91b12110403..b13ff4f5c860 100644 --- a/src/Illuminate/View/Compilers/ComponentTagCompiler.php +++ b/src/Illuminate/View/Compilers/ComponentTagCompiler.php @@ -437,7 +437,11 @@ public function compileSlots(string $value) $name = "'{$name}'"; } - return " @slot({$name}) "; + $this->boundAttributes = []; + + $attributes = $this->getAttributesFromAttributeString($matches['attributes']); + + return " @slot({$name}, [".$this->attributesToString($attributes, $escapeBound = false).']) '; }, $value); return preg_replace('/<\/\s*x[\-\:]slot[^>]*>/', ' @endslot', $value); From 8eab0904c1505f2cbf8f4962af582da3b8a29bc9 Mon Sep 17 00:00:00 2001 From: Dan Harrin Date: Fri, 13 Aug 2021 08:51:17 +0100 Subject: [PATCH 05/12] Compile slot attributes into slot object --- src/Illuminate/View/Concerns/ManagesComponents.php | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/Illuminate/View/Concerns/ManagesComponents.php b/src/Illuminate/View/Concerns/ManagesComponents.php index d720e64fade4..0986ea966416 100644 --- a/src/Illuminate/View/Concerns/ManagesComponents.php +++ b/src/Illuminate/View/Concerns/ManagesComponents.php @@ -6,6 +6,7 @@ use Illuminate\Contracts\View\View; use Illuminate\Support\Arr; use Illuminate\Support\HtmlString; +use Illuminate\View\ComponentSlot; use InvalidArgumentException; trait ManagesComponents @@ -119,21 +120,20 @@ protected function componentData() * Start the slot rendering process. * * @param string $name + * @param array $attributes * @param string|null $content * @return void * * @throws \InvalidArgumentException */ - public function slot($name, $content = null) + public function slot($name, $attributes = [], $content = null) { - if (func_num_args() > 2) { - throw new InvalidArgumentException('You passed too many arguments to the ['.$name.'] slot.'); - } elseif (func_num_args() === 2) { + if (func_num_args() === 3) { $this->slots[$this->currentComponent()][$name] = $content; } elseif (ob_start()) { $this->slots[$this->currentComponent()][$name] = ''; - $this->slotStack[$this->currentComponent()][] = $name; + $this->slotStack[$this->currentComponent()][] = [$name, $attributes]; } } @@ -150,7 +150,9 @@ public function endSlot() $this->slotStack[$this->currentComponent()] ); - $this->slots[$this->currentComponent()][$currentSlot] = new HtmlString(trim(ob_get_clean())); + [$currentSlotName, $currentSlotAttributes] = $currentSlot; + + $this->slots[$this->currentComponent()][$currentSlotName] = new ComponentSlot(trim(ob_get_clean()), $currentSlotAttributes); } /** From 6b5001da20574a463dd28b6bef85afb793da9e2b Mon Sep 17 00:00:00 2001 From: Dan Harrin Date: Fri, 13 Aug 2021 09:09:32 +0100 Subject: [PATCH 06/12] Add compilation tests for attribute support --- .../View/Blade/BladeComponentTagCompilerTest.php | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/View/Blade/BladeComponentTagCompilerTest.php b/tests/View/Blade/BladeComponentTagCompilerTest.php index 586918b1de0e..d59e1d48f563 100644 --- a/tests/View/Blade/BladeComponentTagCompilerTest.php +++ b/tests/View/Blade/BladeComponentTagCompilerTest.php @@ -34,6 +34,22 @@ public function testDynamicSlotsCanBeCompiled() $this->assertSame("@slot(\$foo, []) \n".' @endslot', trim($result)); } + public function testSlotsWithAttributesCanBeCompiled() + { + $result = $this->compiler()->compileSlots(' +'); + + $this->assertSame("@slot('foo', ['class' => 'font-bold']) \n".' @endslot', trim($result)); + } + + public function testSlotsWithDynamicAttributesCanBeCompiled() + { + $result = $this->compiler()->compileSlots(' +'); + + $this->assertSame("@slot('foo', ['class' => \$classes]) \n".' @endslot', trim($result)); + } + public function testBasicComponentParsing() { $this->mockViewFactory(); From 2bc6a1be46433e83f184058fd21261cdd804d966 Mon Sep 17 00:00:00 2001 From: Dan Harrin Date: Fri, 13 Aug 2021 10:20:07 +0100 Subject: [PATCH 07/12] Remove unused exception --- src/Illuminate/View/Concerns/ManagesComponents.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/Illuminate/View/Concerns/ManagesComponents.php b/src/Illuminate/View/Concerns/ManagesComponents.php index 0986ea966416..9e6597a6f9d8 100644 --- a/src/Illuminate/View/Concerns/ManagesComponents.php +++ b/src/Illuminate/View/Concerns/ManagesComponents.php @@ -7,7 +7,6 @@ use Illuminate\Support\Arr; use Illuminate\Support\HtmlString; use Illuminate\View\ComponentSlot; -use InvalidArgumentException; trait ManagesComponents { @@ -123,8 +122,6 @@ protected function componentData() * @param array $attributes * @param string|null $content * @return void - * - * @throws \InvalidArgumentException */ public function slot($name, $attributes = [], $content = null) { From 518c61a1d08ea6f0e67900bb8ac38cf100f5c413 Mon Sep 17 00:00:00 2001 From: Dan Harrin Date: Fri, 13 Aug 2021 12:08:58 +0100 Subject: [PATCH 08/12] Reorder arguments --- src/Illuminate/View/Compilers/ComponentTagCompiler.php | 2 +- src/Illuminate/View/Concerns/ManagesComponents.php | 4 ++-- tests/View/Blade/BladeComponentTagCompilerTest.php | 8 ++++---- tests/View/Blade/BladeComponentsTest.php | 2 +- tests/View/ViewFactoryTest.php | 6 +++--- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/Illuminate/View/Compilers/ComponentTagCompiler.php b/src/Illuminate/View/Compilers/ComponentTagCompiler.php index b13ff4f5c860..c198b32c74e5 100644 --- a/src/Illuminate/View/Compilers/ComponentTagCompiler.php +++ b/src/Illuminate/View/Compilers/ComponentTagCompiler.php @@ -441,7 +441,7 @@ public function compileSlots(string $value) $attributes = $this->getAttributesFromAttributeString($matches['attributes']); - return " @slot({$name}, [".$this->attributesToString($attributes, $escapeBound = false).']) '; + return " @slot({$name}, null, [".$this->attributesToString($attributes, $escapeBound = false).']) '; }, $value); return preg_replace('/<\/\s*x[\-\:]slot[^>]*>/', ' @endslot', $value); diff --git a/src/Illuminate/View/Concerns/ManagesComponents.php b/src/Illuminate/View/Concerns/ManagesComponents.php index 9e6597a6f9d8..24a18ae09cc0 100644 --- a/src/Illuminate/View/Concerns/ManagesComponents.php +++ b/src/Illuminate/View/Concerns/ManagesComponents.php @@ -123,9 +123,9 @@ protected function componentData() * @param string|null $content * @return void */ - public function slot($name, $attributes = [], $content = null) + public function slot($name, $content = null, $attributes = []) { - if (func_num_args() === 3) { + if ($content) { $this->slots[$this->currentComponent()][$name] = $content; } elseif (ob_start()) { $this->slots[$this->currentComponent()][$name] = ''; diff --git a/tests/View/Blade/BladeComponentTagCompilerTest.php b/tests/View/Blade/BladeComponentTagCompilerTest.php index d59e1d48f563..cd5242318434 100644 --- a/tests/View/Blade/BladeComponentTagCompilerTest.php +++ b/tests/View/Blade/BladeComponentTagCompilerTest.php @@ -23,7 +23,7 @@ public function testSlotsCanBeCompiled() $result = $this->compiler()->compileSlots(' '); - $this->assertSame("@slot('foo', []) \n".' @endslot', trim($result)); + $this->assertSame("@slot('foo', null, []) \n".' @endslot', trim($result)); } public function testDynamicSlotsCanBeCompiled() @@ -31,7 +31,7 @@ public function testDynamicSlotsCanBeCompiled() $result = $this->compiler()->compileSlots(' '); - $this->assertSame("@slot(\$foo, []) \n".' @endslot', trim($result)); + $this->assertSame("@slot(\$foo, null, []) \n".' @endslot', trim($result)); } public function testSlotsWithAttributesCanBeCompiled() @@ -39,7 +39,7 @@ public function testSlotsWithAttributesCanBeCompiled() $result = $this->compiler()->compileSlots(' '); - $this->assertSame("@slot('foo', ['class' => 'font-bold']) \n".' @endslot', trim($result)); + $this->assertSame("@slot('foo', null, ['class' => 'font-bold']) \n".' @endslot', trim($result)); } public function testSlotsWithDynamicAttributesCanBeCompiled() @@ -47,7 +47,7 @@ public function testSlotsWithDynamicAttributesCanBeCompiled() $result = $this->compiler()->compileSlots(' '); - $this->assertSame("@slot('foo', ['class' => \$classes]) \n".' @endslot', trim($result)); + $this->assertSame("@slot('foo', null, ['class' => \$classes]) \n".' @endslot', trim($result)); } public function testBasicComponentParsing() diff --git a/tests/View/Blade/BladeComponentsTest.php b/tests/View/Blade/BladeComponentsTest.php index 8aefd61a341f..8f7925f3e6d0 100644 --- a/tests/View/Blade/BladeComponentsTest.php +++ b/tests/View/Blade/BladeComponentsTest.php @@ -44,7 +44,7 @@ public function testEndComponentClassesAreCompiled() public function testSlotsAreCompiled() { - $this->assertSame('slot(\'foo\', ["foo" => "bar"]); ?>', $this->compiler->compileString('@slot(\'foo\', ["foo" => "bar"])')); + $this->assertSame('slot(\'foo\', null, ["foo" => "bar"]); ?>', $this->compiler->compileString('@slot(\'foo\', null, ["foo" => "bar"])')); $this->assertSame('slot(\'foo\'); ?>', $this->compiler->compileString('@slot(\'foo\')')); } diff --git a/tests/View/ViewFactoryTest.php b/tests/View/ViewFactoryTest.php index c1c02599e6c1..a3b2a316c2cd 100755 --- a/tests/View/ViewFactoryTest.php +++ b/tests/View/ViewFactoryTest.php @@ -355,7 +355,7 @@ public function testComponentHandling() $factory->getDispatcher()->shouldReceive('dispatch'); $factory->startComponent('component', ['name' => 'Taylor']); $factory->slot('title'); - $factory->slot('website', [], 'laravel.com'); + $factory->slot('website', 'laravel.com', []); echo 'title
'; $factory->endSlot(); echo 'component'; @@ -371,7 +371,7 @@ public function testComponentHandlingUsingViewObject() $factory->getDispatcher()->shouldReceive('dispatch'); $factory->startComponent($factory->make('component'), ['name' => 'Taylor']); $factory->slot('title'); - $factory->slot('website', [], 'laravel.com'); + $factory->slot('website', 'laravel.com', []); echo 'title
'; $factory->endSlot(); echo 'component'; @@ -392,7 +392,7 @@ public function testComponentHandlingUsingClosure() return $factory->make('component'); }, ['name' => 'Taylor']); $factory->slot('title'); - $factory->slot('website', [], 'laravel.com'); + $factory->slot('website', 'laravel.com', []); echo 'title
'; $factory->endSlot(); echo 'component'; From 66f7dae1b856fcc319890a240426fc5e6712bb3a Mon Sep 17 00:00:00 2001 From: Dan Harrin Date: Sat, 14 Aug 2021 18:07:10 +0100 Subject: [PATCH 09/12] Fix dynamic components with slot attributes --- src/Illuminate/View/DynamicComponent.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Illuminate/View/DynamicComponent.php b/src/Illuminate/View/DynamicComponent.php index 23ed305a336d..cea66e77b304 100644 --- a/src/Illuminate/View/DynamicComponent.php +++ b/src/Illuminate/View/DynamicComponent.php @@ -120,7 +120,7 @@ protected function compileBindings(array $bindings) protected function compileSlots(array $slots) { return collect($slots)->map(function ($slot, $name) { - return $name === '__default' ? null : '{{ $'.$name.' }}'; + return $name === '__default' ? null : 'attributes).'>{{ $'.$name.' }}'; })->filter()->implode(PHP_EOL); } From e6253a2d0dbcd7daa0c360ba9111b2bd00ec3701 Mon Sep 17 00:00:00 2001 From: Dan Harrin Date: Mon, 16 Aug 2021 16:28:24 +0100 Subject: [PATCH 10/12] Escape bound attributes for slots --- src/Illuminate/View/Compilers/ComponentTagCompiler.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Illuminate/View/Compilers/ComponentTagCompiler.php b/src/Illuminate/View/Compilers/ComponentTagCompiler.php index c198b32c74e5..d54d297785a9 100644 --- a/src/Illuminate/View/Compilers/ComponentTagCompiler.php +++ b/src/Illuminate/View/Compilers/ComponentTagCompiler.php @@ -441,7 +441,7 @@ public function compileSlots(string $value) $attributes = $this->getAttributesFromAttributeString($matches['attributes']); - return " @slot({$name}, null, [".$this->attributesToString($attributes, $escapeBound = false).']) '; + return " @slot({$name}, null, [".$this->attributesToString($attributes).']) '; }, $value); return preg_replace('/<\/\s*x[\-\:]slot[^>]*>/', ' @endslot', $value); From b830dcbef9af677456b547659d7d6040de2f9d34 Mon Sep 17 00:00:00 2001 From: Dan Harrin Date: Mon, 16 Aug 2021 16:35:43 +0100 Subject: [PATCH 11/12] Update BladeComponentTagCompilerTest.php --- tests/View/Blade/BladeComponentTagCompilerTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/View/Blade/BladeComponentTagCompilerTest.php b/tests/View/Blade/BladeComponentTagCompilerTest.php index cd5242318434..3a5bc700ebb2 100644 --- a/tests/View/Blade/BladeComponentTagCompilerTest.php +++ b/tests/View/Blade/BladeComponentTagCompilerTest.php @@ -47,7 +47,7 @@ public function testSlotsWithDynamicAttributesCanBeCompiled() $result = $this->compiler()->compileSlots(' '); - $this->assertSame("@slot('foo', null, ['class' => \$classes]) \n".' @endslot', trim($result)); + $this->assertSame("@slot('foo', null, ['class' => \Illuminate\View\Compilers\BladeCompiler::sanitizeComponentAttribute(\$classes)]) \n".' @endslot', trim($result)); } public function testBasicComponentParsing() From 5f43d187eb29d3cf86aa70bec4643470e0293e2e Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 18 Aug 2021 08:31:19 -0500 Subject: [PATCH 12/12] formattinG --- src/Illuminate/View/ComponentSlot.php | 35 ++++++++++--------- .../View/Concerns/ManagesComponents.php | 8 +++-- 2 files changed, 23 insertions(+), 20 deletions(-) diff --git a/src/Illuminate/View/ComponentSlot.php b/src/Illuminate/View/ComponentSlot.php index b2c7684ca4a5..85665ad64575 100644 --- a/src/Illuminate/View/ComponentSlot.php +++ b/src/Illuminate/View/ComponentSlot.php @@ -30,11 +30,25 @@ class ComponentSlot implements Htmlable public function __construct($contents = '', $attributes = []) { $this->contents = $contents; + $this->withAttributes($attributes); } /** - * Get the HTML string. + * Set the extra attributes that the slot should make available. + * + * @param array $attributes + * @return $this + */ + public function withAttributes(array $attributes) + { + $this->attributes = new ComponentAttributeBag($attributes); + + return $this; + } + + /** + * Get the slot's HTML string. * * @return string */ @@ -44,7 +58,7 @@ public function toHtml() } /** - * Determine if the given slot is empty. + * Determine if the slot is empty. * * @return bool */ @@ -54,7 +68,7 @@ public function isEmpty() } /** - * Determine if the given slot is not empty. + * Determine if the slot is not empty. * * @return bool */ @@ -64,20 +78,7 @@ public function isNotEmpty() } /** - * Set the extra attributes that the slot should make available. - * - * @param array $attributes - * @return $this - */ - public function withAttributes(array $attributes) - { - $this->attributes = new ComponentAttributeBag($attributes); - - return $this; - } - - /** - * Get the HTML string. + * Get the slot's HTML string. * * @return string */ diff --git a/src/Illuminate/View/Concerns/ManagesComponents.php b/src/Illuminate/View/Concerns/ManagesComponents.php index 24a18ae09cc0..9febac86b0c3 100644 --- a/src/Illuminate/View/Concerns/ManagesComponents.php +++ b/src/Illuminate/View/Concerns/ManagesComponents.php @@ -119,8 +119,8 @@ protected function componentData() * Start the slot rendering process. * * @param string $name - * @param array $attributes * @param string|null $content + * @param array $attributes * @return void */ public function slot($name, $content = null, $attributes = []) @@ -147,9 +147,11 @@ public function endSlot() $this->slotStack[$this->currentComponent()] ); - [$currentSlotName, $currentSlotAttributes] = $currentSlot; + [$currentName, $currentAttributes] = $currentSlot; - $this->slots[$this->currentComponent()][$currentSlotName] = new ComponentSlot(trim(ob_get_clean()), $currentSlotAttributes); + $this->slots[$this->currentComponent()][$currentName] = new ComponentSlot( + trim(ob_get_clean()), $currentAttributes + ); } /**