Files
serv_benchmark/frontend/js/icon-manager.js
2026-01-11 23:41:30 +01:00

252 lines
11 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Icon Manager - Gestion des packs d'icônes
* Permet de basculer entre emojis, FontAwesome, et icônes personnalisées
*/
(function() {
'use strict';
const ICON_PACKS = {
'emoji': {
name: 'Emojis Unicode',
description: 'Emojis colorés par défaut',
icons: {
'add': '',
'edit': '✏️',
'delete': '🗑️',
'save': '💾',
'upload': '📤',
'download': '📥',
'image': '🖼️',
'file': '📄',
'pdf': '📕',
'link': '🔗',
'refresh': '🔄',
'search': '🌍',
'settings': '⚙️',
'close': '❌',
'check': '✅',
'warning': '⚠️',
'info': '',
'copy': '📋'
}
},
'fontawesome-solid': {
name: 'FontAwesome Solid',
description: 'Icônes FontAwesome pleines (bold)',
icons: {
'add': '<span class="btn-icon svg-inline" data-svg-src="icons/svg/fa/solid/plus.svg" aria-hidden="true"></span>',
'edit': '<span class="btn-icon svg-inline" data-svg-src="icons/svg/fa/solid/pen-to-square.svg" aria-hidden="true"></span>',
'delete': '<span class="btn-icon svg-inline" data-svg-src="icons/svg/fa/solid/trash-can.svg" aria-hidden="true"></span>',
'save': '<span class="btn-icon svg-inline" data-svg-src="icons/svg/fa/solid/floppy-disk.svg" aria-hidden="true"></span>',
'upload': '<span class="btn-icon svg-inline" data-svg-src="icons/svg/fa/solid/upload.svg" aria-hidden="true"></span>',
'download': '<span class="btn-icon svg-inline" data-svg-src="icons/svg/fa/solid/download.svg" aria-hidden="true"></span>',
'image': '<span class="btn-icon svg-inline" data-svg-src="icons/svg/fa/solid/image.svg" aria-hidden="true"></span>',
'file': '<span class="btn-icon svg-inline" data-svg-src="icons/svg/fa/solid/file.svg" aria-hidden="true"></span>',
'pdf': '<span class="btn-icon svg-inline" data-svg-src="icons/svg/fa/solid/file-pdf.svg" aria-hidden="true"></span>',
'link': '<span class="btn-icon svg-inline" data-svg-src="icons/svg/fa/solid/link.svg" aria-hidden="true"></span>',
'refresh': '<span class="btn-icon svg-inline" data-svg-src="icons/svg/fa/solid/arrows-rotate.svg" aria-hidden="true"></span>',
'search': '<span class="btn-icon svg-inline" data-svg-src="icons/svg/fa/solid/earth-europe.svg" aria-hidden="true"></span>',
'settings': '<span class="btn-icon svg-inline" data-svg-src="icons/svg/fa/solid/gear.svg" aria-hidden="true"></span>',
'close': '<span class="btn-icon svg-inline" data-svg-src="icons/svg/fa/solid/xmark.svg" aria-hidden="true"></span>',
'check': '<span class="btn-icon svg-inline" data-svg-src="icons/svg/fa/solid/check.svg" aria-hidden="true"></span>',
'warning': '<span class="btn-icon svg-inline" data-svg-src="icons/svg/fa/solid/triangle-exclamation.svg" aria-hidden="true"></span>',
'info': '<span class="btn-icon svg-inline" data-svg-src="icons/svg/fa/solid/circle-info.svg" aria-hidden="true"></span>',
'copy': '<span class="btn-icon svg-inline" data-svg-src="icons/svg/fa/solid/copy.svg" aria-hidden="true"></span>'
}
},
'fontawesome-regular': {
name: 'FontAwesome Regular',
description: 'Icônes FontAwesome fines (outline)',
icons: {
'add': '<span class="btn-icon svg-inline" data-svg-src="icons/svg/fa/regular/square-plus.svg" aria-hidden="true"></span>',
'edit': '<span class="btn-icon svg-inline" data-svg-src="icons/svg/fa/regular/pen-to-square.svg" aria-hidden="true"></span>',
'delete': '<span class="btn-icon svg-inline" data-svg-src="icons/svg/fa/regular/trash-can.svg" aria-hidden="true"></span>',
'save': '<span class="btn-icon svg-inline" data-svg-src="icons/svg/fa/regular/floppy-disk.svg" aria-hidden="true"></span>',
'upload': '<span class="btn-icon svg-inline" data-svg-src="icons/svg/fa/solid/upload.svg" aria-hidden="true"></span>',
'download': '<span class="btn-icon svg-inline" data-svg-src="icons/svg/fa/solid/download.svg" aria-hidden="true"></span>',
'image': '<span class="btn-icon svg-inline" data-svg-src="icons/svg/fa/regular/image.svg" aria-hidden="true"></span>',
'file': '<span class="btn-icon svg-inline" data-svg-src="icons/svg/fa/regular/file.svg" aria-hidden="true"></span>',
'pdf': '<span class="btn-icon svg-inline" data-svg-src="icons/svg/fa/regular/file-pdf.svg" aria-hidden="true"></span>',
'link': '<span class="btn-icon svg-inline" data-svg-src="icons/svg/fa/solid/link.svg" aria-hidden="true"></span>',
'refresh': '<span class="btn-icon svg-inline" data-svg-src="icons/svg/fa/solid/arrows-rotate.svg" aria-hidden="true"></span>',
'search': '<span class="btn-icon svg-inline" data-svg-src="icons/svg/fa/solid/earth-europe.svg" aria-hidden="true"></span>',
'settings': '<span class="btn-icon svg-inline" data-svg-src="icons/svg/fa/solid/gear.svg" aria-hidden="true"></span>',
'close': '<span class="btn-icon svg-inline" data-svg-src="icons/svg/fa/regular/circle-xmark.svg" aria-hidden="true"></span>',
'check': '<span class="btn-icon svg-inline" data-svg-src="icons/svg/fa/regular/circle-check.svg" aria-hidden="true"></span>',
'warning': '<span class="btn-icon svg-inline" data-svg-src="icons/svg/fa/solid/triangle-exclamation.svg" aria-hidden="true"></span>',
'info': '<span class="btn-icon svg-inline" data-svg-src="icons/svg/fa/solid/circle-info.svg" aria-hidden="true"></span>',
'copy': '<span class="btn-icon svg-inline" data-svg-src="icons/svg/fa/regular/copy.svg" aria-hidden="true"></span>'
}
},
'icons8': {
name: 'Icons8 PNG',
description: 'Icônes Icons8 existantes (PNG)',
icons: {
'add': '<img src="icons/icons8-done-48.png" class="btn-icon" alt="Add">',
'edit': '<img src="icons/icons8-edit-pencil-48.png" class="btn-icon" alt="Edit">',
'delete': '<img src="icons/icons8-delete-48.png" class="btn-icon" alt="Delete">',
'save': '<img src="icons/icons8-save-48.png" class="btn-icon" alt="Save">',
'upload': '📤',
'download': '📥',
'image': '<img src="icons/icons8-picture-48.png" class="btn-icon" alt="Image">',
'file': '📄',
'pdf': '📕',
'link': '🔗',
'refresh': '🔄',
'search': '🌍',
'settings': '<img src="icons/icons8-setting-48.png" class="btn-icon" alt="Settings">',
'close': '<img src="icons/icons8-close-48.png" class="btn-icon" alt="Close">',
'check': '<img src="icons/icons8-check-mark-48.png" class="btn-icon" alt="Check">',
'warning': '⚠️',
'info': '',
'copy': '📋'
}
}
};
const DEFAULT_PACK = 'emoji';
const STORAGE_KEY = 'benchtools_icon_pack';
const svgCache = new Map();
function normalizeSvg(svgText) {
let text = svgText;
text = text.replace(/fill="(?!none)[^"]*"/g, 'fill="currentColor"');
text = text.replace(/stroke="(?!none)[^"]*"/g, 'stroke="currentColor"');
return text;
}
function inlineSvgElement(el) {
const src = el.getAttribute('data-svg-src');
if (!src) return;
const cached = svgCache.get(src);
if (cached) {
el.innerHTML = cached;
return;
}
fetch(src)
.then(response => {
if (!response.ok) {
throw new Error(`SVG load failed: ${response.status}`);
}
return response.text();
})
.then(text => {
const normalized = normalizeSvg(text);
svgCache.set(src, normalized);
el.innerHTML = normalized;
})
.catch(error => {
console.warn('[IconManager] Failed to inline SVG:', src, error);
});
}
function inlineSvgIcons(root = document) {
const elements = root.querySelectorAll('[data-svg-src]');
elements.forEach(el => inlineSvgElement(el));
}
// Icon Manager Object
const IconManager = {
packs: ICON_PACKS,
getCurrentPack: function() {
return localStorage.getItem(STORAGE_KEY) || DEFAULT_PACK;
},
applyPack: function(packName) {
if (!ICON_PACKS[packName]) {
console.error(`Icon pack "${packName}" not found`);
return false;
}
localStorage.setItem(STORAGE_KEY, packName);
// Dispatch custom event for icon pack change
window.dispatchEvent(new CustomEvent('iconPackChanged', {
detail: {
pack: packName,
packName: ICON_PACKS[packName].name
}
}));
return true;
},
getIcon: function(iconName, fallback = '?') {
const currentPack = this.getCurrentPack();
const pack = ICON_PACKS[currentPack];
if (!pack) {
console.warn(`Icon pack "${currentPack}" not found`);
return fallback;
}
return pack.icons[iconName] || fallback;
},
getAllPacks: function() {
return Object.keys(ICON_PACKS);
},
getPackInfo: function(packName) {
return ICON_PACKS[packName] || null;
},
// Helper pour générer un bouton avec icône
createButton: function(iconName, text = '', className = 'btn btn-primary') {
const icon = this.getIcon(iconName);
const textPart = text ? ` ${text}` : '';
return `<button class="${className}">${icon}${textPart}</button>`;
},
// Helper pour mettre à jour tous les boutons de la page
updateAllButtons: function() {
// Cette fonction sera appelée après changement de pack
// Pour mettre à jour dynamiquement tous les boutons
const buttons = document.querySelectorAll('[data-icon]');
buttons.forEach(btn => {
const iconName = btn.getAttribute('data-icon');
const iconSpan = btn.querySelector('.btn-icon-wrapper');
if (iconSpan && iconName) {
iconSpan.innerHTML = this.getIcon(iconName);
}
});
inlineSvgIcons();
},
inlineSvgIcons: function(root = document) {
inlineSvgIcons(root);
}
};
// Auto-initialize on load
function initializeIcons() {
IconManager.updateAllButtons();
// Also call utils.js initializeButtonIcons if available
if (window.initializeButtonIcons) {
window.initializeButtonIcons();
}
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initializeIcons);
} else {
initializeIcons();
}
// Re-initialize when icon pack changes
window.addEventListener('iconPackChanged', function() {
setTimeout(initializeIcons, 100); // Small delay to ensure DOM is ready
});
// Expose globally
window.IconManager = IconManager;
// Log initialization
console.log(`[IconManager] Initialized with pack: ${IconManager.getCurrentPack()}`);
})();