aboutsummaryrefslogtreecommitdiff
path: root/internal/admin/static/editor.js
diff options
context:
space:
mode:
authorhistoria <[not public]>2026-07-04 23:04:00 -0400
committerhistoria <[not public]>2026-07-04 23:04:00 -0400
commit4385244d39b37d003dbb7a66a2ff3b05079b7047 (patch)
tree6498a06879cc4ff06612abed259b1403f071ecb2 /internal/admin/static/editor.js
parentad22ea3526a146ea8ce305c8375c35836465cc30 (diff)
downloadthehouseoficarus-4385244d39b37d003dbb7a66a2ff3b05079b7047.tar.gz
feat: drag/drop files in gui to change directories
Diffstat (limited to 'internal/admin/static/editor.js')
-rw-r--r--internal/admin/static/editor.js82
1 files changed, 74 insertions, 8 deletions
diff --git a/internal/admin/static/editor.js b/internal/admin/static/editor.js
index 35bdbe6..5b2d8ef 100644
--- a/internal/admin/static/editor.js
+++ b/internal/admin/static/editor.js
@@ -52,7 +52,7 @@ function renderList(filter) {
}
function renderDirGroup(name, ids, filter) {
- if (ids.length === 0 && !filter) return '';
+ if (ids.length === 0 && !filter && !name) return '';
var isFiltering = !!filter;
var displayIds = ids;
if (isFiltering) {
@@ -63,15 +63,15 @@ function renderDirGroup(name, ids, filter) {
var displayName = name || editorType.charAt(0).toUpperCase() + editorType.slice(1);
var h = '<div class="dir-group">';
if (name) {
- h += '<div class="dir-header" onclick="toggleDir(\'' + escAttr(name) + '\')">';
+ h += '<div class="dir-header" onclick="toggleDir(\'' + escAttr(name) + '\')" oncontextmenu="showDirContextMenu(event,\'' + escAttr(name) + '\')" ondragover="dirDragOver(event,\'' + escAttr(name) + '\')" ondragleave="dirDragLeave(event)" ondrop="dirDrop(event,\'' + escAttr(name) + '\')">';
h += '<span class="dir-arrow">' + (expanded ? '&#9660;' : '&#9654;') + '</span>';
h += '<span class="dir-name">' + esc(displayName) + '</span>';
h += '<span class="dir-count">' + (isFiltering ? displayIds.length + '/' + ids.length : ids.length) + '</span>';
h += '</div>';
}
- h += '<div class="dir-items"' + (name && !expanded ? ' style="display:none"' : '') + '>';
+ h += '<div class="dir-items"' + (name && !expanded ? ' style="display:none"' : '') + ' ondragover="dirDragOver(event,\'' + (name || '') + '\')" ondragleave="dirDragLeave(event)" ondrop="dirDrop(event,\'' + (name || '') + '\')">';
displayIds.forEach(function(id) {
- h += '<div class="item' + (id === currentID ? ' active' : '') + '" onclick="loadItem(\'' + esc(id) + '\')">' + esc(id) + '</div>';
+ h += '<div class="item' + (id === currentID ? ' active' : '') + '" onclick="loadItem(\'' + esc(id) + '\')" draggable="true" ondragstart="itemDragStart(event,\'' + escAttr(id) + '\')">' + esc(id) + '</div>';
});
h += '</div>';
h += '</div>';
@@ -236,8 +236,7 @@ window.refreshPage = function() { loadList(); if (currentID) loadItem(currentID)
function renderMoveBar() {
var dirs = Object.keys(treeData.dirs).sort();
- var html = '<div class="add-section-bar">';
- html += '<select id="moveDirSelect">';
+ var html = '<select id="moveDirSelect">';
html += '<option value="">Move to directory...</option>';
html += '<option value="">Root</option>';
dirs.forEach(function(dir) {
@@ -245,7 +244,6 @@ function renderMoveBar() {
});
html += '</select>';
html += '<button class="btn btn-primary btn-sm" onclick="moveToDirectory()">Move</button>';
- html += '</div>';
return html;
}
@@ -254,7 +252,6 @@ function moveToDirectory() {
var idx = sel.selectedIndex;
if (idx === 0) return;
var dir = sel.value;
- if (!confirm('Move ' + currentID + ' to ' + (dir || 'Root') + '?')) return;
API.post('/api/' + editorType + '/move', {id: currentID, dir: dir}).then(function() {
notify('Moved ' + currentID + ' to ' + (dir || 'Root'), 'success');
updateUndoBar();
@@ -262,3 +259,72 @@ function moveToDirectory() {
loadItem(currentID);
}).catch(function(e) { notify('Move failed: ' + e.message, 'error'); });
}
+
+function createNewDir() {
+ var name = prompt('New directory name:');
+ if (!name) return;
+ API.post('/api/' + editorType + '/dirs', {action: 'create', name: name}).then(function() {
+ notify('Directory ' + name + ' created', 'success');
+ updateUndoBar();
+ loadList();
+ }).catch(function(e) { notify('Failed to create directory: ' + e.message, 'error'); });
+}
+
+function showDirContextMenu(e, dir) {
+ e.preventDefault();
+ if (dir === 'Root') return;
+ var overlay = document.createElement('div');
+ overlay.className = 'cm-overlay';
+ overlay.onclick = function() { overlay.remove(); menu.remove(); };
+
+ var menu = document.createElement('div');
+ menu.className = 'cm-menu';
+ menu.style.left = e.clientX + 'px';
+ menu.style.top = e.clientY + 'px';
+
+ var delBtn = document.createElement('button');
+ delBtn.textContent = 'Delete Directory';
+ delBtn.className = 'danger';
+ delBtn.onclick = function() {
+ overlay.remove(); menu.remove();
+ API.post('/api/' + editorType + '/dirs', {action: 'delete', dir: dir}).then(function(r) {
+ notify('Deleted directory ' + dir + (r.moved ? ' (' + r.moved + ' files moved to root)' : ''), 'success');
+ updateUndoBar();
+ loadList();
+ }).catch(function(e) { notify('Delete failed: ' + e.message, 'error'); });
+ };
+ menu.appendChild(delBtn);
+
+ document.body.appendChild(overlay);
+ document.body.appendChild(menu);
+}
+
+function dirDragOver(e, dir) {
+ e.preventDefault();
+ e.stopPropagation();
+ e.currentTarget.classList.add('dir-drop-target');
+}
+
+function dirDragLeave(e) {
+ e.currentTarget.classList.remove('dir-drop-target');
+}
+
+function dirDrop(e, dir) {
+ e.preventDefault();
+ e.stopPropagation();
+ e.currentTarget.classList.remove('dir-drop-target');
+ if (dir === 'Root') dir = '';
+ var id = e.dataTransfer.getData('text/plain');
+ if (!id) return;
+ API.post('/api/' + editorType + '/move', {id: id, dir: dir}).then(function() {
+ notify('Moved ' + id + ' to ' + (dir || 'Root'), 'success');
+ updateUndoBar();
+ loadList();
+ if (currentID === id) loadItem(id);
+ }).catch(function(e) { notify('Move failed: ' + e.message, 'error'); });
+}
+
+function itemDragStart(e, id) {
+ e.dataTransfer.setData('text/plain', id);
+ e.dataTransfer.effectAllowed = 'move';
+}