/** * UIManager 모듈 * UI 컴포넌트 및 상태 관리 */ class UIManager { constructor() { console.log('🎨 UIManager 초기화 시작'); // UI 상태 this.showNotesPanel = false; this.showBookmarksPanel = false; this.showBacklinks = false; this.activePanel = 'notes'; // 모달 상태 this.showNoteModal = false; this.showBookmarkModal = false; this.showLinkModal = false; this.showNotesModal = false; this.showBookmarksModal = false; this.showLinksModal = false; this.showBacklinksModal = false; // 기능 메뉴 상태 this.activeFeatureMenu = null; // 검색 상태 this.searchQuery = ''; this.noteSearchQuery = ''; this.filteredNotes = []; // 텍스트 선택 모드 this.textSelectorUISetup = false; console.log('✅ UIManager 초기화 완료'); } /** * 기능 메뉴 토글 */ toggleFeatureMenu(feature) { if (this.activeFeatureMenu === feature) { this.activeFeatureMenu = null; } else { this.activeFeatureMenu = feature; // 해당 기능의 모달 표시 switch(feature) { case 'link': this.showLinksModal = true; break; case 'memo': this.showNotesModal = true; break; case 'bookmark': this.showBookmarksModal = true; break; case 'backlink': this.showBacklinksModal = true; break; } } } /** * 노트 모달 열기 */ openNoteModal(highlight = null) { console.log('📝 노트 모달 열기'); if (highlight) { console.log('🔍 하이라이트와 연결된 노트 모달:', highlight); } this.showNoteModal = true; } /** * 노트 모달 닫기 */ closeNoteModal() { this.showNoteModal = false; } /** * 링크 모달 열기 */ openLinkModal() { console.log('🔗 링크 모달 열기'); console.log('🔗 showLinksModal 설정 전:', this.showLinksModal); this.showLinksModal = true; this.showLinkModal = true; // 기존 호환성 console.log('🔗 showLinksModal 설정 후:', this.showLinksModal); } /** * 링크 모달 닫기 */ closeLinkModal() { this.showLinksModal = false; this.showLinkModal = false; } /** * 북마크 모달 닫기 */ closeBookmarkModal() { this.showBookmarkModal = false; } /** * 검색 결과 하이라이트 */ highlightSearchResults(element, searchText) { if (!searchText.trim()) return; // 기존 검색 하이라이트 제거 const existingHighlights = element.querySelectorAll('.search-highlight'); existingHighlights.forEach(highlight => { const parent = highlight.parentNode; parent.replaceChild(document.createTextNode(highlight.textContent), highlight); parent.normalize(); }); if (!searchText) return; // 새로운 검색 하이라이트 적용 const walker = document.createTreeWalker( element, NodeFilter.SHOW_TEXT, null, false ); const textNodes = []; let node; while (node = walker.nextNode()) { textNodes.push(node); } const searchRegex = new RegExp(`(${searchText.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')})`, 'gi'); textNodes.forEach(textNode => { const text = textNode.textContent; if (searchRegex.test(text)) { const highlightedHTML = text.replace(searchRegex, '$1'); const wrapper = document.createElement('div'); wrapper.innerHTML = highlightedHTML; const fragment = document.createDocumentFragment(); while (wrapper.firstChild) { fragment.appendChild(wrapper.firstChild); } textNode.parentNode.replaceChild(fragment, textNode); } }); } /** * 노트 검색 필터링 */ filterNotes(notes) { if (!this.noteSearchQuery.trim()) { this.filteredNotes = notes; return notes; } const query = this.noteSearchQuery.toLowerCase(); this.filteredNotes = notes.filter(note => note.content.toLowerCase().includes(query) || (note.tags && note.tags.some(tag => tag.toLowerCase().includes(query))) ); return this.filteredNotes; } /** * 텍스트 선택 모드 UI 설정 */ setupTextSelectorUI() { console.log('🔧 setupTextSelectorUI 함수 실행됨'); // 중복 실행 방지 if (this.textSelectorUISetup) { console.log('⚠️ 텍스트 선택 모드 UI가 이미 설정됨 - 중복 실행 방지'); return; } // 헤더 숨기기 const header = document.querySelector('header'); if (header) { header.style.display = 'none'; } // 안내 메시지 표시 const messageDiv = document.createElement('div'); messageDiv.id = 'text-selection-message'; messageDiv.className = 'fixed top-4 left-1/2 transform -translate-x-1/2 bg-blue-500 text-white px-6 py-3 rounded-lg shadow-lg z-50'; messageDiv.innerHTML = `
연결할 텍스트를 드래그하여 선택해주세요
`; document.body.appendChild(messageDiv); this.textSelectorUISetup = true; console.log('✅ 텍스트 선택 모드 UI 설정 완료'); } /** * 텍스트 선택 확인 UI 표시 */ showTextSelectionConfirm(selectedText, startOffset, endOffset) { // 기존 확인 UI 제거 const existingConfirm = document.getElementById('text-selection-confirm'); if (existingConfirm) { existingConfirm.remove(); } // 확인 UI 생성 const confirmDiv = document.createElement('div'); confirmDiv.id = 'text-selection-confirm'; confirmDiv.className = 'fixed top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 bg-white border border-gray-300 rounded-lg shadow-xl p-6 z-50 max-w-md'; confirmDiv.innerHTML = `

선택된 텍스트

"${selectedText}"

`; document.body.appendChild(confirmDiv); } /** * 링크 생성 UI 표시 */ showLinkCreationUI() { console.log('🔗 링크 생성 UI 표시'); this.openLinkModal(); } /** * 성공 메시지 표시 */ showSuccessMessage(message) { const messageDiv = document.createElement('div'); messageDiv.className = 'fixed top-4 right-4 bg-green-500 text-white px-6 py-3 rounded-lg shadow-lg z-50 flex items-center space-x-2'; messageDiv.innerHTML = ` ${message} `; document.body.appendChild(messageDiv); // 3초 후 자동 제거 setTimeout(() => { if (messageDiv.parentNode) { messageDiv.parentNode.removeChild(messageDiv); } }, 3000); } /** * 오류 메시지 표시 */ showErrorMessage(message) { const messageDiv = document.createElement('div'); messageDiv.className = 'fixed top-4 right-4 bg-red-500 text-white px-6 py-3 rounded-lg shadow-lg z-50 flex items-center space-x-2'; messageDiv.innerHTML = ` ${message} `; document.body.appendChild(messageDiv); // 5초 후 자동 제거 setTimeout(() => { if (messageDiv.parentNode) { messageDiv.parentNode.removeChild(messageDiv); } }, 5000); } /** * 로딩 스피너 표시 */ showLoadingSpinner(container, message = '로딩 중...') { const spinner = document.createElement('div'); spinner.className = 'flex items-center justify-center py-8'; spinner.innerHTML = `
${message} `; if (container) { container.innerHTML = ''; container.appendChild(spinner); } return spinner; } /** * 로딩 스피너 제거 */ hideLoadingSpinner(container) { if (container) { const spinner = container.querySelector('.animate-spin'); if (spinner && spinner.parentElement) { spinner.parentElement.remove(); } } } /** * 모달 배경 클릭 시 닫기 */ handleModalBackgroundClick(event, modalId) { if (event.target === event.currentTarget) { switch(modalId) { case 'notes': this.closeNoteModal(); break; case 'bookmarks': this.closeBookmarkModal(); break; case 'links': this.closeLinkModal(); break; } } } /** * 패널 토글 */ togglePanel(panelType) { switch(panelType) { case 'notes': this.showNotesPanel = !this.showNotesPanel; if (this.showNotesPanel) { this.showBookmarksPanel = false; this.activePanel = 'notes'; } break; case 'bookmarks': this.showBookmarksPanel = !this.showBookmarksPanel; if (this.showBookmarksPanel) { this.showNotesPanel = false; this.activePanel = 'bookmarks'; } break; case 'backlinks': this.showBacklinks = !this.showBacklinks; break; } } /** * 검색 쿼리 업데이트 */ updateSearchQuery(query) { this.searchQuery = query; } /** * 노트 검색 쿼리 업데이트 */ updateNoteSearchQuery(query) { this.noteSearchQuery = query; } /** * UI 상태 초기화 */ resetUIState() { this.showNotesPanel = false; this.showBookmarksPanel = false; this.showBacklinks = false; this.activePanel = 'notes'; this.activeFeatureMenu = null; this.searchQuery = ''; this.noteSearchQuery = ''; this.filteredNotes = []; } /** * 모든 모달 닫기 */ closeAllModals() { this.showNoteModal = false; this.showBookmarkModal = false; this.showLinkModal = false; this.showNotesModal = false; this.showBookmarksModal = false; this.showLinksModal = false; this.showBacklinksModal = false; } } // 전역으로 내보내기 window.UIManager = UIManager;