This commit is contained in:
2025-10-26 09:15:51 +04:00
parent b9349425d7
commit cae90ec3dc
3 changed files with 55 additions and 77 deletions

View File

@@ -1,2 +0,0 @@
# New File

View File

@@ -35,43 +35,37 @@ class FileTreeActions {
'new-file': async function(path, isDir) { 'new-file': async function(path, isDir) {
if (!isDir) return; if (!isDir) return;
const filename = await this.showInputDialog('Enter filename:', 'new-file.md');
await this.showInputDialog('Enter filename:', 'new-file.md', async (filename) => { if (filename) {
if (filename) { const fullPath = `${path}/${filename}`.replace(/\/+/g, '/');
const fullPath = `${path}/${filename}`.replace(/\/+/g, '/'); await this.webdavClient.put(fullPath, '# New File\n\n');
await this.webdavClient.put(fullPath, '# New File\n\n'); await this.fileTree.load();
await this.fileTree.load(); showNotification(`Created ${filename}`, 'success');
showNotification(`Created ${filename}`, 'success'); await this.editor.loadFile(fullPath);
await this.editor.loadFile(fullPath); }
}
});
}, },
'new-folder': async function(path, isDir) { 'new-folder': async function(path, isDir) {
if (!isDir) return; if (!isDir) return;
const foldername = await this.showInputDialog('Enter folder name:', 'new-folder');
await this.showInputDialog('Enter folder name:', 'new-folder', async (foldername) => { if (foldername) {
if (foldername) { const fullPath = `${path}/${foldername}`.replace(/\/+/g, '/');
const fullPath = `${path}/${foldername}`.replace(/\/+/g, '/'); await this.webdavClient.mkcol(fullPath);
await this.webdavClient.mkcol(fullPath); await this.fileTree.load();
await this.fileTree.load(); showNotification(`Created folder ${foldername}`, 'success');
showNotification(`Created folder ${foldername}`, 'success'); }
}
});
}, },
rename: async function(path, isDir) { rename: async function(path, isDir) {
const oldName = path.split('/').pop(); const oldName = path.split('/').pop();
await this.showInputDialog('Rename to:', oldName, async (newName) => { const newName = await this.showInputDialog('Rename to:', oldName);
if (newName && newName !== oldName) { if (newName && newName !== oldName) {
const parentPath = path.substring(0, path.lastIndexOf('/')); const parentPath = path.substring(0, path.lastIndexOf('/'));
const newPath = parentPath ? `${parentPath}/${newName}` : newName; const newPath = parentPath ? `${parentPath}/${newName}` : newName;
await this.webdavClient.move(path, newPath);
await this.webdavClient.move(path, newPath); await this.fileTree.load();
await this.fileTree.load(); showNotification('Renamed', 'success');
showNotification('Renamed', 'success'); }
}
});
}, },
copy: async function(path, isDir) { copy: async function(path, isDir) {
@@ -146,49 +140,36 @@ class FileTreeActions {
}; };
// Modern dialog implementations // Modern dialog implementations
async showInputDialog(title, placeholder = '', onConfirm) { async showInputDialog(title, placeholder = '') {
return new Promise((resolve) => { return new Promise((resolve) => {
const dialog = this.createInputDialog(title, placeholder); const dialog = this.createInputDialog(title, placeholder);
const input = dialog.querySelector('input'); const input = dialog.querySelector('input');
const confirmBtn = dialog.querySelector('.btn-primary'); const confirmBtn = dialog.querySelector('.btn-primary');
const cancelBtn = dialog.querySelector('.btn-secondary');
const closeBtn = dialog.querySelector('.btn-close');
const cleanup = () => { const cleanup = () => {
dialog.remove(); dialog.remove();
const backdrop = document.querySelector('.modal-backdrop'); const backdrop = document.querySelector('.modal-backdrop');
if (backdrop) { if (backdrop) backdrop.remove();
backdrop.remove();
}
document.body.classList.remove('modal-open'); document.body.classList.remove('modal-open');
resolve();
}; };
confirmBtn.onclick = async () => { confirmBtn.onclick = () => {
await onConfirm(input.value.trim()); resolve(input.value.trim());
cleanup(); cleanup();
}; };
cancelBtn.onclick = () => { dialog.addEventListener('hidden.bs.modal', () => {
resolve(null);
cleanup(); cleanup();
}; });
closeBtn.onclick = () => {
cleanup();
};
input.onkeypress = (e) => { input.onkeypress = (e) => {
if (e.key === 'Enter') { if (e.key === 'Enter') confirmBtn.click();
e.preventDefault();
confirmBtn.click();
}
if (e.key === 'Escape') {
cancelBtn.click();
}
}; };
document.body.appendChild(dialog); document.body.appendChild(dialog);
document.body.classList.add('modal-open'); const modal = new bootstrap.Modal(dialog);
modal.show();
input.focus(); input.focus();
input.select(); input.select();
}); });
@@ -198,32 +179,27 @@ class FileTreeActions {
return new Promise((resolve) => { return new Promise((resolve) => {
const dialog = this.createConfirmDialog(title, message); const dialog = this.createConfirmDialog(title, message);
const confirmBtn = dialog.querySelector('.btn-danger'); const confirmBtn = dialog.querySelector('.btn-danger');
const cancelBtn = dialog.querySelector('.btn-secondary');
const closeBtn = dialog.querySelector('.btn-close');
const cleanup = () => { const cleanup = () => {
dialog.remove(); dialog.remove();
document.querySelector('.modal-backdrop').remove(); const backdrop = document.querySelector('.modal-backdrop');
if (backdrop) backdrop.remove();
document.body.classList.remove('modal-open'); document.body.classList.remove('modal-open');
}; };
confirmBtn.onclick = () => { confirmBtn.onclick = () => {
resolve(true); resolve(true);
cleanup(); cleanup();
}; };
cancelBtn.onclick = () => {
resolve(false);
cleanup();
};
closeBtn.onclick = () => { dialog.addEventListener('hidden.bs.modal', () => {
resolve(false); resolve(false);
cleanup(); cleanup();
}; });
document.body.appendChild(dialog); document.body.appendChild(dialog);
document.body.classList.add('modal-open'); const modal = new bootstrap.Modal(dialog);
modal.show();
confirmBtn.focus(); confirmBtn.focus();
}); });
} }
@@ -242,13 +218,13 @@ class FileTreeActions {
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<h5 class="modal-title">${title}</h5> <h5 class="modal-title">${title}</h5>
<button type="button" class="btn-close"></button> <button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<input type="text" class="form-control" value="${placeholder}" autofocus> <input type="text" class="form-control" value="${placeholder}" autofocus>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-secondary">Cancel</button> <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-primary">OK</button> <button type="button" class="btn btn-primary">OK</button>
</div> </div>
</div> </div>
@@ -273,14 +249,14 @@ class FileTreeActions {
<div class="modal-content"> <div class="modal-content">
<div class="modal-header border-danger"> <div class="modal-header border-danger">
<h5 class="modal-title text-danger">${title}</h5> <h5 class="modal-title text-danger">${title}</h5>
<button type="button" class="btn-close"></button> <button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<p>${message}</p> <p>${message}</p>
<p class="text-danger small">This action cannot be undone.</p> <p class="text-danger small">This action cannot be undone.</p>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-secondary">Cancel</button> <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-danger">Delete</button> <button type="button" class="btn btn-danger">Delete</button>
</div> </div>
</div> </div>

View File

@@ -39,13 +39,17 @@ class FileTree {
// Context menu // Context menu
this.container.addEventListener('contextmenu', (e) => { this.container.addEventListener('contextmenu', (e) => {
const node = e.target.closest('.tree-node'); const node = e.target.closest('.tree-node');
if (!node) return;
e.preventDefault(); e.preventDefault();
const path = node.dataset.path;
const isDir = node.dataset.isdir === 'true'; if (node) {
// Clicked on a node
window.showContextMenu(e.clientX, e.clientY, { path, isDir }); 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 });
}
}); });
} }