- Symbol IDs now include shape for uniqueness: {shape}_{brand}_{mode}
e.g., text-only_paypal_lm, rectangle_visa_la, icon_mastercard_dm
- Fixed brand name inconsistencies:
- amazon_pay → amazon
- naverpay → naver_pay
- p24 → przelewy24
- direct_debit → bacs (unified BACS Direct Debit name)
- Added font assets (FontAwesome, Material Icons)
- Added MprIconsAssets and MprIconsConfig classes
- Updated preview.html with shape parameter support
- All 752 icons complete (38 payment × 4 shapes × 4 modes + 12 social × 3 shapes × 4 modes)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
459 lines
22 KiB
HTML
459 lines
22 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>PrestaShop Icons Preview</title>
|
||
<style>
|
||
* { box-sizing: border-box; }
|
||
body {
|
||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||
margin: 0; padding: 20px;
|
||
background: #f5f5f5;
|
||
color: #333;
|
||
transition: background 0.3s, color 0.3s;
|
||
}
|
||
body.dark-theme {
|
||
background: #1a1a1a;
|
||
color: #eee;
|
||
}
|
||
h1 { margin: 0 0 20px; }
|
||
h2 { margin: 30px 0 10px; padding: 10px; background: #333; color: #fff; border-radius: 4px; }
|
||
body.dark-theme h2 { background: #444; }
|
||
h3 { margin: 20px 0 10px; color: #666; border-bottom: 1px solid #ddd; padding-bottom: 5px; }
|
||
body.dark-theme h3 { color: #aaa; border-color: #444; }
|
||
.header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 20px;
|
||
}
|
||
.theme-switch {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10px;
|
||
}
|
||
.switch {
|
||
position: relative;
|
||
width: 60px;
|
||
height: 30px;
|
||
}
|
||
.switch input { opacity: 0; width: 0; height: 0; }
|
||
.slider {
|
||
position: absolute;
|
||
cursor: pointer;
|
||
top: 0; left: 0; right: 0; bottom: 0;
|
||
background: #ccc;
|
||
border-radius: 30px;
|
||
transition: 0.3s;
|
||
}
|
||
.slider:before {
|
||
position: absolute;
|
||
content: "";
|
||
height: 22px;
|
||
width: 22px;
|
||
left: 4px;
|
||
bottom: 4px;
|
||
background: white;
|
||
border-radius: 50%;
|
||
transition: 0.3s;
|
||
}
|
||
input:checked + .slider { background: #333; }
|
||
input:checked + .slider:before { transform: translateX(30px); }
|
||
.theme-label { font-size: 14px; }
|
||
.grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
|
||
gap: 15px;
|
||
margin-bottom: 30px;
|
||
}
|
||
.grid-wide {
|
||
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
|
||
}
|
||
.icon-box {
|
||
background: #fff;
|
||
border: 1px solid #ddd;
|
||
border-radius: 8px;
|
||
padding: 15px;
|
||
text-align: center;
|
||
transition: all 0.3s;
|
||
}
|
||
body.dark-theme .icon-box {
|
||
background: #2a2a2a;
|
||
border-color: #444;
|
||
}
|
||
.icon-box:hover { box-shadow: 0 4px 12px rgba(0,0,0,0.15); }
|
||
body.dark-theme .icon-box:hover { box-shadow: 0 4px 12px rgba(0,0,0,0.5); }
|
||
.icon-box.dark { background: #1a1a1a; border-color: #333; }
|
||
body.dark-theme .icon-box.dark { background: #0a0a0a; border-color: #222; }
|
||
.icon-box svg { display: block; margin: 0 auto 10px; }
|
||
.icon-box span { font-size: 11px; color: #666; word-break: break-all; }
|
||
body.dark-theme .icon-box span { color: #999; }
|
||
.icon-box.dark span { color: #999; }
|
||
.tabs { display: flex; gap: 5px; margin-bottom: 20px; flex-wrap: wrap; }
|
||
.tab {
|
||
padding: 8px 16px;
|
||
background: #ddd;
|
||
border: none;
|
||
border-radius: 4px;
|
||
cursor: pointer;
|
||
font-size: 14px;
|
||
transition: all 0.3s;
|
||
}
|
||
body.dark-theme .tab { background: #444; color: #eee; }
|
||
.tab.active { background: #333; color: #fff; }
|
||
body.dark-theme .tab.active { background: #666; }
|
||
.section { display: none; }
|
||
.section.active { display: block; }
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="header">
|
||
<h1>PrestaShop Icons Preview</h1>
|
||
<div class="theme-switch">
|
||
<span class="theme-label">Light</span>
|
||
<label class="switch">
|
||
<input type="checkbox" id="themeToggle" onchange="toggleTheme()">
|
||
<span class="slider"></span>
|
||
</label>
|
||
<span class="theme-label">Dark</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="tabs">
|
||
<button class="tab active" onclick="showSection('payments')">Payment Icons</button>
|
||
<button class="tab" onclick="showSection('socials')">Social Icons</button>
|
||
</div>
|
||
|
||
<div id="payments" class="section active">
|
||
<h2>Payment Icons</h2>
|
||
|
||
<h3>Icon (logo mark only) - Light Accent</h3>
|
||
<div class="grid" id="payments-icon-la"></div>
|
||
|
||
<h3>Icon (logo mark only) - Dark Accent on Dark Background</h3>
|
||
<div class="grid" id="payments-icon-da"></div>
|
||
|
||
<h3>Icon (logo mark only) - Light Mode (monochrome)</h3>
|
||
<div class="grid" id="payments-icon-lm"></div>
|
||
|
||
<h3>Icon (logo mark only) - Dark Mode (monochrome) on Dark Background</h3>
|
||
<div class="grid" id="payments-icon-dm"></div>
|
||
|
||
<h3>Square (logo in square frame) - Light Accent</h3>
|
||
<div class="grid" id="payments-square-la"></div>
|
||
|
||
<h3>Square (logo in square frame) - Dark Accent on Dark Background</h3>
|
||
<div class="grid" id="payments-square-da"></div>
|
||
|
||
<h3>Square (logo in square frame) - Light Mode (monochrome)</h3>
|
||
<div class="grid" id="payments-square-lm"></div>
|
||
|
||
<h3>Square (logo in square frame) - Dark Mode (monochrome) on Dark Background</h3>
|
||
<div class="grid" id="payments-square-dm"></div>
|
||
|
||
<h3>Rectangle (icon + brand text, 150×100) - Light Accent</h3>
|
||
<div class="grid grid-wide" id="payments-rectangle-la"></div>
|
||
|
||
<h3>Rectangle (icon + brand text, 150×100) - Dark Accent on Dark Background</h3>
|
||
<div class="grid grid-wide" id="payments-rectangle-da"></div>
|
||
|
||
<h3>Rectangle (icon + brand text, 150×100) - Light Mode (monochrome)</h3>
|
||
<div class="grid grid-wide" id="payments-rectangle-lm"></div>
|
||
|
||
<h3>Rectangle (icon + brand text, 150×100) - Dark Mode (monochrome) on Dark Background</h3>
|
||
<div class="grid grid-wide" id="payments-rectangle-dm"></div>
|
||
|
||
<h3>Text Only (brand name) - Light Accent</h3>
|
||
<div class="grid grid-wide" id="payments-text-only-la"></div>
|
||
|
||
<h3>Text Only (brand name) - Dark Accent on Dark Background</h3>
|
||
<div class="grid grid-wide" id="payments-text-only-da"></div>
|
||
|
||
<h3>Text Only (brand name) - Light Mode (monochrome)</h3>
|
||
<div class="grid grid-wide" id="payments-text-only-lm"></div>
|
||
|
||
<h3>Text Only (brand name) - Dark Mode (monochrome) on Dark Background</h3>
|
||
<div class="grid grid-wide" id="payments-text-only-dm"></div>
|
||
</div>
|
||
|
||
<div id="socials" class="section">
|
||
<h2>Social Icons</h2>
|
||
|
||
<h3>Icon (logo mark only) - Light Accent</h3>
|
||
<div class="grid" id="socials-icon-la"></div>
|
||
|
||
<h3>Icon (logo mark only) - Dark Accent on Dark Background</h3>
|
||
<div class="grid" id="socials-icon-da"></div>
|
||
|
||
<h3>Icon (logo mark only) - Light Mode (monochrome)</h3>
|
||
<div class="grid" id="socials-icon-lm"></div>
|
||
|
||
<h3>Icon (logo mark only) - Dark Mode (monochrome) on Dark Background</h3>
|
||
<div class="grid" id="socials-icon-dm"></div>
|
||
|
||
<h3>Full Logo (icon + brand text, 150×100) - Light Accent</h3>
|
||
<div class="grid grid-wide" id="socials-full-logo-la"></div>
|
||
|
||
<h3>Full Logo (icon + brand text, 150×100) - Dark Accent on Dark Background</h3>
|
||
<div class="grid grid-wide" id="socials-full-logo-da"></div>
|
||
|
||
<h3>Full Logo (icon + brand text, 150×100) - Light Mode (monochrome)</h3>
|
||
<div class="grid grid-wide" id="socials-full-logo-lm"></div>
|
||
|
||
<h3>Full Logo (icon + brand text, 150×100) - Dark Mode (monochrome) on Dark Background</h3>
|
||
<div class="grid grid-wide" id="socials-full-logo-dm"></div>
|
||
|
||
<h3>Text Only (brand name) - Light Accent</h3>
|
||
<div class="grid grid-wide" id="socials-text-only-la"></div>
|
||
|
||
<h3>Text Only (brand name) - Dark Accent on Dark Background</h3>
|
||
<div class="grid grid-wide" id="socials-text-only-da"></div>
|
||
|
||
<h3>Text Only (brand name) - Light Mode (monochrome)</h3>
|
||
<div class="grid grid-wide" id="socials-text-only-lm"></div>
|
||
|
||
<h3>Text Only (brand name) - Dark Mode (monochrome) on Dark Background</h3>
|
||
<div class="grid grid-wide" id="socials-text-only-dm"></div>
|
||
</div>
|
||
|
||
<script>
|
||
const paymentBrands = [
|
||
'afterpay', 'alipay', 'alma', 'amazon', 'american_express', 'apple_pay',
|
||
'bacs', 'bancontact', 'bank', 'billie', 'blik', 'cartes_bancaires', 'cod',
|
||
'credit_pay', 'eps', 'google_pay', 'ideal', 'jcb', 'kakao_pay', 'klarna',
|
||
'link', 'mastercard', 'mobilepay', 'multibanco', 'naver_pay', 'przelewy24', 'payco',
|
||
'paypal', 'pickup', 'point_of_sale', 'quote', 'revolut', 'samsung_pay', 'satispay',
|
||
'sepa', 'twint', 'visa', 'wechat_pay'
|
||
];
|
||
|
||
const socialBrands = [
|
||
'amazon', 'apple', 'facebook', 'google', 'linkedin', 'microsoft',
|
||
'outlook', 'paypal', 'reddit', 'tiktok', 'x', 'yahoo'
|
||
];
|
||
|
||
function renderIcons(containerId, brands, spriteFile, suffix, size, isDark = false, shape = 'icon') {
|
||
const container = document.getElementById(containerId);
|
||
if (!container) return;
|
||
|
||
console.log(`[${containerId}] Loading sprite: ${spriteFile}`);
|
||
|
||
// Load sprite
|
||
fetch(spriteFile)
|
||
.then(r => {
|
||
console.log(`[${containerId}] Fetch status: ${r.status}`);
|
||
return r.text();
|
||
})
|
||
.then(svg => {
|
||
// Insert hidden sprite with unique ID
|
||
// Use position:absolute + clip instead of display:none to keep gradients/clipPaths working
|
||
const spriteId = `sprite-${containerId}`;
|
||
const div = document.createElement('div');
|
||
div.id = spriteId;
|
||
div.style.cssText = 'position:absolute;width:0;height:0;overflow:hidden;';
|
||
div.innerHTML = svg;
|
||
document.body.appendChild(div);
|
||
|
||
// Find all symbols in this sprite
|
||
const symbols = div.querySelectorAll('symbol');
|
||
const symbolIds = Array.from(symbols).map(s => s.id);
|
||
console.log(`[${containerId}] Found ${symbols.length} symbols:`, symbolIds);
|
||
|
||
// Render icons - symbol ID format: {shape}_{brand}_{mode}
|
||
brands.forEach(brand => {
|
||
const id = `${shape}_${brand}_${suffix}`;
|
||
const exists = symbolIds.includes(id);
|
||
|
||
if (!exists) {
|
||
console.warn(`[${containerId}] MISSING symbol: #${id}`);
|
||
}
|
||
|
||
const box = document.createElement('div');
|
||
box.className = 'icon-box' + (isDark ? ' dark' : '') + (exists ? '' : ' missing');
|
||
box.innerHTML = `
|
||
<svg width="${size}" height="${size}" style="${exists ? '' : 'border: 2px dashed red;'}">
|
||
<use href="#${id}"></use>
|
||
</svg>
|
||
<span>${brand}${exists ? '' : ' (MISSING)'}</span>
|
||
`;
|
||
container.appendChild(box);
|
||
|
||
// Check if symbol has content
|
||
if (exists) {
|
||
const symbol = div.querySelector(`#${id}`);
|
||
const content = symbol ? symbol.innerHTML.trim() : '';
|
||
if (!content) {
|
||
console.warn(`[${containerId}] EMPTY symbol: #${id}`);
|
||
} else {
|
||
// Check for references in the symbol
|
||
const urls = content.match(/url\(#([^)]+)\)/g) || [];
|
||
const hrefs = content.match(/href="#([^"]+)"/g) || [];
|
||
if (urls.length || hrefs.length) {
|
||
console.log(`[${containerId}] ${id} references:`, [...urls, ...hrefs]);
|
||
// Check if references exist
|
||
[...urls, ...hrefs].forEach(ref => {
|
||
const refId = ref.match(/#([^)"]+)/)?.[1];
|
||
if (refId && !document.getElementById(refId)) {
|
||
console.error(`[${containerId}] BROKEN REF in ${id}: #${refId} not found in DOM`);
|
||
}
|
||
});
|
||
}
|
||
}
|
||
|
||
}
|
||
});
|
||
})
|
||
.catch(e => {
|
||
console.error(`[${containerId}] Error:`, e);
|
||
container.innerHTML = `<p>Error loading ${spriteFile}: ${e.message}</p>`;
|
||
});
|
||
}
|
||
|
||
// Render wide icons (150x100 aspect ratio)
|
||
function renderIconsWide(containerId, brands, spriteFile, suffix, width, height, isDark = false, shape = 'rectangle') {
|
||
const container = document.getElementById(containerId);
|
||
if (!container) return;
|
||
|
||
fetch(spriteFile)
|
||
.then(r => r.text())
|
||
.then(svg => {
|
||
const spriteId = `sprite-${containerId}`;
|
||
const div = document.createElement('div');
|
||
div.id = spriteId;
|
||
div.style.cssText = 'position:absolute;width:0;height:0;overflow:hidden;';
|
||
div.innerHTML = svg;
|
||
document.body.appendChild(div);
|
||
|
||
const symbols = div.querySelectorAll('symbol');
|
||
const symbolIds = Array.from(symbols).map(s => s.id);
|
||
|
||
// Symbol ID format: {shape}_{brand}_{mode}
|
||
brands.forEach(brand => {
|
||
const id = `${shape}_${brand}_${suffix}`;
|
||
const exists = symbolIds.includes(id);
|
||
|
||
if (!exists) {
|
||
console.warn(`[${containerId}] MISSING symbol: #${id}`);
|
||
}
|
||
|
||
const box = document.createElement('div');
|
||
box.className = 'icon-box' + (isDark ? ' dark' : '') + (exists ? '' : ' missing');
|
||
box.innerHTML = `
|
||
<svg width="${width}" height="${height}" viewBox="0 0 ${width} ${height}" style="${exists ? '' : 'border: 2px dashed red;'}">
|
||
<use href="#${id}"></use>
|
||
</svg>
|
||
<span>${brand}${exists ? '' : ' (MISSING)'}</span>
|
||
`;
|
||
container.appendChild(box);
|
||
});
|
||
})
|
||
.catch(e => {
|
||
console.error(`[${containerId}] Error:`, e);
|
||
container.innerHTML = `<p>Error loading ${spriteFile}: ${e.message}</p>`;
|
||
});
|
||
}
|
||
|
||
// Debug: Check for invisible icons after render
|
||
function debugInvisibleIcons() {
|
||
document.querySelectorAll('.icon-box').forEach(box => {
|
||
const svg = box.querySelector('svg');
|
||
const use = box.querySelector('use');
|
||
const label = box.querySelector('span')?.textContent || 'unknown';
|
||
|
||
if (!use) return;
|
||
|
||
const href = use.getAttribute('href');
|
||
const symbolId = href?.replace('#', '');
|
||
const symbol = document.getElementById(symbolId);
|
||
|
||
if (symbol) {
|
||
// Check if symbol has visible paths
|
||
const paths = symbol.querySelectorAll('path, rect, circle, polygon, g');
|
||
const hasVisibleContent = paths.length > 0;
|
||
|
||
// Check if any path has fill or stroke
|
||
let hasColor = false;
|
||
paths.forEach(p => {
|
||
const fill = p.getAttribute('fill') || p.style.fill;
|
||
const stroke = p.getAttribute('stroke') || p.style.stroke;
|
||
if ((fill && fill !== 'none') || (stroke && stroke !== 'none')) {
|
||
hasColor = true;
|
||
}
|
||
});
|
||
|
||
if (!hasVisibleContent) {
|
||
console.error(`NO CONTENT: ${label} (${symbolId}) - no paths/shapes found`);
|
||
} else if (!hasColor) {
|
||
console.warn(`NO COLOR: ${label} (${symbolId}) - shapes exist but no fill/stroke`);
|
||
}
|
||
}
|
||
});
|
||
console.log('Debug check complete. Look for NO CONTENT or NO COLOR warnings above.');
|
||
}
|
||
|
||
// Run debug after all sprites loaded
|
||
setTimeout(debugInvisibleIcons, 2000);
|
||
|
||
// Payment icons - Icon
|
||
renderIcons('payments-icon-la', paymentBrands, 'sprites/payments/icon-la.svg', 'la', 45, false, 'icon');
|
||
renderIcons('payments-icon-da', paymentBrands, 'sprites/payments/icon-da.svg', 'da', 45, true, 'icon');
|
||
renderIcons('payments-icon-lm', paymentBrands, 'sprites/payments/icon-lm.svg', 'lm', 45, false, 'icon');
|
||
renderIcons('payments-icon-dm', paymentBrands, 'sprites/payments/icon-dm.svg', 'dm', 45, true, 'icon');
|
||
|
||
// Payment icons - Square
|
||
renderIcons('payments-square-la', paymentBrands, 'sprites/payments/square-la.svg', 'la', 45, false, 'square');
|
||
renderIcons('payments-square-da', paymentBrands, 'sprites/payments/square-da.svg', 'da', 45, true, 'square');
|
||
renderIcons('payments-square-lm', paymentBrands, 'sprites/payments/square-lm.svg', 'lm', 45, false, 'square');
|
||
renderIcons('payments-square-dm', paymentBrands, 'sprites/payments/square-dm.svg', 'dm', 45, true, 'square');
|
||
|
||
// Payment icons - Rectangle
|
||
renderIconsWide('payments-rectangle-la', paymentBrands, 'sprites/payments/rectangle-la.svg', 'la', 150, 100, false, 'rectangle');
|
||
renderIconsWide('payments-rectangle-da', paymentBrands, 'sprites/payments/rectangle-da.svg', 'da', 150, 100, true, 'rectangle');
|
||
renderIconsWide('payments-rectangle-lm', paymentBrands, 'sprites/payments/rectangle-lm.svg', 'lm', 150, 100, false, 'rectangle');
|
||
renderIconsWide('payments-rectangle-dm', paymentBrands, 'sprites/payments/rectangle-dm.svg', 'dm', 150, 100, true, 'rectangle');
|
||
|
||
// Payment icons - Text Only
|
||
renderIconsWide('payments-text-only-la', paymentBrands, 'sprites/payments/text-only-la.svg', 'la', 150, 100, false, 'text-only');
|
||
renderIconsWide('payments-text-only-da', paymentBrands, 'sprites/payments/text-only-da.svg', 'da', 150, 100, true, 'text-only');
|
||
renderIconsWide('payments-text-only-lm', paymentBrands, 'sprites/payments/text-only-lm.svg', 'lm', 150, 100, false, 'text-only');
|
||
renderIconsWide('payments-text-only-dm', paymentBrands, 'sprites/payments/text-only-dm.svg', 'dm', 150, 100, true, 'text-only');
|
||
|
||
// Social icons - Icon
|
||
renderIcons('socials-icon-la', socialBrands, 'sprites/socials/icon-la.svg', 'la', 45, false, 'icon');
|
||
renderIcons('socials-icon-da', socialBrands, 'sprites/socials/icon-da.svg', 'da', 45, true, 'icon');
|
||
renderIcons('socials-icon-lm', socialBrands, 'sprites/socials/icon-lm.svg', 'lm', 45, false, 'icon');
|
||
renderIcons('socials-icon-dm', socialBrands, 'sprites/socials/icon-dm.svg', 'dm', 45, true, 'icon');
|
||
|
||
// Social icons - Full Logo
|
||
renderIconsWide('socials-full-logo-la', socialBrands, 'sprites/socials/full-logo-la.svg', 'la', 150, 100, false, 'full-logo');
|
||
renderIconsWide('socials-full-logo-da', socialBrands, 'sprites/socials/full-logo-da.svg', 'da', 150, 100, true, 'full-logo');
|
||
renderIconsWide('socials-full-logo-lm', socialBrands, 'sprites/socials/full-logo-lm.svg', 'lm', 150, 100, false, 'full-logo');
|
||
renderIconsWide('socials-full-logo-dm', socialBrands, 'sprites/socials/full-logo-dm.svg', 'dm', 150, 100, true, 'full-logo');
|
||
|
||
// Social icons - Text Only
|
||
renderIconsWide('socials-text-only-la', socialBrands, 'sprites/socials/text-only-la.svg', 'la', 150, 100, false, 'text-only');
|
||
renderIconsWide('socials-text-only-da', socialBrands, 'sprites/socials/text-only-da.svg', 'da', 150, 100, true, 'text-only');
|
||
renderIconsWide('socials-text-only-lm', socialBrands, 'sprites/socials/text-only-lm.svg', 'lm', 150, 100, false, 'text-only');
|
||
renderIconsWide('socials-text-only-dm', socialBrands, 'sprites/socials/text-only-dm.svg', 'dm', 150, 100, true, 'text-only');
|
||
|
||
function showSection(name) {
|
||
document.querySelectorAll('.section').forEach(s => s.classList.remove('active'));
|
||
document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
|
||
document.getElementById(name).classList.add('active');
|
||
event.target.classList.add('active');
|
||
}
|
||
|
||
function toggleTheme() {
|
||
document.body.classList.toggle('dark-theme');
|
||
localStorage.setItem('theme', document.body.classList.contains('dark-theme') ? 'dark' : 'light');
|
||
}
|
||
|
||
// Load saved theme
|
||
if (localStorage.getItem('theme') === 'dark') {
|
||
document.body.classList.add('dark-theme');
|
||
document.getElementById('themeToggle').checked = true;
|
||
}
|
||
</script>
|
||
</body>
|
||
</html>
|