From cae90ec3dce60deafe860c211832949be1ee38cd Mon Sep 17 00:00:00 2001 From: despiegk Date: Sun, 26 Oct 2025 09:15:51 +0400 Subject: [PATCH] ... --- collections/documents/images/new-file.md | 2 - static/js/file-tree-actions.js | 114 +++++++++-------------- static/js/file-tree.js | 16 ++-- 3 files changed, 55 insertions(+), 77 deletions(-) delete mode 100644 collections/documents/images/new-file.md diff --git a/collections/documents/images/new-file.md b/collections/documents/images/new-file.md deleted file mode 100644 index 09f37b3..0000000 --- a/collections/documents/images/new-file.md +++ /dev/null @@ -1,2 +0,0 @@ -# New File - diff --git a/static/js/file-tree-actions.js b/static/js/file-tree-actions.js index ab21030..512b749 100644 --- a/static/js/file-tree-actions.js +++ b/static/js/file-tree-actions.js @@ -35,43 +35,37 @@ class FileTreeActions { 'new-file': async function(path, isDir) { if (!isDir) return; - - await this.showInputDialog('Enter filename:', 'new-file.md', async (filename) => { - if (filename) { - const fullPath = `${path}/${filename}`.replace(/\/+/g, '/'); - await this.webdavClient.put(fullPath, '# New File\n\n'); - await this.fileTree.load(); - showNotification(`Created ${filename}`, 'success'); - await this.editor.loadFile(fullPath); - } - }); + const filename = await this.showInputDialog('Enter filename:', 'new-file.md'); + if (filename) { + const fullPath = `${path}/${filename}`.replace(/\/+/g, '/'); + await this.webdavClient.put(fullPath, '# New File\n\n'); + await this.fileTree.load(); + showNotification(`Created ${filename}`, 'success'); + await this.editor.loadFile(fullPath); + } }, 'new-folder': async function(path, isDir) { if (!isDir) return; - - await this.showInputDialog('Enter folder name:', 'new-folder', async (foldername) => { - if (foldername) { - const fullPath = `${path}/${foldername}`.replace(/\/+/g, '/'); - await this.webdavClient.mkcol(fullPath); - await this.fileTree.load(); - showNotification(`Created folder ${foldername}`, 'success'); - } - }); + const foldername = await this.showInputDialog('Enter folder name:', 'new-folder'); + if (foldername) { + const fullPath = `${path}/${foldername}`.replace(/\/+/g, '/'); + await this.webdavClient.mkcol(fullPath); + await this.fileTree.load(); + showNotification(`Created folder ${foldername}`, 'success'); + } }, rename: async function(path, isDir) { const oldName = path.split('/').pop(); - await this.showInputDialog('Rename to:', oldName, async (newName) => { - if (newName && newName !== oldName) { - const parentPath = path.substring(0, path.lastIndexOf('/')); + const newName = await this.showInputDialog('Rename to:', oldName); + if (newName && newName !== oldName) { + const parentPath = path.substring(0, path.lastIndexOf('/')); const newPath = parentPath ? `${parentPath}/${newName}` : newName; - - await this.webdavClient.move(path, newPath); - await this.fileTree.load(); - showNotification('Renamed', 'success'); - } - }); + await this.webdavClient.move(path, newPath); + await this.fileTree.load(); + showNotification('Renamed', 'success'); + } }, copy: async function(path, isDir) { @@ -146,49 +140,36 @@ class FileTreeActions { }; // Modern dialog implementations - async showInputDialog(title, placeholder = '', onConfirm) { + async showInputDialog(title, placeholder = '') { return new Promise((resolve) => { const dialog = this.createInputDialog(title, placeholder); const input = dialog.querySelector('input'); const confirmBtn = dialog.querySelector('.btn-primary'); - const cancelBtn = dialog.querySelector('.btn-secondary'); - const closeBtn = dialog.querySelector('.btn-close'); const cleanup = () => { dialog.remove(); const backdrop = document.querySelector('.modal-backdrop'); - if (backdrop) { - backdrop.remove(); - } + if (backdrop) backdrop.remove(); document.body.classList.remove('modal-open'); - resolve(); }; - confirmBtn.onclick = async () => { - await onConfirm(input.value.trim()); + confirmBtn.onclick = () => { + resolve(input.value.trim()); cleanup(); }; - cancelBtn.onclick = () => { + dialog.addEventListener('hidden.bs.modal', () => { + resolve(null); cleanup(); - }; - - closeBtn.onclick = () => { - cleanup(); - }; + }); input.onkeypress = (e) => { - if (e.key === 'Enter') { - e.preventDefault(); - confirmBtn.click(); - } - if (e.key === 'Escape') { - cancelBtn.click(); - } + if (e.key === 'Enter') confirmBtn.click(); }; document.body.appendChild(dialog); - document.body.classList.add('modal-open'); + const modal = new bootstrap.Modal(dialog); + modal.show(); input.focus(); input.select(); }); @@ -198,32 +179,27 @@ class FileTreeActions { return new Promise((resolve) => { const dialog = this.createConfirmDialog(title, message); const confirmBtn = dialog.querySelector('.btn-danger'); - const cancelBtn = dialog.querySelector('.btn-secondary'); - const closeBtn = dialog.querySelector('.btn-close'); - + const cleanup = () => { dialog.remove(); - document.querySelector('.modal-backdrop').remove(); + const backdrop = document.querySelector('.modal-backdrop'); + if (backdrop) backdrop.remove(); document.body.classList.remove('modal-open'); }; - + confirmBtn.onclick = () => { resolve(true); cleanup(); }; - - cancelBtn.onclick = () => { - resolve(false); - cleanup(); - }; - closeBtn.onclick = () => { + dialog.addEventListener('hidden.bs.modal', () => { resolve(false); cleanup(); - }; - + }); + document.body.appendChild(dialog); - document.body.classList.add('modal-open'); + const modal = new bootstrap.Modal(dialog); + modal.show(); confirmBtn.focus(); }); } @@ -242,13 +218,13 @@ class FileTreeActions { @@ -273,14 +249,14 @@ class FileTreeActions { diff --git a/static/js/file-tree.js b/static/js/file-tree.js index 7774253..29a3fd6 100644 --- a/static/js/file-tree.js +++ b/static/js/file-tree.js @@ -39,13 +39,17 @@ class FileTree { // Context menu this.container.addEventListener('contextmenu', (e) => { const node = e.target.closest('.tree-node'); - if (!node) return; - e.preventDefault(); - const path = node.dataset.path; - const isDir = node.dataset.isdir === 'true'; - - window.showContextMenu(e.clientX, e.clientY, { path, isDir }); + + if (node) { + // Clicked on a node + const path = node.dataset.path; + const isDir = node.dataset.isdir === 'true'; + window.showContextMenu(e.clientX, e.clientY, { path, isDir }); + } else if (e.target === this.container) { + // Clicked on the empty space in the file tree container + window.showContextMenu(e.clientX, e.clientY, { path: '', isDir: true }); + } }); }