사이드바의 카테고리 섹션에 이모지를 사용하고 있지만, 공개된 사용자 지정 섹션도 하나 있습니다. 카테고리 섹션에 비해 이 섹션이 너무 “단조로워” 보이지 않도록 동일한 컬러 아이콘을 사용하고 싶습니다.
이것이 가능한가요?
사이드바의 카테고리 섹션에 이모지를 사용하고 있지만, 공개된 사용자 지정 섹션도 하나 있습니다. 카테고리 섹션에 비해 이 섹션이 너무 “단조로워” 보이지 않도록 동일한 컬러 아이콘을 사용하고 싶습니다.
이것이 가능한가요?
ChatGPT와 Claude의 도움으로 작동하게 만들었고, 매우 커스터마이징할 수 있게 되었습니다:
해 보고 싶으시다면, 새로운 컴포넌트를 생성하고 CSS 탭에 다음 내용을 추가하세요:
.sidebar-section-link-prefix .emoji.prefix-emoji {
width: 1rem !important;
height: 1rem !important;
}
제 경우
1rem이 아주 잘 작동합니다. 포럼/커뮤니티에 맞게 조정해 주세요.
그런 다음 JS 탭에서:
import { apiInitializer } from "discourse/lib/api";
import { emojiUrlFor } from "discourse/lib/text";
export default apiInitializer("0.11.1", (api) => {
// Map section names to IDs
const sectionIds = {
"community": 1,
"tiago": 2,
"personal-section": 3,
// Add more sections here
};
// Map of [sectionId, itemName] to emoji names
const iconReplacements = {
// Community section (ID: 1)
"1,admin": "wrench",
"1,review": "triangular_flag_on_post",
"1,everything": "books",
"1,my-posts": "writing_hand",
"1,my-messages": "envelope_with_arrow",
// Tiago section (ID: 2)
"2,Journal": "notebook_with_decorative_cover",
"2,Music": "musical_note",
"2,About": "bust_in_silhouette",
// Personal section (ID: 3)
"3,Backups": "floppy_disk",
"3,Scheduled": "clock3",
"3,Staff": "lock",
"3,Components": "electric_plug",
};
function replaceIcons() {
Object.entries(sectionIds).forEach(([sectionName, sectionId]) => {
Object.entries(iconReplacements).forEach(([key, emojiName]) => {
const [keyId, itemName] = key.split(',');
// Only process if this replacement is for the current section
if (parseInt(keyId) === sectionId) {
const url = emojiUrlFor(emojiName);
// Try multiple selectors to catch both hamburger menu and fixed sidebar
const selectors = [
// Fixed sidebar selector
`div[data-section-name="${sectionName}"] li[data-list-item-name="${itemName}"] .sidebar-section-link-prefix.icon`,
// Hamburger menu selector (more specific)
`.menu-panel div[data-section-name="${sectionName}"] li[data-list-item-name="${itemName}"] .sidebar-section-link-prefix.icon`,
// Generic fallback
`[data-section-name="${sectionName}"] [data-list-item-name="${itemName}"] .sidebar-section-link-prefix.icon`
];
for (const selector of selectors) {
const prefix = document.querySelector(selector);
if (prefix && url) {
// Check if it's already replaced to avoid unnecessary DOM manipulation
if (!prefix.querySelector('.prefix-emoji')) {
prefix.innerHTML = `
<img src="${url}"
title="${emojiName}"
alt="${emojiName}"
class="emoji prefix-emoji">
`;
}
break; // Exit loop once we find and replace the icon
}
}
}
});
});
}
// Run on page change
api.onPageChange(replaceIcons);
// Also run with delay to catch dynamically loaded content
api.onPageChange(() => {
setTimeout(replaceIcons, 100);
setTimeout(replaceIcons, 500);
});
// Enhanced MutationObserver to catch sidebar changes
const observer = new MutationObserver((mutations) => {
let shouldReplace = false;
mutations.forEach((mutation) => {
// Watch for added nodes (original functionality)
mutation.addedNodes.forEach((node) => {
if (node.nodeType === Node.ELEMENT_NODE) {
if (node.classList?.contains('menu-panel') ||
node.querySelector?.('.sidebar-sections') ||
node.classList?.contains('sidebar-sections') ||
node.querySelector?.('[data-section-name]')) {
shouldReplace = true;
}
}
});
// Watch for attribute changes on sidebar sections (collapse/expand)
if (mutation.type === 'attributes' && mutation.target.nodeType === Node.ELEMENT_NODE) {
const target = mutation.target;
if (target.matches('[data-section-name]') ||
target.closest('[data-section-name]') ||
target.matches('.sidebar-section') ||
target.closest('.sidebar-section')) {
shouldReplace = true;
}
}
// Watch for childList changes in sidebar sections
if (mutation.type === 'childList' && mutation.target.nodeType === Node.ELEMENT_NODE) {
const target = mutation.target;
if (target.matches('[data-section-name]') ||
target.closest('[data-section-name]') ||
target.querySelector('[data-section-name]')) {
shouldReplace = true;
}
}
});
if (shouldReplace) {
setTimeout(replaceIcons, 50);
}
});
// Start observing with enhanced options
observer.observe(document.body, {
childList: true,
subtree: true,
attributes: true, // Watch for attribute changes
attributeFilter: ['class', 'style', 'aria-expanded'], // Common attributes that change on collapse/expand
});
// Additional event listeners for common sidebar interactions
document.addEventListener('click', (event) => {
// Check if click was on a sidebar section header or toggle button
if (event.target.closest('.sidebar-section-header') ||
event.target.closest('[data-section-name]') ||
event.target.matches('.sidebar-section-toggle')) {
setTimeout(replaceIcons, 100);
}
});
});
이름으로 새로운 커스텀 섹션을 쉽게 추가할 수 있도록 만들었습니다. id를 부여한 후 각 섹션의 매핑을 생성하는 방식입니다.
그 이유는 예를 들어 Journal이라는 제목의 섹션이 2개 있을 수 있지만, 각각에 다른 이모지를 사용하고 싶을 수 있기 때문입니다.
이 기능을 위한 컴포넌트나 플러그인이 있다는 말을 하는 사람이 분명 있을 거라고 확신합니다.
하지만 제 방식대로 모든 것이 작동할 때까지 ChatGPT와 Claude를 오가며 작업하는 것도 여전히 재미있었습니다.
다른 사용자분들에게 도움이 되기를 바랍니다.
조정하거나 개선할 수 있는 부분이 보인다면, 공유해 주세요
JS 코드를 방금 업데이트했는데, 모바일에서 여전히 기본 아이콘이 표시되고 있는 것을 발견했기 때문입니다. 이제 모바일과 데스크톱 모두에서 예상대로 정상 작동합니다.
또 다른 수정 사항: 섹션을 접었다가 다시 펼치면 이모지가 사라지고 원래 아이콘으로 대체되었습니다. 지금은 정상적으로 작동하며, JS 코드를 다시 업데이트했습니다.
방법을 찾아주셔서 감사합니다 – 이제 카테고리에서 이모지를 지원하므로, 이는 핵심 기능으로 매우 자연스럽게 어울릴 것 같습니다.
동의합니다. 언젠가 네이티브 기능이 되었으면 좋겠어요. 그때까지 이 커스텀 컴포넌트가 제 역할을 잘하고 있어요 ![]()
This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.