feat: add related icons section to modal

- Add "Maybe Related" section showing icons with similar names
- Find related icons by base name matching (e.g., time-check → time-delete)
- Add icons to modal section labels (settings-sliders, link)
- Style related icons grid with hover effects

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-24 12:02:35 +08:00
parent 2572808291
commit 0dd0dd0224
3 changed files with 132 additions and 1 deletions
+54
View File
@@ -1030,6 +1030,14 @@ body {
font-weight: 700; font-weight: 700;
margin-bottom: 8px; margin-bottom: 8px;
letter-spacing: 0.6px; letter-spacing: 0.6px;
display: flex;
align-items: center;
gap: 6px;
& .fi {
font-size: 12px;
color: var(--accent-primary);
}
} }
.modal-variations { .modal-variations {
@@ -1038,6 +1046,48 @@ body {
gap: 8px; gap: 8px;
} }
.related-icons {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(64px, 1fr));
gap: 8px;
}
.related-icon {
text-align: center;
padding: 10px 8px;
background: var(--bg-tertiary);
border-radius: var(--radius-sm);
cursor: pointer;
border: 1px solid var(--border-color);
transition: var(--transition);
&:hover {
background: var(--bg-elevated);
border-color: var(--accent-primary);
transform: translateY(-2px);
box-shadow: var(--shadow-md);
& .related-icon-symbol {
color: var(--accent-primary);
}
}
& .related-icon-symbol {
font-size: 20px;
display: block;
margin-bottom: 6px;
color: var(--text-primary);
}
& .related-icon-name {
font-size: 9px;
color: var(--text-muted);
font-weight: 500;
word-break: break-all;
line-height: 1.3;
}
}
.modal-var { .modal-var {
text-align: center; text-align: center;
padding: 10px 6px; padding: 10px 6px;
@@ -1374,6 +1424,10 @@ body {
grid-template-columns: repeat(4, 1fr); grid-template-columns: repeat(4, 1fr);
} }
.related-icons {
grid-template-columns: repeat(4, 1fr);
}
.modal-actions-inline { .modal-actions-inline {
margin-left: 0; margin-left: 0;
width: 100%; width: 100%;
+6 -1
View File
@@ -135,10 +135,15 @@
<div class="modal-content"> <div class="modal-content">
<div class="modal-section"> <div class="modal-section">
<div class="modal-label">All Variations</div> <div class="modal-label"><span class="fi fi-rs-settings-sliders"></span> All Variations</div>
<div class="modal-variations" id="modalVariations"></div> <div class="modal-variations" id="modalVariations"></div>
</div> </div>
<div class="modal-section" id="relatedSection" style="display:none">
<div class="modal-label"><span class="fi fi-rs-link"></span> Maybe Related</div>
<div class="related-icons" id="relatedIcons"></div>
</div>
<div class="modal-footer"> <div class="modal-footer">
<button class="modal-btn" id="modalClose"> <button class="modal-btn" id="modalClose">
<span class="fi fi-rs-cross"></span> Close <span class="btn-key">Esc</span> <span class="fi fi-rs-cross"></span> Close <span class="btn-key">Esc</span>
+72
View File
@@ -237,6 +237,53 @@ function animateGrid() {
function animateModal() { function animateModal() {
} }
function findRelatedIcons(currentName, currentPrefix, limit = 12) {
const parts = currentName.split('-');
const baseName = parts[0];
const relatedScores = new Map();
for (const prefix of Object.keys(iconsByPrefix)) {
for (const icon of iconsByPrefix[prefix]) {
if (icon.name === currentName) continue;
const iconParts = icon.name.split('-');
const iconBase = iconParts[0];
let score = 0;
if (iconBase === baseName) {
score += 50;
const remainingParts = iconParts.slice(1).join('-');
const currentRemaining = parts.slice(1).join('-');
if (remainingParts === currentRemaining) {
score += 20;
}
} else if (icon.name.includes(baseName) || baseName.includes(iconBase)) {
score += 10;
}
if (prefix === currentPrefix) {
score += 5;
}
if (score > 0) {
const existing = relatedScores.get(icon.name) || { score: 0, prefixes: new Set() };
existing.score = Math.max(existing.score, score);
existing.prefixes.add(prefix);
relatedScores.set(icon.name, existing);
}
}
}
return Array.from(relatedScores.entries())
.sort((a, b) => b[1].score - a[1].score)
.slice(0, limit)
.map(([name, data]) => ({ name, prefixes: Array.from(data.prefixes) }));
}
function renderIcons() { function renderIcons() {
const prefix = getCurrentPrefix(); const prefix = getCurrentPrefix();
const filtered = getFilteredIcons(); const filtered = getFilteredIcons();
@@ -300,6 +347,8 @@ function showModal(name, currentPrefix) {
const modalTitle = document.getElementById('modalTitle'); const modalTitle = document.getElementById('modalTitle');
const modalSubtitle = document.getElementById('modalSubtitle'); const modalSubtitle = document.getElementById('modalSubtitle');
const modalVariations = document.getElementById('modalVariations'); const modalVariations = document.getElementById('modalVariations');
const relatedSection = document.getElementById('relatedSection');
const relatedIcons = document.getElementById('relatedIcons');
modalTitle.textContent = name; modalTitle.textContent = name;
@@ -337,6 +386,29 @@ function showModal(name, currentPrefix) {
}); });
}); });
const related = findRelatedIcons(name, currentPrefix);
if (related.length > 0) {
relatedSection.style.display = 'block';
relatedIcons.innerHTML = related.map(({ name: relatedName, prefixes }) => {
const relatedPrefix = prefixes.includes(currentPrefix) ? currentPrefix : prefixes[0];
const relatedClassName = `fi-${relatedPrefix}-${relatedName}`;
return `
<div class="related-icon" data-name="${relatedName}" data-prefix="${relatedPrefix}">
<span class="fi ${relatedClassName} related-icon-symbol"></span>
<div class="related-icon-name">${relatedName}</div>
</div>
`;
}).join('');
relatedIcons.querySelectorAll('.related-icon').forEach(el => {
el.addEventListener('click', () => {
showModal(el.dataset.name, el.dataset.prefix);
});
});
} else {
relatedSection.style.display = 'none';
}
modalOverlay.classList.add('show'); modalOverlay.classList.add('show');
animateModal(); animateModal();
} }