Last active
November 9, 2025 17:11
-
-
Save sammyaxe/cb8e0475be6417257344962d192ac956 to your computer and use it in GitHub Desktop.
Import FontAwesome Pro Icons into Flux. Must have downloaded the Font Awesome icons as SVG (improved based on @wojt-janowski gist)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <?php | |
| declare(strict_types=1); | |
| namespace App\Console\Commands; | |
| use Illuminate\Console\Command; | |
| use Illuminate\Filesystem\Filesystem; | |
| use function Laravel\Prompts\confirm; | |
| use function Laravel\Prompts\error; | |
| use function Laravel\Prompts\info; | |
| use function Laravel\Prompts\intro; | |
| use function Laravel\Prompts\search; | |
| use function Laravel\Prompts\select; | |
| use function Laravel\Prompts\text; | |
| use function Laravel\Prompts\warning; | |
| class PublishFontAwesomeIconCommand extends Command | |
| { | |
| protected $signature = 'flux:fa-icon {icons?*} {--family=} {--manual : Manually type icon name instead of searching}'; | |
| protected $description = 'Import third-party icons from Font Awesome Pro.'; | |
| public function handle(): void | |
| { | |
| if (! $this->isFontAwesomeProInstalled()) { | |
| error('Font Awesome Pro is not installed.'); | |
| warning('Please install Font Awesome Pro package: npm install @fortawesome/fontawesome-pro'); | |
| return; | |
| } | |
| $availableFamilies = $this->getAvailableFamilies(); | |
| if (empty($availableFamilies)) { | |
| error('No Font Awesome icon families found in '.$this->getFontAwesomePath()); | |
| return; | |
| } | |
| $family = $this->option('family') ?: $this->selectFamily($availableFamilies); | |
| if (! in_array($family, $availableFamilies)) { | |
| error("Invalid family '{$family}'. Available families: ".implode(', ', $availableFamilies)); | |
| return; | |
| } | |
| if (count($icons = $this->argument('icons')) > 0) { | |
| foreach ($icons as $icon) { | |
| $this->publishIcon($icon, $family); | |
| } | |
| return; | |
| } | |
| intro('Need an icon not included in Heroicons?'); | |
| info('Search for the perfect icon at: https://fontawesome.com/icons'); | |
| info('Selected family: '.$family); | |
| prompt: | |
| if ($this->option('manual')) { | |
| $icon = text( | |
| label: 'Which icon would you like to import from Font Awesome Pro?', | |
| required: 'An icon name is required.', | |
| placeholder: 'e.g. arrow-left', | |
| ); | |
| } else { | |
| $icon = $this->searchIcon($family); | |
| } | |
| if ($icon) { | |
| $this->publishIcon($icon, $family); | |
| } | |
| if (confirm('Would you like to import another icon?')) { | |
| goto prompt; | |
| } | |
| } | |
| protected function isFontAwesomeProInstalled(): bool | |
| { | |
| $basePath = base_path(config('services.fontawesome.path')); | |
| return file_exists($basePath) && is_dir($basePath); | |
| } | |
| protected function getFontAwesomePath(): string | |
| { | |
| return base_path(config('services.fontawesome.path')); | |
| } | |
| protected function getAvailableFamilies(): array | |
| { | |
| $path = $this->getFontAwesomePath(); | |
| if (! is_dir($path)) { | |
| return []; | |
| } | |
| $directories = array_filter( | |
| scandir($path), | |
| fn ($item) => $item !== '.' && $item !== '..' && is_dir($path.'/'.$item) | |
| ); | |
| return array_values($directories); | |
| } | |
| protected function selectFamily(array $families): string | |
| { | |
| return select( | |
| label: 'Which icon family would you like to use?', | |
| options: $families, | |
| default: in_array('regular', $families) ? 'regular' : $families[0] | |
| ); | |
| } | |
| protected function getAvailableIcons(string $family): array | |
| { | |
| $path = $this->getFontAwesomePath().'/'.$family; | |
| if (! is_dir($path)) { | |
| return []; | |
| } | |
| $files = array_filter( | |
| scandir($path), | |
| fn ($item) => str_ends_with($item, '.svg') | |
| ); | |
| return array_map( | |
| fn ($file) => str_replace('.svg', '', $file), | |
| array_values($files) | |
| ); | |
| } | |
| protected function searchIcon(string $family): ?string | |
| { | |
| $icons = $this->getAvailableIcons($family); | |
| if (empty($icons)) { | |
| error('No icons found in '.$family.' family.'); | |
| return null; | |
| } | |
| info('Found '.count($icons).' icons in '.$family.' family.'); | |
| return search( | |
| label: 'Search for an icon', | |
| options: fn (string $value) => strlen($value) > 0 | |
| ? array_values(array_filter($icons, fn ($icon) => str_contains($icon, $value))) | |
| : $icons, | |
| placeholder: 'Type to search...', | |
| scroll: 15 | |
| ); | |
| } | |
| protected function publishIcon(string $icon, string $pack): void | |
| { | |
| $filePath = base_path(config('services.fontawesome.path'))."/{$pack}/{$icon}.svg"; | |
| if (! file_exists($filePath)) { | |
| error('Icon not found: '.$icon.' at path '.$filePath); | |
| return; | |
| } | |
| $svg = file_get_contents($filePath); | |
| $packDirectory = resource_path('views/flux/icon/'.$pack); | |
| (new Filesystem)->ensureDirectoryExists($packDirectory); | |
| $destinationAsFile = $packDirectory.'/'.$icon.'.blade.php'; | |
| file_put_contents($destinationAsFile, $this->generateIconBlade($svg)); | |
| info('Published icon: '.$destinationAsFile); | |
| info('Usage: <flux:icon.'.str_replace('/', '.', $pack.'.'.$icon).'" />'); | |
| } | |
| protected function generateIconBlade($svg) | |
| { | |
| $viewBox = '0 0 512 512'; // Default fallback | |
| if (preg_match('/viewBox="([^"]+)"/', $svg, $matches)) { | |
| $viewBox = $matches[1]; | |
| } | |
| $svg = str($svg) | |
| ->replaceMatches('/<svg.*?>/s', <<<SVG | |
| <svg | |
| {{ \$attributes->class(\$classes) }} | |
| data-flux-icon | |
| xmlns="http://www.w3.org/2000/svg" | |
| viewBox="{$viewBox}" | |
| fill="currentColor" | |
| stroke="none" | |
| aria-hidden="true" | |
| data-slot="icon" | |
| > | |
| SVG)->toString(); | |
| $stub = <<<'HTML' | |
| {{-- Credit: Font Awesome (https://fontawesome.com) --}} | |
| @props([ | |
| 'variant' => 'outline', | |
| ]) | |
| @php | |
| $classes = Flux::classes('shrink-0') | |
| ->add(match($variant) { | |
| 'outline' => '[:where(&)]:size-6', | |
| 'solid' => '[:where(&)]:size-6', | |
| 'mini' => '[:where(&)]:size-5', | |
| 'micro' => '[:where(&)]:size-4', | |
| }); | |
| @endphp | |
| [[INJECT:SVG]] | |
| HTML; | |
| return (string) str($stub)->replace('[[INJECT:SVG]]', $svg); | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // ...specify the path where fontawsome svgs can be found, in my case I used NPM to install fontawesome pro | |
| 'fontawesome' => [ | |
| 'path' => 'node_modules/@fortawesome/fontawesome-pro/svgs-full' | |
| ] |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment