refactor(paths): move Add Path form into its own modal

The path creation form is now a separate modal (z-index 1070) that
opens above Contact Info with its own backdrop, making the UI layers
clearly distinguishable. Map picker modal bumped to z-index 1080 so
it stacks correctly above both Contact Info and Add Path modals.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
MarekWo
2026-03-23 08:48:38 +01:00
parent 0bca19e936
commit 796fb917e4
2 changed files with 82 additions and 70 deletions

View File

@@ -1807,34 +1807,36 @@ async function movePathItem(pubkey, paths, currentIndex, direction) {
*/
function setupPathFormHandlers(pubkey) {
const addBtn = document.getElementById('dmAddPathBtn');
const form = document.getElementById('dmAddPathForm');
const cancelBtn = document.getElementById('dmCancelPathBtn');
const saveBtn = document.getElementById('dmSavePathBtn');
const pickBtn = document.getElementById('dmPickRepeaterBtn');
const picker = document.getElementById('dmRepeaterPicker');
const resetFloodBtn = document.getElementById('dmResetFloodBtn');
const addPathModalEl = document.getElementById('addPathModal');
if (!addBtn || !form) return;
if (!addBtn || !addPathModalEl) return;
// Remove old listeners by cloning
const addPathModal = new bootstrap.Modal(addPathModalEl);
// "+" button opens the Add Path modal
const newAddBtn = addBtn.cloneNode(true);
addBtn.parentNode.replaceChild(newAddBtn, addBtn);
newAddBtn.addEventListener('click', () => {
form.style.display = '';
newAddBtn.style.display = 'none';
document.getElementById('dmPathHexInput').value = '';
document.getElementById('dmPathLabelInput').value = '';
document.getElementById('dmPathUniquenessWarning').style.display = 'none';
if (picker) picker.style.display = 'none';
addPathModal.show();
});
const newCancelBtn = cancelBtn.cloneNode(true);
cancelBtn.parentNode.replaceChild(newCancelBtn, cancelBtn);
newCancelBtn.addEventListener('click', () => {
form.style.display = 'none';
newAddBtn.style.display = '';
// Raise backdrop when Add Path modal opens (above Contact Info)
addPathModalEl.addEventListener('shown.bs.modal', () => {
const backdrops = document.querySelectorAll('.modal-backdrop');
if (backdrops.length > 1) {
backdrops[backdrops.length - 1].style.zIndex = '1060';
}
});
// Save button
const newSaveBtn = saveBtn.cloneNode(true);
saveBtn.parentNode.replaceChild(newSaveBtn, saveBtn);
newSaveBtn.addEventListener('click', async () => {
@@ -1855,8 +1857,7 @@ function setupPathFormHandlers(pubkey) {
});
const data = await response.json();
if (data.success) {
form.style.display = 'none';
newAddBtn.style.display = '';
addPathModal.hide();
await renderPathList(pubkey);
showNotification('Path added', 'info');
} else {
@@ -2069,10 +2070,10 @@ function openRepeaterMapPicker() {
const modal = new bootstrap.Modal(modalEl);
const onShown = async function () {
// Raise backdrop z-index so it covers the Contact Info modal behind
// Raise backdrop z-index so it covers modals behind (Contact Info + Add Path)
const backdrops = document.querySelectorAll('.modal-backdrop');
if (backdrops.length > 1) {
backdrops[backdrops.length - 1].style.zIndex = '1060';
if (backdrops.length > 0) {
backdrops[backdrops.length - 1].style.zIndex = '1075';
}
// Init map once

View File

@@ -327,59 +327,6 @@
</button>
</div>
<div id="dmPathList"></div>
<!-- Add Path form (hidden by default) -->
<div id="dmAddPathForm" style="display: none;" class="border rounded p-2 mb-2">
<div class="mb-2">
<label class="form-label small mb-1">Hash Size</label>
<div class="btn-group btn-group-sm w-100" role="group">
<input type="radio" class="btn-check" name="pathHashSize" id="pathHash1" value="1" checked>
<label class="btn btn-outline-secondary" for="pathHash1">1B (max 64)</label>
<input type="radio" class="btn-check" name="pathHashSize" id="pathHash2" value="2">
<label class="btn btn-outline-secondary" for="pathHash2">2B (max 32)</label>
<input type="radio" class="btn-check" name="pathHashSize" id="pathHash3" value="3">
<label class="btn btn-outline-secondary" for="pathHash3">3B (max 21)</label>
</div>
</div>
<div class="mb-2">
<label class="form-label small mb-1">Path (hex)</label>
<div class="input-group input-group-sm">
<input type="text" class="form-control font-monospace" id="dmPathHexInput"
placeholder="e.g. 5e,e7 or 5e34,e761" autocomplete="off">
<button type="button" class="btn btn-outline-secondary" id="dmPickRepeaterBtn"
title="Pick repeater from list">
<i class="bi bi-plus-circle"></i>
</button>
<button type="button" class="btn btn-outline-secondary" id="dmPickRepeaterMapBtn"
title="Pick repeater from map">
<i class="bi bi-geo-alt"></i>
</button>
</div>
<div id="dmPathUniquenessWarning" class="path-uniqueness-warning mt-1" style="display: none;"></div>
</div>
<!-- Repeater picker (hidden by default) -->
<div id="dmRepeaterPicker" style="display: none;" class="border rounded mb-2">
<div class="d-flex border-bottom">
<div class="btn-group btn-group-sm flex-shrink-0" role="group">
<input type="radio" class="btn-check" name="repeaterSearchMode" id="rptSearchName" value="name" checked>
<label class="btn btn-outline-secondary border-0 rounded-0" for="rptSearchName">Name</label>
<input type="radio" class="btn-check" name="repeaterSearchMode" id="rptSearchId" value="id">
<label class="btn btn-outline-secondary border-0 rounded-0" for="rptSearchId">ID</label>
</div>
<input type="text" class="form-control form-control-sm border-0"
id="dmRepeaterSearch" placeholder="Search by name..." autocomplete="off">
</div>
<div id="dmRepeaterList" style="max-height: 180px; overflow-y: auto;"></div>
</div>
<div class="mb-2">
<label class="form-label small mb-1">Label (optional)</label>
<input type="text" class="form-control form-control-sm" id="dmPathLabelInput"
placeholder="e.g. via Mountain RPT" maxlength="50">
</div>
<div class="d-flex justify-content-end gap-2">
<button type="button" class="btn btn-sm btn-outline-secondary" id="dmCancelPathBtn">Cancel</button>
<button type="button" class="btn btn-sm btn-primary" id="dmSavePathBtn">Add Path</button>
</div>
</div>
<!-- Reset to FLOOD button -->
<div class="text-end mt-1">
<button type="button" class="btn btn-outline-danger btn-sm" id="dmResetFloodBtn"
@@ -408,7 +355,7 @@
</div>
<!-- Repeater Map Picker Modal -->
<div class="modal fade" id="repeaterMapModal" tabindex="-1" style="z-index: 1070;">
<div class="modal fade" id="repeaterMapModal" tabindex="-1" style="z-index: 1080;">
<div class="modal-dialog modal-lg modal-dialog-centered">
<div class="modal-content">
<div class="modal-header py-2">
@@ -434,6 +381,70 @@
</div>
</div>
<!-- Add Path Modal -->
<div class="modal fade" id="addPathModal" tabindex="-1" style="z-index: 1070;">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header py-2">
<h6 class="modal-title"><i class="bi bi-signpost-split"></i> Add Path</h6>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body" id="dmAddPathForm">
<div class="mb-2">
<label class="form-label small mb-1">Hash Size</label>
<div class="btn-group btn-group-sm w-100" role="group">
<input type="radio" class="btn-check" name="pathHashSize" id="pathHash1" value="1" checked>
<label class="btn btn-outline-secondary" for="pathHash1">1B (max 64)</label>
<input type="radio" class="btn-check" name="pathHashSize" id="pathHash2" value="2">
<label class="btn btn-outline-secondary" for="pathHash2">2B (max 32)</label>
<input type="radio" class="btn-check" name="pathHashSize" id="pathHash3" value="3">
<label class="btn btn-outline-secondary" for="pathHash3">3B (max 21)</label>
</div>
</div>
<div class="mb-2">
<label class="form-label small mb-1">Path (hex)</label>
<div class="input-group input-group-sm">
<input type="text" class="form-control font-monospace" id="dmPathHexInput"
placeholder="e.g. 5e,e7 or 5e34,e761" autocomplete="off">
<button type="button" class="btn btn-outline-secondary" id="dmPickRepeaterBtn"
title="Pick repeater from list">
<i class="bi bi-plus-circle"></i>
</button>
<button type="button" class="btn btn-outline-secondary" id="dmPickRepeaterMapBtn"
title="Pick repeater from map">
<i class="bi bi-geo-alt"></i>
</button>
</div>
<div id="dmPathUniquenessWarning" class="path-uniqueness-warning mt-1" style="display: none;"></div>
</div>
<!-- Repeater picker (hidden by default) -->
<div id="dmRepeaterPicker" style="display: none;" class="border rounded mb-2">
<div class="d-flex border-bottom">
<div class="btn-group btn-group-sm flex-shrink-0" role="group">
<input type="radio" class="btn-check" name="repeaterSearchMode" id="rptSearchName" value="name" checked>
<label class="btn btn-outline-secondary border-0 rounded-0" for="rptSearchName">Name</label>
<input type="radio" class="btn-check" name="repeaterSearchMode" id="rptSearchId" value="id">
<label class="btn btn-outline-secondary border-0 rounded-0" for="rptSearchId">ID</label>
</div>
<input type="text" class="form-control form-control-sm border-0"
id="dmRepeaterSearch" placeholder="Search by name..." autocomplete="off">
</div>
<div id="dmRepeaterList" style="max-height: 180px; overflow-y: auto;"></div>
</div>
<div class="mb-2">
<label class="form-label small mb-1">Label (optional)</label>
<input type="text" class="form-control form-control-sm" id="dmPathLabelInput"
placeholder="e.g. via Mountain RPT" maxlength="50">
</div>
</div>
<div class="modal-footer py-2">
<button type="button" class="btn btn-sm btn-outline-secondary" id="dmCancelPathBtn" data-bs-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-sm btn-primary" id="dmSavePathBtn">Add Path</button>
</div>
</div>
</div>
</div>
<!-- Toast container for notifications -->
<div class="toast-container position-fixed top-0 start-0 p-3">
<div id="notificationToast" class="toast" role="alert">