Add reverse lookup, passthrough, and expand icon map
- Reverse lookup: resolveNative() maps Material/FA names to semantic keys - Passthrough: unknown Material icon names render as-is when theme uses Material - New icons: cancel, check-circle, error-circle, return, add-to-cart, payment, wallet, cloud-upload, sync, swap-vert, send, undo, attach, hand, note, summary, category, grid, view-grid, layers, palette, aspect-ratio, ruler, architecture, scale, zoom-in, zoom-out, security, file-document, quote, receipt Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
111
src/MprIcons.php
111
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 '<i class="material-icons' . $extra . '">' . $name . '</i>';
|
||||
}
|
||||
return '';
|
||||
}
|
||||
}
|
||||
@@ -183,38 +236,20 @@ class MprIcons
|
||||
return '<i class="' . $fa . $extra . '"></i>';
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user