|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import Utils from "./utils.js";
|
|
|
import DB from "./db.js";
|
|
|
|
|
|
const Documents = {
|
|
|
currentFilter: "all",
|
|
|
searchQuery: "",
|
|
|
|
|
|
init() {
|
|
|
this.setupEventListeners();
|
|
|
this.loadDocuments();
|
|
|
},
|
|
|
|
|
|
setupEventListeners() {
|
|
|
|
|
|
document.getElementById("btn-documents").addEventListener("click", () => {
|
|
|
this.toggleSidebar();
|
|
|
});
|
|
|
|
|
|
document.getElementById("btn-close-sidebar").addEventListener("click", () => {
|
|
|
this.toggleSidebar();
|
|
|
});
|
|
|
|
|
|
|
|
|
document.querySelectorAll(".filter-tab").forEach(tab => {
|
|
|
tab.addEventListener("click", () => {
|
|
|
const filter = tab.getAttribute("data-filter");
|
|
|
this.setFilter(filter);
|
|
|
});
|
|
|
});
|
|
|
|
|
|
|
|
|
document.getElementById("search-docs").addEventListener(
|
|
|
"input",
|
|
|
Utils.debounce(e => {
|
|
|
this.searchQuery = e.target.value;
|
|
|
this.loadDocuments();
|
|
|
}, 300)
|
|
|
);
|
|
|
},
|
|
|
|
|
|
toggleSidebar() {
|
|
|
const sidebar = document.getElementById("sidebar-docs");
|
|
|
const main = document.querySelector(".main-container");
|
|
|
|
|
|
sidebar.classList.toggle("collapsed");
|
|
|
main.classList.toggle("sidebar-open");
|
|
|
|
|
|
if (!sidebar.classList.contains("collapsed")) {
|
|
|
this.loadDocuments();
|
|
|
}
|
|
|
},
|
|
|
|
|
|
setFilter(filter) {
|
|
|
this.currentFilter = filter;
|
|
|
|
|
|
|
|
|
document.querySelectorAll(".filter-tab").forEach(tab => {
|
|
|
tab.classList.toggle("active", tab.getAttribute("data-filter") === filter);
|
|
|
});
|
|
|
|
|
|
this.loadDocuments();
|
|
|
},
|
|
|
|
|
|
async loadDocuments() {
|
|
|
const container = document.getElementById("documents-list");
|
|
|
|
|
|
try {
|
|
|
let docs;
|
|
|
|
|
|
if (this.searchQuery) {
|
|
|
docs = await DB.searchDocuments(this.searchQuery);
|
|
|
} else {
|
|
|
docs = await DB.getAllDocuments(this.currentFilter);
|
|
|
}
|
|
|
|
|
|
if (docs.length === 0) {
|
|
|
container.innerHTML = `
|
|
|
<div class="empty-state">
|
|
|
<p>π No documents found</p>
|
|
|
<p class="hint">Create your first document!</p>
|
|
|
</div>
|
|
|
`;
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
container.innerHTML = docs
|
|
|
.map(
|
|
|
doc => `
|
|
|
<div class="doc-item ${window.app?.currentDocId === doc.id ? "active" : ""}" data-id="${doc.id}">
|
|
|
<div class="doc-item-title">
|
|
|
${doc.favorite ? "β " : ""}${Utils.truncate(doc.title, 40)}
|
|
|
</div>
|
|
|
<div class="doc-item-meta">
|
|
|
${doc.wordCount || 0} words β’ ${Utils.formatDate(doc.updatedAt)}
|
|
|
</div>
|
|
|
</div>
|
|
|
`
|
|
|
)
|
|
|
.join("");
|
|
|
|
|
|
|
|
|
container.querySelectorAll(".doc-item").forEach(item => {
|
|
|
item.addEventListener("click", () => {
|
|
|
const id = item.getAttribute("data-id");
|
|
|
window.app?.loadDocument(id);
|
|
|
});
|
|
|
|
|
|
|
|
|
item.addEventListener("contextmenu", e => {
|
|
|
e.preventDefault();
|
|
|
this.showContextMenu(e, item.getAttribute("data-id"));
|
|
|
});
|
|
|
});
|
|
|
} catch (err) {
|
|
|
console.error("Failed to load documents:", err);
|
|
|
Utils.toast.error("Failed to load documents");
|
|
|
}
|
|
|
},
|
|
|
|
|
|
showContextMenu(e, docId) {
|
|
|
|
|
|
const existingMenu = document.querySelector(".context-menu");
|
|
|
if (existingMenu) existingMenu.remove();
|
|
|
|
|
|
|
|
|
const menu = document.createElement("div");
|
|
|
menu.className = "context-menu";
|
|
|
menu.innerHTML = `
|
|
|
<div class="context-menu-item" data-action="favorite">
|
|
|
<span class="context-icon">β</span>
|
|
|
<span>Toggle Favorite</span>
|
|
|
</div>
|
|
|
<div class="context-menu-item" data-action="rename">
|
|
|
<span class="context-icon">βοΈ</span>
|
|
|
<span>Rename</span>
|
|
|
</div>
|
|
|
<div class="context-menu-divider"></div>
|
|
|
<div class="context-menu-item danger" data-action="delete">
|
|
|
<span class="context-icon">ποΈ</span>
|
|
|
<span>Delete</span>
|
|
|
</div>
|
|
|
`;
|
|
|
|
|
|
|
|
|
document.body.appendChild(menu);
|
|
|
|
|
|
|
|
|
const menuRect = menu.getBoundingClientRect();
|
|
|
const viewportWidth = window.innerWidth;
|
|
|
const viewportHeight = window.innerHeight;
|
|
|
|
|
|
|
|
|
let left = e.pageX;
|
|
|
let top = e.pageY;
|
|
|
|
|
|
|
|
|
if (left + menuRect.width > viewportWidth) {
|
|
|
left = viewportWidth - menuRect.width - 10;
|
|
|
}
|
|
|
|
|
|
|
|
|
if (top + menuRect.height > viewportHeight) {
|
|
|
top = viewportHeight - menuRect.height - 10;
|
|
|
}
|
|
|
|
|
|
menu.style.left = `${left}px`;
|
|
|
menu.style.top = `${top}px`;
|
|
|
|
|
|
|
|
|
menu.querySelectorAll(".context-menu-item").forEach(item => {
|
|
|
item.addEventListener("click", async () => {
|
|
|
const action = item.getAttribute("data-action");
|
|
|
|
|
|
switch (action) {
|
|
|
case "favorite":
|
|
|
await this.toggleFavorite(docId);
|
|
|
break;
|
|
|
case "rename":
|
|
|
await this.renameDocument(docId);
|
|
|
break;
|
|
|
case "delete":
|
|
|
const confirmDelete = confirm("Are you sure you want to delete this document?");
|
|
|
if (confirmDelete) {
|
|
|
await this.deleteDocument(docId);
|
|
|
}
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
menu.remove();
|
|
|
});
|
|
|
});
|
|
|
|
|
|
|
|
|
const closeMenu = event => {
|
|
|
if (!menu.contains(event.target)) {
|
|
|
menu.remove();
|
|
|
document.removeEventListener("click", closeMenu);
|
|
|
}
|
|
|
};
|
|
|
|
|
|
setTimeout(() => {
|
|
|
document.addEventListener("click", closeMenu);
|
|
|
}, 10);
|
|
|
},
|
|
|
|
|
|
async renameDocument(docId) {
|
|
|
const doc = await DB.getDocument(docId);
|
|
|
if (!doc) return;
|
|
|
|
|
|
const newTitle = prompt("Enter new title:", doc.title);
|
|
|
if (newTitle && newTitle !== doc.title) {
|
|
|
await DB.updateDocument(docId, { title: newTitle });
|
|
|
Utils.toast.success("Document renamed!");
|
|
|
this.loadDocuments();
|
|
|
|
|
|
|
|
|
if (window.app?.currentDocId === docId) {
|
|
|
document.getElementById("doc-title").value = newTitle;
|
|
|
}
|
|
|
}
|
|
|
},
|
|
|
|
|
|
async toggleFavorite(docId) {
|
|
|
const isFavorite = await DB.toggleFavorite(docId);
|
|
|
Utils.toast.success(isFavorite ? "Added to favorites" : "Removed from favorites");
|
|
|
this.loadDocuments();
|
|
|
},
|
|
|
|
|
|
async deleteDocument(docId) {
|
|
|
await DB.deleteDocument(docId);
|
|
|
|
|
|
|
|
|
if (window.app?.currentDocId === docId) {
|
|
|
window.app.newDocument();
|
|
|
}
|
|
|
|
|
|
this.loadDocuments();
|
|
|
},
|
|
|
|
|
|
|
|
|
updateActiveDoc(docId) {
|
|
|
document.querySelectorAll(".doc-item").forEach(item => {
|
|
|
item.classList.toggle("active", item.getAttribute("data-id") === docId);
|
|
|
});
|
|
|
}
|
|
|
};
|
|
|
|
|
|
export default Documents;
|
|
|
|