diff options
| author | historia <[not public]> | 2026-07-09 17:12:03 -0400 |
|---|---|---|
| committer | historia <[not public]> | 2026-07-09 17:12:03 -0400 |
| commit | b888414c63c0e5fae0d33b3c5faea8a604d195fa (patch) | |
| tree | 1c5a54101c774d6126c3d08fa5b6e9b44d0c3c04 /internal/admin/static/map.js | |
| parent | b8c90886ef1f3afb8d908aac89028bd177836cae (diff) | |
| download | thehouseoficarus-b888414c63c0e5fae0d33b3c5faea8a604d195fa.tar.gz | |
feat(admin): multi-select and delete rooms
Diffstat (limited to 'internal/admin/static/map.js')
| -rw-r--r-- | internal/admin/static/map.js | 87 |
1 files changed, 76 insertions, 11 deletions
diff --git a/internal/admin/static/map.js b/internal/admin/static/map.js index 1f67275..e053dda 100644 --- a/internal/admin/static/map.js +++ b/internal/admin/static/map.js @@ -42,7 +42,7 @@ function onMapMouseMove(e) { if (!dragging) return; var moved = Math.abs(e.clientX - startX) >= 4 || Math.abs(e.clientY - startY) >= 4; - if (delFromId && moved && !delDrag) { delDrag = true; } + if (delFromId && moved && !delDrag && multiSelectedIds.size === 0) { delDrag = true; } if (delDrag && moved) { delOneWay = e.ctrlKey; updateDelDragTarget(e); @@ -81,7 +81,11 @@ function onMapMouseUp(e) { var moved = Math.abs(e.clientX - startX) >= 4 || Math.abs(e.clientY - startY) >= 4; if (delFromId && !delDrag && !moved) { - showRoomContextMenu(startX, startY, delFromId); + if (multiSelectedIds.size > 0) { + showBulkDeleteContextMenu(startX, startY, delFromId); + } else { + showRoomContextMenu(startX, startY, delFromId); + } clearDelHighlight(); dragging = false; dragRoom = false; delFromId = null; delTargetId = null; delDrag = false; if (document.activeElement && document.activeElement.blur) document.activeElement.blur(); @@ -368,7 +372,7 @@ function renderMap(data) { var onUdBtn = e.target.classList.contains('ud-btn'); var onNavBtn = e.target.classList.contains('nav-btn'); - if (!e.shiftKey && multiSelectedIds.size > 0) { + if (e.button === 0 && !e.shiftKey && multiSelectedIds.size > 0) { clearMultiSelect(); } @@ -2263,6 +2267,58 @@ function showRoomContextMenu(x, y, id) { clampMenuToViewport(menu); } +function showBulkDeleteContextMenu(x, y, clickedId) { + var overlay = document.createElement('div'); + overlay.className = 'cm-overlay'; + var closeAll = function() { + if (overlay._subHideTimer) { clearTimeout(overlay._subHideTimer); overlay._subHideTimer = null; } + overlay.remove(); menu.remove(); + }; + overlay.onclick = closeAll; + overlay.addEventListener('contextmenu', function(e) { + e.preventDefault(); + e.stopPropagation(); + closeAll(); + }); + + var menu = document.createElement('div'); + menu.className = 'cm-menu'; + menu.style.left = x + 'px'; + menu.style.top = y + 'px'; + menu.oncontextmenu = function(e) { e.preventDefault(); }; + + var n = multiSelectedIds.size; + var delBtn = document.createElement('button'); + delBtn.textContent = 'Delete ' + n + ' room' + (n !== 1 ? 's' : ''); + delBtn.className = 'danger'; + delBtn.onclick = function() { + closeAll(); + deleteMultiRooms(); + }; + menu.appendChild(delBtn); + + document.body.appendChild(overlay); + document.body.appendChild(menu); + + clampMenuToViewport(menu); +} + +function deleteMultiRooms() { + var n = multiSelectedIds.size; + var cb = document.getElementById('confirmDelete'); + if (!cb || cb.checked) { + if (!confirm('Delete ' + n + ' room' + (n !== 1 ? 's' : '') + '?')) return; + } + var ids = Array.from(multiSelectedIds); + API.post('/api/rooms/bulk-delete', {ids: ids}).then(function(res) { + var msg = 'Deleted ' + n + ' room' + (n !== 1 ? 's' : ''); + notify(msg, 'success'); + updateUndoBar(); + clearMultiSelect(); + loadMap(); + }).catch(function(e) { notify('Delete failed: ' + extractError(e), 'error'); }); +} + // buildSubmenuItem constructs a parent menu row ("Insert Room"/"Remove Room") // that carries a nested submenu of the room's exits. The submenu is // revealed by both hover (Windows-style: mouseenter with a small dismiss delay @@ -2572,6 +2628,19 @@ function applyLinkHighlight(fromId, toId) { g.appendChild(line); } +function restoreRoomStroke(el, id) { + if (multiSelectedIds.has(id)) { + el.setAttribute('stroke', '#5cf'); + el.setAttribute('stroke-width', '3.5'); + } else if (selectedRoom === id) { + el.setAttribute('stroke', '#fff'); + el.setAttribute('stroke-width', '3'); + } else { + el.setAttribute('stroke', el.getAttribute('fill') || '#3a3a6a'); + el.setAttribute('stroke-width', '1.5'); + } +} + function clearLinkHighlight() { var line = document.getElementById('__hl_line'); if (line) line.remove(); @@ -2579,15 +2648,13 @@ function clearLinkHighlight() { if (linkFromId) { var el = document.querySelector('.rm[data-id="'+linkFromId+'"]'); if (el) { - el.setAttribute('stroke', selectedRoom === linkFromId ? '#fff' : (el.getAttribute('fill') || '#3a3a6a')); - el.setAttribute('stroke-width', selectedRoom === linkFromId ? '3' : '1.5'); + restoreRoomStroke(el, linkFromId); } } if (linkTargetId) { var el = document.querySelector('.rm[data-id="'+linkTargetId+'"]'); if (el) { - el.setAttribute('stroke', selectedRoom === linkTargetId ? '#fff' : (el.getAttribute('fill') || '#3a3a6a')); - el.setAttribute('stroke-width', selectedRoom === linkTargetId ? '3' : '1.5'); + restoreRoomStroke(el, linkTargetId); } } } @@ -2666,15 +2733,13 @@ function clearDelHighlight() { if (delFromId) { var el = document.querySelector('.rm[data-id="'+delFromId+'"]'); if (el) { - el.setAttribute('stroke', selectedRoom === delFromId ? '#fff' : (el.getAttribute('fill') || '#3a3a6a')); - el.setAttribute('stroke-width', selectedRoom === delFromId ? '3' : '1.5'); + restoreRoomStroke(el, delFromId); } } if (delTargetId) { var el = document.querySelector('.rm[data-id="'+delTargetId+'"]'); if (el) { - el.setAttribute('stroke', selectedRoom === delTargetId ? '#fff' : (el.getAttribute('fill') || '#3a3a6a')); - el.setAttribute('stroke-width', selectedRoom === delTargetId ? '3' : '1.5'); + restoreRoomStroke(el, delTargetId); } } } |
