diff --git a/README.md b/README.md index 677e624..43fac3d 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ # Font Awesome Blade -Laravel [Blade Components](https://laravel.com/docs/8.x/blade#components) to make it dead simple to use Font Awesome SVG's in Laravel. +A collection of [Laravel Blade Components](https://laravel.com/docs/9.x/blade#components) to make it dead simple to use the [Font Awesome icons](https://fontawesome.com/v6/icons) in Laravel. # Prerequisites @@ -19,7 +19,9 @@ composer require devlop/fontawesome-blade # Configuration -By default this package will assume you are using the Pro versions, if you are using the free versions you can change this in the configuration. +By default this package will assume you are using the `@fortawesome/fontawesome-free`. + +If you are using any other version, publish the configuration and change the path accordingly. ``` php artisan vendor:publish --provider="Devlop\FontAwesome\FontAwesomeBladeServiceProvider" @@ -30,20 +32,20 @@ php artisan vendor:publish --provider="Devlop\FontAwesome\FontAwesomeBladeServic Each of Font Awesome's different styles are available as separate components, they all accept the same `name` argument for specifying which icon to display. ```html - + - + - + - + - + - + ``` -You also need to import the Font Awesome styles into your SASS to get the icons to display identical to using the JS method. +You also need to import the Font Awesome CSS into your CSS build to get the icons to display like they would if you were using the Font Awesome JS method. ```scss @import '@fortawesome/fontawesome-pro/css/svg-with-js.css'; diff --git a/UPGRADING.md b/UPGRADING.md new file mode 100644 index 0000000..ee90710 --- /dev/null +++ b/UPGRADING.md @@ -0,0 +1,6 @@ +# From v1 to v2 + +- The config file have been renamed from `fontawesome-blade.php` to `fontawesome.php` (the old config file will still work until v3). +- The package config option have been renamed from `package` to `path`, and expects the full path to the /svgs folder, and not only to the package root (the old config option `package` will still work until v3). +- The blade components are now namespaced as `fa`, use `` instead of the old `` (the old component will still work until v3). +- The default path now refers to `@fortawesome/fontawesome-free` instead of `@fortawesome/fontawesome-pro`, publish the config to change to your package of choice. diff --git a/config/fontawesome-blade.php b/config/fontawesome-blade.php deleted file mode 100644 index 4c91055..0000000 --- a/config/fontawesome-blade.php +++ /dev/null @@ -1,10 +0,0 @@ - base_path('node_modules/@fortawesome/fontawesome-pro'), - -]; diff --git a/config/fontawesome.php b/config/fontawesome.php new file mode 100644 index 0000000..09c6cf2 --- /dev/null +++ b/config/fontawesome.php @@ -0,0 +1,10 @@ + base_path('node_modules/@fortawesome/fontawesome-free/svgs'), + +]; diff --git a/package-lock.json b/package-lock.json index 4d6ef0b..257767a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2,10 +2,16 @@ "requires": true, "lockfileVersion": 1, "dependencies": { + "@fortawesome/fontawesome-free": { + "version": "6.3.0", + "resolved": "https://npm.fontawesome.com/@fortawesome/fontawesome-free/-/6.3.0/fontawesome-free-6.3.0.tgz", + "integrity": "sha512-qVtd5i1Cc7cdrqnTWqTObKQHjPWAiRwjUPaXObaeNPcy7+WKxJumGBx66rfSFgK6LNpIasVKkEgW8oyf0tmPLA==", + "dev": true + }, "@fortawesome/fontawesome-pro": { - "version": "6.0.0-beta1", - "resolved": "https://npm.fontawesome.com/@fortawesome/fontawesome-pro/-/6.0.0-beta1/fontawesome-pro-6.0.0-beta1.tgz", - "integrity": "sha512-qn/HZ4I1xiGiWRUhZtO4TMZBwQ9p9Ku95IcwehqEnqQ3KWtg79gYVl5g4MZL/7JxW4bDmZzORc0Gc9MHRS111w==", + "version": "6.3.0", + "resolved": "https://npm.fontawesome.com/@fortawesome/fontawesome-pro/-/6.3.0/fontawesome-pro-6.3.0.tgz", + "integrity": "sha512-OAOIn+MLlPTlIE++NyRSHXFhJ+MQB3C0vTNTPqrMn0nukyZMtQexRFe0UIf+Ln40Pr9CqqncfO0o1OcGHzUAfQ==", "dev": true } } diff --git a/package.json b/package.json index 18516a1..cb1835f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,7 @@ { "private": true, "devDependencies": { - "@fortawesome/fontawesome-pro": "6.0.0-beta1" + "@fortawesome/fontawesome-free": "^6.0.0", + "@fortawesome/fontawesome-pro": "^6.0.0" } } diff --git a/src/Components/Brands.php b/src/Components/Brands.php new file mode 100644 index 0000000..d045774 --- /dev/null +++ b/src/Components/Brands.php @@ -0,0 +1,20 @@ +package = $package; - + $this->path = $path; $this->style = $style; - $this->name = $name; } /** * Get the view / contents that represent the component. */ - public function render() : View + final public function render() : View { - $svg = $this->getSvg($this->package, $this->style, $this->name); + $svg = $this->getSvg($this->path, $this->style, $this->name); $inlineSvgClasses = $this->getInlineSvgClasses($this->name, $svg['width'], $svg['height']); @@ -63,11 +56,11 @@ public function render() : View } /** - * Get the svg contents + * Get the svg contents. */ - private function getSvg(string $package, string $style, string $name) : array + private function getSvg(string $path, string $style, string $name) : array { - $path = $this->getSvgPath($package, $style, $name); + $path = $this->getSvgPath($path, $style, $name); $contents = file_get_contents($path); @@ -84,13 +77,13 @@ private function getSvg(string $package, string $style, string $name) : array } /** - * Get the path to the svg + * Get the path to the svg. */ - private function getSvgPath(string $package, string $style, string $name) : string + private function getSvgPath(string $path, string $style, string $name) : string { return sprintf( - '%1$s/svgs/%2$s/%3$s.svg', - rtrim($package, '/'), + '%1$s/%2$s/%3$s.svg', + rtrim($path, '/'), $style, Str::after($name, 'fa-'), ); @@ -171,8 +164,8 @@ private function getPathsRegex() : string } /** - * Get the css classes needed to display the svg inline - * This is meant to mimic how fa's js->svg works + * Get the CSS classes needed to display the svg inline. + * This is meant to mimic how fa's js->svg works. * * @link https://fontawesome.com/how-to-use/on-the-web/advanced/svg-javascript-core */ diff --git a/src/FontAwesomeBladeServiceProvider.php b/src/FontAwesomeBladeServiceProvider.php index 0643346..db12e5a 100644 --- a/src/FontAwesomeBladeServiceProvider.php +++ b/src/FontAwesomeBladeServiceProvider.php @@ -4,12 +4,12 @@ namespace Devlop\FontAwesome; -use Devlop\FontAwesome\Components\FaBrands; -use Devlop\FontAwesome\Components\FaDuotone; -use Devlop\FontAwesome\Components\FaLight; -use Devlop\FontAwesome\Components\FaRegular; -use Devlop\FontAwesome\Components\FaSolid; -use Devlop\FontAwesome\Components\FaThin; +use Devlop\FontAwesome\Components\Brands; +use Devlop\FontAwesome\Components\Duotone; +use Devlop\FontAwesome\Components\Light; +use Devlop\FontAwesome\Components\Regular; +use Devlop\FontAwesome\Components\Solid; +use Devlop\FontAwesome\Components\Thin; use Illuminate\Support\Facades\Blade; use Illuminate\Support\ServiceProvider; use RuntimeException; @@ -33,7 +33,7 @@ public function provides() : array */ public function register() : void { - $this->mergeConfigFrom($this->getConfigPath(), 'fontawesome-blade'); + $this->mergeConfigFrom($this->getConfigPath('fontawesome.php'), 'fontawesome'); } /** @@ -45,39 +45,54 @@ public function boot() : void $this->publishes( [ - $this->getConfigPath() => config_path('fontawesome-blade.php'), + $this->getConfigPath('fontawesome.php') => config_path('fontawesome.php'), ], 'config', ); - $config = $this->app['config']->get('fontawesome-blade'); + // first check legacy config, then load the new config. + $config = $this->app['config']->get('fontawesome-blade') ?? $this->app['config']->get('fontawesome'); - if (! is_string($config['package']) || $config['package'] === '') { - throw new RuntimeException('No Font Awesome package path configured.'); + $path = array_key_exists('package', $config) + ? rtrim($config['package'], '/') . '/svgs' + : $config['path']; + + if (! is_string($path)) { + throw new RuntimeException(sprintf( + 'fontawesome.path must be a string, %1$s given.', + get_debug_type($path), + )); + } + + if (! is_dir($path)) { + throw new RuntimeException(sprintf( + '"%1$s" is not a valid Font Awesome path.', + $path, + )); } + Blade::componentNamespace('Devlop\\FontAwesome\\Components', 'fa'); + + // legacy aliases Blade::components([ - FaSolid::class => 'fa.solid', - FaRegular::class => 'fa.regular', - FaLight::class => 'fa.light', - FaThin::class => 'fa.thin', - FaDuotone::class => 'fa.duotone', - FaBrands::class => 'fa.brands', + Solid::class => 'fa.solid', + Regular::class => 'fa.regular', + Light::class => 'fa.light', + Thin::class => 'fa.thin', + Duotone::class => 'fa.duotone', + Brands::class => 'fa.brands', ]); - $this->app->when(FaSolid::class)->needs('$package')->give($config['package']); - $this->app->when(FaRegular::class)->needs('$package')->give($config['package']); - $this->app->when(FaLight::class)->needs('$package')->give($config['package']); - $this->app->when(FaThin::class)->needs('$package')->give($config['package']); - $this->app->when(FaDuotone::class)->needs('$package')->give($config['package']); - $this->app->when(FaBrands::class)->needs('$package')->give($config['package']); + $this->app->when(Solid::class)->needs('$path')->give($path); + $this->app->when(Regular::class)->needs('$path')->give($path); + $this->app->when(Light::class)->needs('$path')->give($path); + $this->app->when(Thin::class)->needs('$path')->give($path); + $this->app->when(Duotone::class)->needs('$path')->give($path); + $this->app->when(Brands::class)->needs('$path')->give($path); } - /** - * Get the speedtrap config path - */ - private function getConfigPath() : string + private function getConfigPath(string $fileName) : string { - return __DIR__ . '/../config/fontawesome-blade.php'; + return realpath(__DIR__ . '/../config/' . $fileName); } } diff --git a/tests/ComponentsTest.php b/tests/ComponentsTest.php index 0d422fe..20dbb4c 100644 --- a/tests/ComponentsTest.php +++ b/tests/ComponentsTest.php @@ -4,14 +4,14 @@ namespace Devlop\FontAwesome\Tests; -use Devlop\FontAwesome\Components\FaBrands; -use Devlop\FontAwesome\Components\FaDuotone; -use Devlop\FontAwesome\Components\FaLight; -use Devlop\FontAwesome\Components\FaRegular; -use Devlop\FontAwesome\Components\FaSolid; -use Devlop\FontAwesome\Components\FaThin; +use Devlop\FontAwesome\Components\Brands; +use Devlop\FontAwesome\Components\Duotone; +use Devlop\FontAwesome\Components\Light; +use Devlop\FontAwesome\Components\Regular; +use Devlop\FontAwesome\Components\Solid; +use Devlop\FontAwesome\Components\Thin; +use Devlop\FontAwesome\FontAwesomeBaseComponent; use Devlop\FontAwesome\FontAwesomeBladeServiceProvider; -use Devlop\FontAwesome\FontAwesomeComponent; use Illuminate\Contracts\Foundation\Application; use Illuminate\Support\Collection; use Illuminate\Support\Str; @@ -39,49 +39,56 @@ protected function getPackageProviders($app) : array */ protected function getEnvironmentSetUp($app) : void { - $app['config']->set('fontawesome-blade.package', (function () : string { - return realpath(__DIR__ . '/../node_modules/@fortawesome/fontawesome-pro'); - })()); + $app['config']->set( + 'fontawesome.path', + realpath(__DIR__ . '/../node_modules/@fortawesome/fontawesome-pro/svgs'), + ); } /** - * Component data provider. + * (-pro) Component data provider. * * @return array> */ - public function components() : array + public static function components() : array { return [ - 'brands' => [FaBrands::class], - 'duotone' => [FaDuotone::class], - 'light' => [FaLight::class], - 'regular' => [FaRegular::class], - 'solid' => [FaSolid::class], - 'thin' => [FaThin::class], + 'brands' => [Brands::class], + 'duotone' => [Duotone::class], + 'light' => [Light::class], + 'regular' => [Regular::class], + 'solid' => [Solid::class], + 'thin' => [Thin::class], ]; } /** - * @test - * @dataProvider components + * (-pro and -free) Component data provider. * - * @param class-string $componentName - * @return void + * @return array> */ - public function style_components_are_instances_of_the_base_component(string $componentName) : void + public static function packageComponents() : array { - $this->assertInstanceOf( - FontAwesomeComponent::class, - $this->app->make($componentName, [ - 'name' => 'fa-laravel', - ]), - ); + $freePath = realpath(__DIR__ . '/../node_modules/@fortawesome/fontawesome-free/svgs'); + $proPath = realpath(__DIR__ . '/../node_modules/@fortawesome/fontawesome-pro/svgs'); + + return [ + 'free-brands' => [$freePath, Brands::class], + 'free-regular' => [$freePath, Regular::class], + 'free-solid' => [$freePath, Solid::class], + 'pro-brands' => [$proPath, Brands::class], + 'pro-duotone' => [$proPath, Duotone::class], + 'pro-light' => [$proPath, Light::class], + 'pro-regular' => [$proPath, Regular::class], + 'pro-solid' => [$proPath, Solid::class], + 'pro-thin' => [$proPath, Thin::class], + ]; } /** @test */ public function brands_buffer_can_be_rendered() : void { - $component = $this->app->make(FaBrands::class, [ + $component = $this->app->make(Brands::class, [ 'name' => 'buffer', ]); @@ -91,7 +98,7 @@ public function brands_buffer_can_be_rendered() : void /** @test */ public function duotone_zero_can_be_rendered() : void { - $component = $this->app->make(FaDuotone::class, [ + $component = $this->app->make(Duotone::class, [ 'name' => '0', ]); @@ -101,7 +108,7 @@ public function duotone_zero_can_be_rendered() : void /** @test */ public function duotone_360_degrees_can_be_rendered() : void { - $component = $this->app->make(FaDuotone::class, [ + $component = $this->app->make(Duotone::class, [ 'name' => '360-degrees', ]); @@ -111,7 +118,7 @@ public function duotone_360_degrees_can_be_rendered() : void /** @test */ public function duotone_user_crown_have_the_correct_classnames() : void { - $component = $this->app->make(FaDuotone::class, [ + $component = $this->app->make(Duotone::class, [ 'name' => 'user-crown', ]); @@ -123,37 +130,36 @@ public function duotone_user_crown_have_the_correct_classnames() : void /** * @test - * @dataProvider components + * @dataProvider packageComponents * * @param class-string $componentName - * @return void */ - public function all_icons_can_be_rendered(string $componentName) : void + public function all_icons_can_be_rendered(string $path, string $componentName) : void { - $package = $this->app['config']->get('fontawesome-blade.package'); - $style = Str::of(class_basename($componentName)) ->after('Fa') ->lower(); - $path = implode('/', [ - $package, - 'svgs', + $iconsPath = implode('/', [ + $path, $style, ]); - $icons = (new Collection(scandir($path))) + $icons = (new Collection(scandir($iconsPath))) ->filter(function (string $icon) : bool { - if (in_array($icon, ['.', '..'], true)) { - return false; - } + $ignore = [ + '.', + '..', + ]; - return true; + return ! in_array($icon, $ignore, true); }) + ->values() ->map(fn (string $icon) : string => Str::before($icon, '.svg')); foreach ($icons as $icon) { $component = $this->app->make($componentName, [ + 'path' => $path, 'name' => $icon, ]); @@ -166,7 +172,7 @@ public function all_icons_can_be_rendered(string $componentName) : void * * @link https://laracasts.com/discuss/channels/laravel/testing-blade-components */ - private function renderComponent(FontAwesomeComponent $component) : string + private function renderComponent(FontAwesomeBaseComponent $component) : string { return $component ->resolveView() diff --git a/tests/RenderTest.php b/tests/RenderTest.php new file mode 100644 index 0000000..f8936bc --- /dev/null +++ b/tests/RenderTest.php @@ -0,0 +1,113 @@ + + */ + protected function getPackageProviders($app) : array + { + return [ + FontAwesomeBladeServiceProvider::class, + ]; + } + + /** + * Define environment setup. + * + * @param Application $app + */ + protected function getEnvironmentSetUp($app) : void + { + $app['config']->set( + 'fontawesome.path', + realpath(__DIR__ . '/../node_modules/@fortawesome/fontawesome-pro/svgs'), + ); + } + + /** + * (-pro) Component data provider. + * + * @return array> + */ + public static function components() : array + { + return [ + 'fa::brands' => ['fa::brands', Brands::class], + 'fa::duotone' => ['fa::duotone', Duotone::class], + 'fa::light' => ['fa::light', Light::class], + 'fa::regular' => ['fa::regular', Regular::class], + 'fa::solid' => ['fa::solid', Solid::class], + 'fa::thin' => ['fa::thin', Thin::class], + // legacy + 'fa.brands' => ['fa.brands', Brands::class], + 'fa.duotone' => ['fa.duotone', Duotone::class], + 'fa.light' => ['fa.light', Light::class], + 'fa.regular' => ['fa.regular', Regular::class], + 'fa.solid' => ['fa.solid', Solid::class], + 'fa.thin' => ['fa.thin', Thin::class], + ]; + } + + /** + * @test + * @dataProvider components + * + * @param class-string $componentClassName + */ + public function all_icons_can_be_rendered(string $componentName, string $componentClassName) : void + { + $iconName = 'starship-freighter'; + $classNames = [ + 'fa-fw', + 'fa-2x', + ]; + + $bladeSnippet = sprintf( + '', + $componentName, + $iconName, + implode(' ', $classNames), + ); + + $expectedOutputs = [ + sprintf( + '$component = $__env->getContainer()->make(%1$s::class, [\'name\' => \'%2$s\']);', + $componentClassName, + $iconName, + ), + sprintf( + '$component->withName(\'%1$s\');', + $componentName, + ), + sprintf( + '$component->withAttributes([\'class\' => \'%1$s\']);', + implode(' ', $classNames), + ), + ]; + + $actualOutput = Blade::compileString($bladeSnippet); + + foreach ($expectedOutputs as $expectedOutput) { + $this->assertStringContainsString($expectedOutput, $actualOutput); + } + } +}