/** * UI 렌더링 및 이벤트를 관리하는 오케스트레이터 (Orchestrator) */ import { API } from './api.js'; import { createMemoCardHtml } from './components/MemoCard.js'; import { renderGroupList } from './components/SidebarUI.js'; import { ThemeManager } from './components/ThemeManager.js'; import { ModalManager } from './components/ModalManager.js'; import { I18nManager } from './utils/I18nManager.js'; const DOM = { memoGrid: document.getElementById('memoGrid'), groupList: document.getElementById('groupList'), modal: document.getElementById('memoModal'), loadingOverlay: document.getElementById('loadingOverlay'), searchInput: document.getElementById('searchInput'), sidebar: document.getElementById('sidebar'), systemNav: document.getElementById('systemNav'), scrollSentinel: document.getElementById('scrollSentinel') }; export const UI = { /** * 사이드바 및 로그아웃 버튼 초기화 */ initSidebarToggle() { const toggle = document.getElementById('sidebarToggle'); const sidebar = DOM.sidebar; const overlay = document.getElementById('sidebarOverlay'); const logoutBtn = document.getElementById('logoutBtn'); if (toggle && sidebar) { const isCollapsed = localStorage.getItem('sidebarCollapsed') === 'true'; if (isCollapsed) { sidebar.classList.add('collapsed'); const calendar = document.getElementById('calendarContainer'); if (calendar) calendar.style.display = 'none'; } const toggleSidebar = () => { const isMobile = window.innerWidth <= 768; if (isMobile) { sidebar.classList.toggle('mobile-open'); overlay.style.display = sidebar.classList.contains('mobile-open') ? 'block' : 'none'; } else { sidebar.classList.toggle('collapsed'); const collapsed = sidebar.classList.contains('collapsed'); localStorage.setItem('sidebarCollapsed', collapsed); const calendar = document.getElementById('calendarContainer'); if (calendar) calendar.style.display = collapsed ? 'none' : 'block'; } }; toggle.onclick = toggleSidebar; const mobileBtn = document.getElementById('mobileMenuBtn'); if (mobileBtn) mobileBtn.onclick = toggleSidebar; if (overlay) { overlay.onclick = () => { sidebar.classList.remove('mobile-open'); overlay.style.display = 'none'; }; } } if (logoutBtn) { logoutBtn.onclick = () => { if (confirm(I18nManager.t('msg_logout_confirm'))) { window.location.href = '/logout'; } }; } }, /** * 환경 설정 및 테마 엔진 초기화 (ThemeManager 위임) */ async initSettings() { return await ThemeManager.initSettings(); }, /** * 무한 스크롤 초기화 */ initInfiniteScroll(onLoadMore) { if (!DOM.scrollSentinel) return; const observer = new IntersectionObserver((entries) => { if (entries[0].isIntersecting) { onLoadMore(); } }, { threshold: 0.1 }); observer.observe(DOM.scrollSentinel); }, /** * 사이드바 시스템 고정 메뉴 상태 갱신 */ updateSidebar(memos, activeGroup, onGroupClick) { if (!DOM.systemNav) return; DOM.systemNav.querySelectorAll('li').forEach(li => { const group = li.dataset.group; li.className = (group === activeGroup) ? 'active' : ''; li.onclick = () => onGroupClick(group); }); }, /** * 메모 목록 메인 렌더링 (서버 사이드 필터링 결과 기반) */ renderMemos(memos, filters = {}, handlers, isAppend = false) { if (!isAppend) { DOM.memoGrid.innerHTML = ''; } if (!memos || memos.length === 0) { if (!isAppend) { DOM.memoGrid.innerHTML = `