diff options
| author | historia <[not public]> | 2026-07-04 23:04:00 -0400 |
|---|---|---|
| committer | historia <[not public]> | 2026-07-04 23:04:00 -0400 |
| commit | 4385244d39b37d003dbb7a66a2ff3b05079b7047 (patch) | |
| tree | 6498a06879cc4ff06612abed259b1403f071ecb2 /internal/admin/static/editor.js | |
| parent | ad22ea3526a146ea8ce305c8375c35836465cc30 (diff) | |
| download | thehouseoficarus-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.js | 82 |
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 ? '▼' : '▶') + '</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'; +} |
