feat: 플로팅 메모창 구현 및 문서 삭제 기능 안정화
✨ 새로운 기능: - 플로팅 메모창: 드래그 가능한 독립적인 메모/하이라이트 창 - 크기 조절: 마우스로 창 크기 자유롭게 조정 - 위치 이동: 헤더 드래그로 원하는 위치에 배치 - 메모 검색: 실시간 메모 내용 검색 및 필터링 - 하이라이트 이동: 메모창에서 문서 내 하이라이트 위치로 스크롤 🛠️ 개선사항: - 문서 삭제 안정화: 외래키 제약조건 해결 (Note->Highlight->Document 순서) - 레이아웃 개선: 문서 뷰어가 전체 화면 사용 가능 - 사용자 경험: 사이드바 대신 플로팅 윈도우로 더 유연한 UI - 캐시 버스팅: JavaScript 파일 버전 관리 개선 🐛 버그 수정: - 중복 스크립트 로드 문제 해결 - 하이라이트 메모 업데이트 API 오류 수정 - 문서 삭제 시 500 에러 해결
This commit is contained in:
@@ -30,6 +30,18 @@ window.hierarchyApp = function() {
|
||||
highlightMenuPosition: { x: 0, y: 0 },
|
||||
highlightMode: false, // 하이라이트 모드 토글
|
||||
|
||||
// 메모 사이드바
|
||||
memoSearchQuery: '',
|
||||
filteredNotes: [],
|
||||
|
||||
// 플로팅 메모창
|
||||
showFloatingMemo: false,
|
||||
floatingMemoPosition: { x: 100, y: 100 },
|
||||
floatingMemoSize: { width: 400, height: 500 },
|
||||
isDragging: false,
|
||||
isResizing: false,
|
||||
dragOffset: { x: 0, y: 0 },
|
||||
|
||||
// 드래그 앤 드롭
|
||||
draggedDocument: null,
|
||||
|
||||
@@ -193,6 +205,9 @@ window.hierarchyApp = function() {
|
||||
this.highlights = highlights || [];
|
||||
this.notes = notes || [];
|
||||
|
||||
// 메모 사이드바용 필터링된 노트 초기화
|
||||
this.filterMemos();
|
||||
|
||||
console.log(`📝 최종 저장: 하이라이트 ${this.highlights.length}개, 메모 ${this.notes.length}개`);
|
||||
|
||||
if (this.highlights.length > 0) {
|
||||
@@ -1108,6 +1123,119 @@ window.authModal = function() {
|
||||
resetLoginForm() {
|
||||
this.loginForm = { email: '', password: '' };
|
||||
this.loginError = null;
|
||||
},
|
||||
|
||||
// 메모 필터링
|
||||
filterMemos() {
|
||||
console.log('🔍 메모 필터링 시작:', {
|
||||
totalNotes: this.notes.length,
|
||||
searchQuery: this.memoSearchQuery,
|
||||
notes: this.notes
|
||||
});
|
||||
|
||||
if (!this.memoSearchQuery.trim()) {
|
||||
this.filteredNotes = this.notes.filter(note => !note.highlight_id);
|
||||
console.log('📝 독립 메모 필터링 결과:', this.filteredNotes.length, this.filteredNotes);
|
||||
return;
|
||||
}
|
||||
|
||||
const query = this.memoSearchQuery.toLowerCase();
|
||||
this.filteredNotes = this.notes
|
||||
.filter(note => !note.highlight_id) // 독립 메모만
|
||||
.filter(note => note.content.toLowerCase().includes(query));
|
||||
console.log('🔍 검색 결과:', this.filteredNotes.length, this.filteredNotes);
|
||||
},
|
||||
|
||||
// 하이라이트로 스크롤
|
||||
scrollToHighlight(highlightId) {
|
||||
const highlightElement = document.querySelector(`[data-highlight-id="${highlightId}"]`);
|
||||
if (highlightElement) {
|
||||
highlightElement.scrollIntoView({
|
||||
behavior: 'smooth',
|
||||
block: 'center'
|
||||
});
|
||||
|
||||
// 잠시 강조 효과
|
||||
highlightElement.style.boxShadow = '0 0 10px rgba(59, 130, 246, 0.5)';
|
||||
setTimeout(() => {
|
||||
highlightElement.style.boxShadow = '';
|
||||
}, 2000);
|
||||
}
|
||||
},
|
||||
|
||||
// 플로팅 윈도우 드래그 시작
|
||||
startDragging(event) {
|
||||
this.isDragging = true;
|
||||
this.dragOffset.x = event.clientX - this.floatingMemoPosition.x;
|
||||
this.dragOffset.y = event.clientY - this.floatingMemoPosition.y;
|
||||
|
||||
document.addEventListener('mousemove', this.handleDragging.bind(this));
|
||||
document.addEventListener('mouseup', this.stopDragging.bind(this));
|
||||
event.preventDefault();
|
||||
},
|
||||
|
||||
// 드래그 중
|
||||
handleDragging(event) {
|
||||
if (!this.isDragging) return;
|
||||
|
||||
const newX = event.clientX - this.dragOffset.x;
|
||||
const newY = event.clientY - this.dragOffset.y;
|
||||
|
||||
// 화면 경계 제한
|
||||
const maxX = window.innerWidth - this.floatingMemoSize.width;
|
||||
const maxY = window.innerHeight - this.floatingMemoSize.height;
|
||||
|
||||
this.floatingMemoPosition.x = Math.max(0, Math.min(newX, maxX));
|
||||
this.floatingMemoPosition.y = Math.max(0, Math.min(newY, maxY));
|
||||
},
|
||||
|
||||
// 드래그 종료
|
||||
stopDragging() {
|
||||
this.isDragging = false;
|
||||
document.removeEventListener('mousemove', this.handleDragging.bind(this));
|
||||
document.removeEventListener('mouseup', this.stopDragging.bind(this));
|
||||
},
|
||||
|
||||
// 리사이즈 시작
|
||||
startResizing(event) {
|
||||
this.isResizing = true;
|
||||
document.addEventListener('mousemove', this.handleResizing.bind(this));
|
||||
document.addEventListener('mouseup', this.stopResizing.bind(this));
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
},
|
||||
|
||||
// 리사이즈 중
|
||||
handleResizing(event) {
|
||||
if (!this.isResizing) return;
|
||||
|
||||
const rect = event.target.closest('.fixed').getBoundingClientRect();
|
||||
const newWidth = event.clientX - rect.left;
|
||||
const newHeight = event.clientY - rect.top;
|
||||
|
||||
// 최소/최대 크기 제한
|
||||
this.floatingMemoSize.width = Math.max(300, Math.min(newWidth, 800));
|
||||
this.floatingMemoSize.height = Math.max(200, Math.min(newHeight, 600));
|
||||
},
|
||||
|
||||
// 리사이즈 종료
|
||||
stopResizing() {
|
||||
this.isResizing = false;
|
||||
document.removeEventListener('mousemove', this.handleResizing.bind(this));
|
||||
document.removeEventListener('mouseup', this.stopResizing.bind(this));
|
||||
},
|
||||
|
||||
// 플로팅 메모창 크기 토글
|
||||
toggleFloatingMemoSize() {
|
||||
if (this.floatingMemoSize.width === 400) {
|
||||
// 큰 크기로
|
||||
this.floatingMemoSize.width = 600;
|
||||
this.floatingMemoSize.height = 700;
|
||||
} else {
|
||||
// 기본 크기로
|
||||
this.floatingMemoSize.width = 400;
|
||||
this.floatingMemoSize.height = 500;
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user