diff --git a/src/MprIcons.php b/src/MprIcons.php index 78c75bb..500605b 100644 --- a/src/MprIcons.php +++ b/src/MprIcons.php @@ -29,6 +29,7 @@ class MprIcons private static $iconSet = null; private static $smartyRegistered = false; + private static $reverseMap = null; /** * Semantic name => [FA class, Material name] @@ -42,6 +43,7 @@ class MprIcons // E-commerce 'cart' => ['fa fa-shopping-cart', 'shopping_cart'], + 'add-to-cart' => ['fa fa-cart-plus', 'add_shopping_cart'], 'orders' => ['fa fa-list', 'receipt_long'], 'truck' => ['fa fa-truck', 'local_shipping'], 'credit-card' => ['fa fa-credit-card', 'credit_card'], @@ -50,12 +52,20 @@ class MprIcons 'percent' => ['fa fa-percent', 'percent'], 'inventory' => ['fa fa-cube', 'inventory_2'], 'payments' => ['fa fa-money', 'payments'], + 'payment' => ['fa fa-money', 'payment'], + 'wallet' => ['fa fa-money', 'account_balance_wallet'], 'campaign' => ['fa fa-bullhorn', 'campaign'], 'trophy' => ['fa fa-trophy', 'emoji_events'], + 'receipt' => ['fa fa-file-text-o', 'receipt'], + 'quote' => ['fa fa-quote-right', 'request_quote'], + 'return' => ['fa fa-undo', 'assignment_return'], // Status 'success' => ['fa fa-check', 'check'], + 'check-circle' => ['fa fa-check-circle', 'check_circle'], 'error' => ['fa fa-times', 'close'], + 'error-circle' => ['fa fa-times-circle', 'error'], + 'cancel' => ['fa fa-ban', 'cancel'], 'warning' => ['fa fa-exclamation-triangle', 'warning'], 'info' => ['fa fa-info-circle', 'info'], @@ -66,14 +76,21 @@ class MprIcons 'settings' => ['fa fa-cog', 'settings'], 'download' => ['fa fa-download', 'download'], 'upload' => ['fa fa-upload', 'upload'], + 'cloud-upload' => ['fa fa-cloud-upload', 'cloud_upload'], 'refresh' => ['fa fa-refresh', 'refresh'], + 'sync' => ['fa fa-refresh', 'sync'], 'save' => ['fa fa-save', 'save'], 'print' => ['fa fa-print', 'print'], 'copy' => ['fa fa-copy', 'content_copy'], 'tune' => ['fa fa-sliders', 'tune'], 'build' => ['fa fa-wrench', 'build'], 'swap' => ['fa fa-exchange', 'swap_horiz'], + 'swap-vert' => ['fa fa-exchange', 'swap_vert'], 'touch' => ['fa fa-hand-pointer-o', 'touch_app'], + 'hand' => ['fa fa-hand-paper-o', 'pan_tool'], + 'send' => ['fa fa-paper-plane', 'send'], + 'undo' => ['fa fa-undo', 'undo'], + 'attach' => ['fa fa-paperclip', 'attach_file'], // Analytics & Charts 'chart' => ['fa fa-bar-chart', 'bar_chart'], @@ -104,12 +121,12 @@ class MprIcons 'phone' => ['fa fa-phone', 'phone'], 'comment' => ['fa fa-comment', 'comment'], 'chat' => ['fa fa-comments', 'chat'], - 'send' => ['fa fa-paper-plane', 'send'], // Security 'lock' => ['fa fa-lock', 'lock'], 'unlock' => ['fa fa-unlock', 'lock_open'], 'shield' => ['fa fa-shield', 'shield'], + 'security' => ['fa fa-lock', 'security'], 'key' => ['fa fa-key', 'vpn_key'], 'verified' => ['fa fa-check-circle', 'verified_user'], 'bolt' => ['fa fa-bolt', 'bolt'], @@ -134,15 +151,28 @@ class MprIcons 'eye' => ['fa fa-eye', 'visibility'], 'eye-off' => ['fa fa-eye-slash', 'visibility_off'], 'file' => ['fa fa-file', 'description'], + 'file-document' => ['fa fa-file-text', 'insert_drive_file'], 'folder' => ['fa fa-folder', 'folder'], 'link' => ['fa fa-link', 'link'], 'external' => ['fa fa-external-link', 'open_in_new'], 'help' => ['fa fa-question-circle', 'help'], + 'note' => ['fa fa-sticky-note', 'note'], + 'summary' => ['fa fa-list-alt', 'summarize'], + 'category' => ['fa fa-th-large', 'category'], - // Documents & Quotes - 'quote' => ['fa fa-file-text-o', 'request_quote'], - 'receipt' => ['fa fa-file-text', 'receipt'], - 'undo' => ['fa fa-undo', 'undo'], + // Layout & Design + 'grid' => ['fa fa-th', 'grid_on'], + 'view-grid' => ['fa fa-th-large', 'view_module'], + 'layers' => ['fa fa-clone', 'layers'], + 'palette' => ['fa fa-paint-brush', 'palette'], + 'aspect-ratio' => ['fa fa-arrows-alt', 'aspect_ratio'], + 'ruler' => ['fa fa-minus', 'straighten'], + 'architecture' => ['fa fa-building', 'architecture'], + 'scale' => ['fa fa-balance-scale', 'scale'], + + // Zoom + 'zoom-in' => ['fa fa-search-plus', 'zoom_in'], + 'zoom-out' => ['fa fa-search-minus', 'zoom_out'], // Loading 'spinner' => ['fa fa-spinner fa-spin', 'hourglass_empty'], @@ -150,9 +180,25 @@ class MprIcons ]; /** - * Reverse map: Material icon name => semantic name (built lazily) + * Reverse lookup: resolve a native Material/FA icon name to its semantic key. + * Builds the reverse map lazily on first call. */ - private static $reverseMap = null; + private static function resolveNative(string $nativeName): ?string + { + if (self::$reverseMap === null) { + self::$reverseMap = []; + foreach (self::$map as $semantic => [$fa, $material]) { + if (!isset(self::$reverseMap[$material])) { + self::$reverseMap[$material] = $semantic; + } + $faShort = str_replace(['fa fa-', 'fa-spin'], '', trim($fa)); + if ($faShort && !isset(self::$reverseMap[$faShort])) { + self::$reverseMap[$faShort] = $semantic; + } + } + } + return self::$reverseMap[$nativeName] ?? null; + } /** * Get icon HTML - main method @@ -163,11 +209,18 @@ class MprIcons */ public static function get(string $name, string $class = ''): string { - // Direct semantic lookup if (!isset(self::$map[$name])) { - // Reverse lookup: try matching a raw Material/FA icon name - $name = self::resolveNative($name); - if (!$name) { + $resolved = self::resolveNative($name); + if ($resolved) { + $name = $resolved; + } else { + // Passthrough: if theme uses Material Icons, render the name as-is. + // Handles the long tail of Material Icons not in the semantic map. + $set = self::getIconSet(); + if ($set === self::MATERIAL && preg_match('/^[a-z][a-z0-9_]*$/', $name)) { + $extra = $class ? ' ' . $class : ''; + return '' . $name . ''; + } return ''; } } @@ -183,38 +236,20 @@ class MprIcons return ''; } - /** - * Resolve a native Material/FA icon name to its semantic name. - * Returns the semantic name or null if not found. - */ - private static function resolveNative(string $nativeName): ?string - { - if (self::$reverseMap === null) { - self::$reverseMap = []; - foreach (self::$map as $semantic => [$fa, $material]) { - // Map Material name → semantic - if (!isset(self::$reverseMap[$material])) { - self::$reverseMap[$material] = $semantic; - } - // Map FA class (e.g. "fa fa-trash" → "delete") - $faShort = str_replace(['fa fa-', 'fa-spin'], '', trim($fa)); - if (!isset(self::$reverseMap[$faShort])) { - self::$reverseMap[$faShort] = $semantic; - } - } - } - - return self::$reverseMap[$nativeName] ?? null; - } - /** * Get raw class/name without HTML wrapper */ public static function raw(string $name): string { if (!isset(self::$map[$name])) { - $name = self::resolveNative($name) ?? ''; - if (!$name) { + $resolved = self::resolveNative($name); + if ($resolved) { + $name = $resolved; + } else { + $set = self::getIconSet(); + if ($set === self::MATERIAL && preg_match('/^[a-z][a-z0-9_]*$/', $name)) { + return $name; + } return ''; } } @@ -600,7 +635,7 @@ class MprIcons */ public static function exists(string $name): bool { - return isset(self::$map[$name]); + return isset(self::$map[$name]) || self::resolveNative($name) !== null; } /**