Icons - v2.1.1

Architecture Overview

This document explains the architectural design of ArtisanPack UI Icons v2.0, including the adapter pattern implementation, integration with blade-ui-kit/blade-icons, and the hybrid registration system.

System Architecture

ArtisanPack UI Icons v2.0 is built on the adapter pattern, transforming from a hardcoded icon provider into an extensibility layer that integrates custom icon sets with Laravel's Blade component system.

High-Level Architecture

┌─────────────────────────────────────────────────────────────┐
│                 ArtisanPack UI Icons v2.0                  │
│                    (Extensibility Layer)                   │
├─────────────────────────────────────────────────────────────┤
│  Config-Based Registration  │  Event-Driven Registration   │
│  ┌─────────────────────────┐ │ ┌─────────────────────────┐  │
│  │ config/artisanpack/icons.php │ │ │ Third-Party Packages    │  │
│  │                         │ │ │ via Filter Hooks        │  │
│  └─────────────────────────┘ │ └─────────────────────────┘  │
├─────────────────────────────────────────────────────────────┤
│              IconsServiceProvider                     │
│               (Hybrid Registration)                         │
├─────────────────────────────────────────────────────────────┤
│                  blade-ui-kit/blade-icons                  │
│                 (SVG Component Rendering)                   │
├─────────────────────────────────────────────────────────────┤
│                    Laravel Blade Engine                     │
│                 (Component Resolution)                      │
└─────────────────────────────────────────────────────────────┘

Core Components

  1. Configuration System - Manages user-defined icon sets
  2. Event System - Enables third-party package integration
  3. Service Provider - Orchestrates registration and integration
  4. Adapter Layer - Bridges custom icon sets with blade-ui-kit
  5. Component System - Renders icons as Blade components

Adapter Pattern Implementation

The v2.0 architecture implements the adapter pattern to bridge the gap between custom icon sets and the standardized blade-ui-kit/blade-icons system.

Traditional Approach (v1.x)

// v1.x - Hardcoded approach
class Icons {
    protected $icons = [
        'home' => '<svg>...</svg>',
        'user' => '<svg>...</svg>',
        // 17,000+ hardcoded icons
    ];
}

Adapter Approach (v2.0)

// v2.0 - Adapter approach
class IconsServiceProvider {
    public function boot() {
        // Adapt custom icon sets to BladeIcons format
        $this->registerIcons();
    }
    
    protected function registerIcons() {
        $iconSets = $this->collectIconSets();
        
        foreach ($iconSets as $set) {
            BladeIcons::add($set['prefix'], [
                'path' => $set['path'],
                'prefix' => $set['prefix'],
            ]);
        }
    }
}

Benefits of Adapter Pattern

  • Flexibility - Use any SVG icon collection
  • Performance - No hardcoded arrays, minimal memory usage
  • Extensibility - Third-party packages can register icon sets
  • Maintainability - Clear separation of concerns
  • Standards Compliance - Leverages established blade-ui-kit patterns

Integration with blade-ui-kit/blade-icons

The package integrates deeply with blade-ui-kit/blade-icons to provide seamless SVG component rendering.

BladeIcons Factory Integration

use BladeUI\Icons\Factory as BladeIcons;

// Register icon set with BladeIcons factory
BladeIcons::add('prefix', [
    'path' => '/path/to/svg/files',
    'prefix' => 'prefix',
]);

// This enables components like:
// <x-icon-prefix-home />
// <x-icon-prefix-user />

Component Resolution Flow

1. Blade Template: <x-icon-fa-home class="w-6 h-6" />
                          ↓
2. Laravel Component Resolution
                          ↓
3. BladeIcons Factory Lookup (prefix: 'fa', name: 'home')
                          ↓
4. SVG File Loading (/path/to/fa/home.svg)
                          ↓
5. SVG Content Rendering with Attributes
                          ↓
6. Final HTML: <svg class="w-6 h-6">...</svg>

Deferred Registration

Icon registration is deferred until the BladeCompiler is resolved to ensure proper initialization order:

$this->app->callAfterResolving(BladeCompiler::class, function () {
    $this->registerIcons();
});

Hybrid Registration System

The v2.0 architecture implements a hybrid registration system that combines two registration methods.

Registration Sources

1. Config-Based Registration

  • Source: config/artisanpack/icons.php
  • Priority: High (overrides event-driven)
  • Use Case: User-configured icon sets
  • Control: Direct user control
