252 lines
11 KiB
JavaScript
252 lines
11 KiB
JavaScript
/**
|
||
* 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()}`);
|
||
})();
|