From 6b31fa784dc7ee6365de7cceddb5948ab50f4c8c Mon Sep 17 00:00:00 2001 From: Pascal Baljet Date: Sat, 20 Jan 2024 18:48:14 +0100 Subject: [PATCH] Added `vue` macro to `ComponentAttributeBag` --- app/tests/Unit/ComponentAttributeBagTest.php | 59 ++++++++++++++++++++ src/SpladeCoreServiceProvider.php | 39 +++++++++++++ 2 files changed, 98 insertions(+) create mode 100644 app/tests/Unit/ComponentAttributeBagTest.php diff --git a/app/tests/Unit/ComponentAttributeBagTest.php b/app/tests/Unit/ComponentAttributeBagTest.php new file mode 100644 index 0000000..8f900f6 --- /dev/null +++ b/app/tests/Unit/ComponentAttributeBagTest.php @@ -0,0 +1,59 @@ +vue($attribute, $value, $omitBlankValue, $escape) + ->toHtml(); + } + + /** @test */ + public function it_omits_blank_values_by_default() + { + $this->assertEmpty($this->attr('animation', '')); + $this->assertEmpty($this->attr('animation', null)); + $this->assertEmpty($this->attr('animation', [])); + } + + /** @test */ + public function it_doesnt_affect_non_vue_attributes() + { + $this->assertEquals('animation="default"', $this->attr('animation', 'default')); + } + + /** @test */ + public function it_rewrites_a_vue_event_shortcut_to_the_full_notation() + { + $this->assertEquals('v-on:click="doSomething"', $this->attr('@click', 'doSomething')); + $this->assertEquals('v-on:click="doSomething"', $this->attr('v-on:click', 'doSomething')); + } + + /** @test */ + public function it_rewrites_a_vue_binding_shortcut_to_the_full_notation() + { + $this->assertEquals('v-bind:animation="default"', $this->attr(':animation', 'default')); + $this->assertEquals('v-bind:animation="default"', $this->attr('v-bind:animation', 'default')); + } + + /** @test */ + public function it_rewrites_a_boolean_value_to_a_string() + { + $this->assertEquals('v-bind:animation="true"', $this->attr(':animation', true)); + $this->assertEquals('v-bind:animation="false"', $this->attr(':animation', false)); + } + + /** @test */ + public function it_can_bind_arrays() + { + $this->assertEquals('v-bind:animation="JSON.parse('[1,2,3]')"', $this->attr(':animation', [1, 2, 3])); + $this->assertEquals('v-bind:animation="JSON.parse('[true,false]')"', $this->attr(':animation', [true, false])); + $this->assertEquals('v-bind:animation="JSON.parse('[\u0022a\u0022,\u0022b\u0022]')"', $this->attr(':animation', ['a', 'b'])); + } +} diff --git a/src/SpladeCoreServiceProvider.php b/src/SpladeCoreServiceProvider.php index 00b8a50..5640d0d 100644 --- a/src/SpladeCoreServiceProvider.php +++ b/src/SpladeCoreServiceProvider.php @@ -8,8 +8,11 @@ use Illuminate\Support\Facades\Artisan; use Illuminate\Support\Facades\Event; use Illuminate\Support\Facades\Route; +use Illuminate\Support\Js; +use Illuminate\Support\Str; use Illuminate\View\Compilers\ComponentTagCompiler; use Illuminate\View\Component; +use Illuminate\View\ComponentAttributeBag; use Illuminate\View\DynamicComponent; use Illuminate\View\Engines\EngineResolver; use ProtoneMedia\SpladeCore\Commands\BuildComponents; @@ -45,6 +48,7 @@ public function packageRegistered() $this->registerComponentHelper(); $this->registerBladeEngine(); $this->registerFactory(); + $this->registerComponentAttributeBagMacro(); $this->app->singleton(SpladeCoreRequest::class, function (Application $app) { return new SpladeCoreRequest(fn () => $app['request']); @@ -160,4 +164,39 @@ public function packageBooted() } }); } + + protected function registerComponentAttributeBagMacro() + { + ComponentAttributeBag::macro('vue', function ($attribute, $value = null, bool $omitBlankValue = true, bool $escape = true) { + /** @var ComponentAttributeBag $this */ + if ($omitBlankValue && blank($value)) { + return $this; + } + + $isEvent = Str::startsWith($attribute, ['@', 'v-on:']); + $isBinding = Str::startsWith($attribute, [':', 'v-bind:']); + + if (! $isEvent && ! $isBinding) { + return $this->merge([$attribute => $value], $escape); + } + + foreach (['@', 'v-on:', ':', 'v-bind:'] as $modifier) { + if (Str::startsWith($attribute, $modifier)) { + $attribute = Str::substr($attribute, strlen($modifier)); + } + } + + $shortBindAttribute = ($isEvent ? '@' : ':').$attribute; + $fullBindAttribute = ($isEvent ? 'v-on:' : 'v-bind:').$attribute; + + return $this->unless($this->has($shortBindAttribute) || $this->has($fullBindAttribute), function () use ($fullBindAttribute, $value, $escape) { + if (is_array($value) || is_bool($value) || is_object($value)) { + $value = Js::from($value)->toHtml(); + } + + /** @var ComponentAttributeBag $this */ + return $this->merge([$fullBindAttribute => $value], $escape); + }); + }); + } }