// config/artisanpack/icons.php
return [
    'sets' => [
        ['path' => resource_path('icons/fa'), 'prefix' => 'fa'],
        ['path' => resource_path('icons/custom'), 'prefix' => 'custom'],
    ],
];

2. Event-Driven Registration

  • Source: Third-party packages via filter hooks
  • Priority: Low (config overrides)
  • Use Case: Package-provided icon sets
  • Control: Programmatic registration
// In a package service provider
addFilter('ap.icons.register-icon-sets', function ($sets) {
    $sets[] = new IconSetRegistration(
        path: __DIR__ . '/../../resources/icons',
        prefix: 'mypackage'
    );
    return $sets;
});

Registration Process

protected function collectIconSets(): array
{
    // 1. Collect event-driven registrations
    $eventSets = apply_filters('ap.icons.register-icon-sets', []);
    
    // 2. Get config-based registrations
    $configSets = config('artisanpack.icons.sets', []);
    
    // 3. Merge with config precedence
    $allSets = array_merge($eventSets, $configSets);
    
    // 4. Validate and filter
    return array_filter($allSets, [$this, 'validateIconSet']);
}

protected function validateIconSet(array $set): bool
{
    return isset($set['path']) && 
           isset($set['prefix']) && 
           is_dir($set['path']);
}

Configuration Reference

Configuration Structure

<?php
// config/artisanpack/icons.php

return [
    /*
    |--------------------------------------------------------------------------
    | Custom Icon Sets
    |--------------------------------------------------------------------------
    |
    | Register your custom SVG icon sets here. Each set requires:
    | - path: Absolute path to directory containing SVG files
    | - prefix: Unique prefix for Blade components (e.g., 'fa', 'hero')
    |
    */
    
    'sets' => [
        // Font Awesome Pro icons
        [
            'path' => resource_path('icons/fontawesome/solid'),
            'prefix' => 'fas',
        ],
        [
            'path' => resource_path('icons/fontawesome/regular'),
            'prefix' => 'far',
        ],
        [
            'path' => resource_path('icons/fontawesome/brands'),
            'prefix' => 'fab',
        ],
        
        // Heroicons
        [
            'path' => resource_path('icons/heroicons/outline'),
            'prefix' => 'hero-outline',
        ],
        [
            'path' => resource_path('icons/heroicons/solid'),
            'prefix' => 'hero-solid',
        ],
        
        // Custom application icons
        [
            'path' => resource_path('icons/custom'),
            'prefix' => 'custom',
        ],
    ],
];

Configuration Validation Rules

Each icon set must follow these rules:

  • path (string, required): Absolute path to directory containing SVG files
  • prefix (string, required): Unique prefix for Blade components (alphanumeric + hyphens)
  • Directory must exist: The path must point to an existing, readable directory
  • Contains SVG files: Directory should contain at least one .svg file

Invalid Configuration Examples

// ❌ Missing required keys
['path' => '/path/to/icons'], // Missing prefix

// ❌ Non-existent directory
['path' => '/non/existent/path', 'prefix' => 'test'],

// ❌ Invalid prefix characters
['path' => resource_path('icons'), 'prefix' => 'my_icons!'],

// ❌ Conflicting prefixes (will cause overwrites)
['path' => resource_path('icons/set1'), 'prefix' => 'fa'],
['path' => resource_path('icons/set2'), 'prefix' => 'fa'], // Conflict!

Performance Characteristics

Memory Usage

Aspect v1.x v2.0
Startup Memory ~50MB (17K+ icons) ~1KB (metadata only)
Per Icon Preloaded in memory Loaded on-demand
Total Reduction - 95%+ improvement

Loading Performance

v1.x Loading Process:
Application Start → Load 17K+ Icons → Memory Allocation → Ready
     ~500ms              ~2GB RAM

v2.0 Loading Process:
Application Start → Register Metadata → Ready → Load Icon on Use
     ~10ms              ~1KB RAM       ~5ms per icon

Caching Strategy

The v2.0 architecture leverages multiple caching layers:

  1. Configuration Caching - Laravel's config:cache caches merged configuration
  2. BladeIcons Caching - blade-ui-kit handles SVG content caching
  3. Blade Component Caching - Laravel caches compiled components
  4. Browser Caching - SVG content benefits from HTTP caching

Error Handling and Validation

Configuration Validation

protected function validateIconSet(array $set): bool
{
    // Required keys validation
    if (!isset($set['path']) || !isset($set['prefix'])) {
        $this->logWarning('Icon set missing required keys', $set);
        return false;
    }
    
    // Path existence validation
    if (!is_dir($set['path'])) {
        $this->logWarning("Icon directory not found: {$set['path']}", $set);
        return false;
    }
    
    // Path readability validation
    if (!is_readable($set['path'])) {
        $this->logWarning("Icon directory not readable: {$set['path']}", $set);
        return false;
    }
    
    // Prefix format validation
    if (!preg_match('/^[a-zA-Z0-9-]+$/', $set['prefix'])) {
        $this->logWarning("Invalid prefix format: {$set['prefix']}", $set);
        return false;
    }
    
    return true;
}

Runtime Error Handling

protected function registerIconSet(array $set): void
{
    try {
        BladeIcons::add($set['prefix'], $set);
    } catch (\Exception $e) {
        $this->logError("Failed to register icon set '{$set['prefix']}'", [
            'error' => $e->getMessage(),
            'set' => $set,
        ]);
    }
}

Troubleshooting Guide

Common Issues and Solutions

1. Icons Not Displaying

Symptoms:

  • Blank space where icon should be
  • Component not found errors
  • Invalid component name errors

Solutions:

// Check configuration
php artisan tinker
> config('artisanpack.icons.sets')

// Verify directory exists
> is_dir(resource_path('icons/fontawesome'))

// Check for SVG files
> glob(resource_path('icons/fontawesome') . '/*.svg')

// Clear caches
php artisan config:clear
php artisan view:clear

2. Prefix Conflicts

Symptoms:

  • Icons from one set override another
  • Unexpected icon rendering

Solutions:

// Check for duplicate prefixes in config
$sets = config('artisanpack.icons.sets');
$prefixes = array_column($sets, 'prefix');
$duplicates = array_diff_assoc($prefixes, array_unique($prefixes));

// Use unique prefixes
'prefix' => 'mypackage-admin' // Instead of generic 'admin'

3. Performance Issues

Symptoms:

  • Slow page loading
  • High memory usage
  • Long application startup time

Solutions:

// Check icon set sizes
foreach (config('artisanpack.icons.sets') as $set) {
    $count = count(glob($set['path'] . '/*.svg'));
    echo "Set '{$set['prefix']}': {$count} icons\n";
}

// Consider splitting large sets
// Instead of one huge set:
['path' => resource_path('icons/all'), 'prefix' => 'all'],

// Use multiple smaller sets:
['path' => resource_path('icons/navigation'), 'prefix' => 'nav'],
['path' => resource_path('icons/actions'), 'prefix' => 'action'],

4. Third-Party Integration Issues

Symptoms:

  • Package icons not appearing
  • Filter hooks not firing
  • Registration order conflicts

Solutions:

// Debug event-driven registration
addFilter('ap.icons.register-icon-sets', function (IconSetRegistration $registry) {
    $beforeCount = count($registry->getSets());
    \Log::debug('MyPackage registering icons', [
        'path' => __DIR__ . '/../../resources/icons',
        'prefix' => 'mypackage',
        'existing_sets_count' => $beforeCount
    ]);
    
    $registry->addSet(__DIR__ . '/../../resources/icons', 'mypackage');
    
    \Log::debug('Icon sets after MyPackage', ['count' => count($registry->getSets())]);
    return $registry;
});

FAQ

Q: Can I use multiple icon libraries together?

A: Yes, the hybrid system is designed for this. Each icon set uses a unique prefix, allowing you to mix Font Awesome, Heroicons, custom icons, etc.

Q: How do I handle icon naming conflicts?

A: Use descriptive prefixes. Instead of fa-home and hero-home, consider fa-solid-home and hero-outline-home.

Q: Can third-party packages override my config icons?

A: No, config-based registrations always take precedence over event-driven ones.

Q: What happens if an SVG file is malformed?

A: blade-ui-kit handles SVG parsing errors gracefully, typically rendering nothing or showing an error in development mode.

Q: How do I optimize for production?

A: Use php artisan config:cache, ensure SVG files are optimized, and consider using a CDN for icon assets.

Q: Can I register icons from cloud storage?

A: Currently, the system requires local file paths. Consider syncing cloud icons to local storage or extending the IconSetRegistration class to support remote paths.

Next Steps