Refactor code structure for improved readability and maintainability

This commit is contained in:
Ross
2026-05-18 11:55:40 +01:00
parent b09fbee2c9
commit f3af6d47ee
2 changed files with 327 additions and 213 deletions
+171 -57
View File
@@ -47,19 +47,19 @@
</button>
<button type="button"
class="btn btn-outline-info btn-sm series-open-primary-btn"
title="Open series in primary viewport"
title="View series in viewer"
data-series-id="{{series.pk}}"
data-series-name="{{ series|escape }}">
<i class="bi bi-eye"></i> Open
<i class="bi bi-eye"></i> View
</button>
<button type="button"
class="btn btn-outline-info btn-sm series-open-new-viewport-btn d-none"
title="Add a viewport and open series"
title="View series in a new pane"
data-series-id="{{series.pk}}"
data-series-name="{{ series|escape }}"
tabindex="-1"
aria-hidden="true">
<i class="bi bi-layout-split"></i> Open + viewport
<i class="bi bi-layout-split"></i> View in new pane
</button>
</div>
</div>
@@ -505,20 +505,43 @@
<details class="series-panel" id="case-series-panel" open>
<summary class="series-panel-summary">
<span class="series-panel-title">Series</span>
<span class="series-panel-badge">{{ case.get_ordered_series|length }} total</span>
</summary>
<div class="series-panel-body">
<div class="series-toolbar">
<span class="series-panel-summary-actions">
<span class="series-panel-badge">{{ case.get_ordered_series|length }} total</span>
<button type="button"
class="btn btn-outline-primary btn-sm series-toolbar-btn"
class="btn btn-outline-primary btn-sm series-summary-btn"
id="series-import-uploads-btn"
onclick="window.location.href='{% url 'atlas:user_uploads_case' case_id=case.pk %}'">
Import new uploads
</button>
<details class="series-actions" id="series-actions">
<summary class="series-actions-summary">
<span class="btn btn-outline-secondary btn-sm series-toolbar-btn">Manage series</span>
</summary>
<div class="series-actions-body">
<button type="button"
class="btn btn-outline-secondary btn-sm series-summary-btn"
id="series-manage-toggle-btn"
aria-controls="series-actions-panel"
aria-expanded="false">
Manage series
</button>
</span>
</summary>
<div class="series-panel-body">
<form>
<div class="series-card-grid" id="case-series-row">
{% partial case-series %}
</div>
</form>
</div>
</details>
<div class="series-actions-panel" id="series-actions-panel" aria-hidden="true">
<div class="series-actions-panel-inner">
<div class="series-actions-panel-header">
<div>
<h5 class="mb-1">Manage series</h5>
<div class="small text-muted">Selection tools and bulk actions for this case.</div>
</div>
<button type="button" class="btn-close btn-close-white" id="series-manage-close-btn" aria-label="Close"></button>
</div>
<div class="series-actions" id="series-actions">
<div class="series-actions-body">
<a class="btn btn-sm btn-outline-secondary text-start" href="{% url 'atlas:series_id_create' pk=case.pk %}">Create and add new series</a>
<div class="series-selection-toolbar d-flex flex-wrap gap-2">
@@ -663,19 +686,13 @@
<div class="text-muted small mt-2">Case move options are unavailable in this view.</div>
{% endif %}
</details>
</div>
</div>
<div class="alert alert-info mt-2" id="series-action-alert" style="display:none;">
<span id="series-action-results" ></span>
</div>
</details>
</div>
<form>
<div class="series-card-grid" id="case-series-row">
{% partial case-series %}
</div>
</form>
</div>
</details>
</div>
<div class="container">
@@ -1028,10 +1045,10 @@
</div>
<div class="modal-body d-flex flex-column gap-2">
<button type="button" class="btn btn-outline-info" id="series-modal-open-primary-btn">
<i class="bi bi-eye"></i> Open in viewer
<i class="bi bi-eye"></i> View in viewer
</button>
<button type="button" class="btn btn-outline-info" id="series-modal-open-new-viewport-btn">
<i class="bi bi-layout-split"></i> Open with new viewport
<i class="bi bi-layout-split"></i> View in new pane
</button>
<a href="#" class="btn btn-outline-secondary" id="series-modal-page-link">
<i class="bi bi-box-arrow-up-right"></i> Open series page
@@ -1171,6 +1188,9 @@
const seriesCheckboxes = document.querySelectorAll("input[type='checkbox'][name='series-ids']");
const selectAllBtn = document.getElementById("series-select-all-btn");
const deselectAllBtn = document.getElementById("series-deselect-all-btn");
const seriesManageToggleBtn = document.getElementById("series-manage-toggle-btn");
const seriesManageCloseBtn = document.getElementById("series-manage-close-btn");
const seriesActionsPanel = document.getElementById("series-actions-panel");
const seriesActionsModalEl = document.getElementById("seriesActionsModal");
const seriesActionsModal = seriesActionsModalEl ? new bootstrap.Modal(seriesActionsModalEl) : null;
const seriesModalSubheading = document.getElementById("seriesActionsModalSubheading");
@@ -1181,8 +1201,26 @@
const seriesModalSelectBtn = document.getElementById("series-modal-select-btn");
let activeSeriesCard = null;
function isSeriesActionsPanelOpen() {
return !!(seriesActionsPanel && seriesActionsPanel.classList.contains("is-open"));
}
function setSeriesActionsPanelOpen(isOpen) {
if (!seriesActionsPanel) {
return;
}
seriesActionsPanel.classList.toggle("is-open", isOpen);
seriesActionsPanel.setAttribute("aria-hidden", isOpen ? "false" : "true");
if (seriesManageToggleBtn) {
seriesManageToggleBtn.setAttribute("aria-expanded", isOpen ? "true" : "false");
seriesManageToggleBtn.classList.toggle("btn-secondary", isOpen);
seriesManageToggleBtn.classList.toggle("btn-outline-secondary", !isOpen);
}
document.body.classList.toggle("series-actions-panel-open", isOpen);
}
function toggleCheckboxes() {
const isOpen = detailsElement && detailsElement.hasAttribute("open");
const isOpen = isSeriesActionsPanelOpen();
buttons.forEach((button) => {
button.style.display = isOpen ? "inline-block" : "none";
});
@@ -1191,6 +1229,26 @@
}
}
if (seriesManageToggleBtn) {
seriesManageToggleBtn.addEventListener("click", function(evt) {
evt.preventDefault();
evt.stopPropagation();
setSeriesActionsPanelOpen(!isSeriesActionsPanelOpen());
toggleCheckboxes();
});
}
if (seriesManageCloseBtn) {
seriesManageCloseBtn.addEventListener("click", function() {
setSeriesActionsPanelOpen(false);
toggleCheckboxes();
});
}
document.getElementById("series-import-uploads-btn")?.addEventListener("click", function(evt) {
evt.stopPropagation();
});
function toggleSeriesSelection(card) {
if (!card) {
return;
@@ -1379,11 +1437,6 @@
});
}
// Listen for toggle events on the <details> element
if (detailsElement) {
detailsElement.addEventListener("toggle", toggleCheckboxes);
}
// Display Set modal handler
const dsModal = new bootstrap.Modal(document.getElementById("displaysetModal"));
const dsModalBody = document.getElementById("displaysetModalBody");
@@ -1709,11 +1762,7 @@
? { rows: Math.max(1, Number(state.grid.rows)), cols: Math.max(1, Number(state.grid.cols)) }
: { rows: 1, cols: 1 };
if ((grid.rows * grid.cols) < 2) {
grid.cols = 2;
}
const numActive = Math.max(2, grid.rows * grid.cols);
let numActive = Math.max(1, grid.rows * grid.cols);
const stackIdx = Array.isArray(state.stackIdx) ? [...state.stackIdx] : [];
const modes = Array.isArray(state.modes) ? [...state.modes] : [];
while (stackIdx.length < numActive) {
@@ -1722,9 +1771,35 @@
while (modes.length < numActive) {
modes.push('stack');
}
stackIdx[1] = targetIdx;
modes[0] = 'stack';
modes[1] = 'stack';
const viewportStacks = Array.from({ length: numActive }, (_, viewportIndex) => {
const referencedStack = currentStacks[stackIdx[viewportIndex]];
return Array.isArray(referencedStack?.i) ? referencedStack.i : [];
});
let targetViewportIndex = viewportStacks.findIndex((imageIds) => !Array.isArray(imageIds) || imageIds.length === 0);
if (targetViewportIndex < 0) {
targetViewportIndex = viewportStacks.findIndex((imageIds) => {
const norm = toNorm(imageIds);
return norm.length === targetNorm.length && norm.every((id, index) => id === targetNorm[index]);
});
}
if (targetViewportIndex < 0) {
grid.cols += 1;
numActive = grid.rows * grid.cols;
while (stackIdx.length < numActive) {
stackIdx.push(0);
}
while (modes.length < numActive) {
modes.push('stack');
}
targetViewportIndex = numActive - 1;
}
stackIdx[targetViewportIndex] = targetIdx;
modes[targetViewportIndex] = 'stack';
const nextState = {
...state,
@@ -1804,6 +1879,13 @@
font-size: 0.95rem;
font-weight: 700;
}
.series-panel-summary-actions {
display: inline-flex;
align-items: center;
gap: .5rem;
flex-wrap: wrap;
justify-content: flex-end;
}
.series-panel-badge {
display: inline-flex;
align-items: center;
@@ -1822,13 +1904,8 @@
gap: 0.75rem;
padding: 0.5rem 0;
}
.series-toolbar {
display: flex;
flex-wrap: wrap;
gap: .75rem;
}
.series-toolbar-btn {
min-width: 160px;
.series-summary-btn {
white-space: nowrap;
}
.series-card-grid {
display: flex;
@@ -1960,24 +2037,49 @@
border-color: rgba(13,110,253,0.5);
background: rgba(13,110,253,0.08);
}
.series-actions[open] {
border: 2px solid #0d6efd;
border-radius: .85rem;
padding: .85rem;
.series-actions-panel {
position: fixed;
left: 0;
right: 0;
bottom: 0;
z-index: 1050;
pointer-events: none;
padding: 0 1rem 1rem;
transform: translateY(calc(100% + 1rem));
transition: transform 0.22s ease;
}
.series-actions-panel.is-open {
transform: translateY(0);
pointer-events: auto;
}
.series-actions-panel-inner {
max-width: 1280px;
margin: 0 auto;
border: 1px solid rgba(13,110,253,0.22);
border-radius: 1rem 1rem 0 0;
background: rgba(20, 25, 36, 0.98);
box-shadow: 0 -12px 32px rgba(0,0,0,0.28);
padding: 1rem;
max-height: min(62vh, 640px);
overflow: auto;
}
.series-actions-panel-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
gap: 1rem;
margin-bottom: .75rem;
}
.series-actions {
flex: 1 1 auto;
min-width: 0;
display: flex;
flex-direction: column;
gap: .5rem;
border: 1px solid rgba(0,0,0,0.06);
border: 1px solid rgba(255,255,255,0.08);
border-radius: .85rem;
padding: .5rem;
padding: .85rem;
background: rgba(255,255,255,0.02);
}
.series-actions-summary { list-style: none; }
.series-actions-summary::-webkit-details-marker { display:none; }
.series-actions-body {
display: flex;
flex-direction: column;
@@ -2029,11 +2131,12 @@
width: 100%;
}
@media (max-width: 991.98px) {
.series-toolbar {
flex-direction: column;
.series-panel-summary {
align-items: flex-start;
}
.series-toolbar-btn {
.series-panel-summary-actions {
width: 100%;
justify-content: flex-start;
}
.case-detail-section .card-header {
padding: .65rem .85rem;
@@ -2041,6 +2144,11 @@
.case-detail-section .card-body {
padding: .75rem .85rem;
}
.series-actions-panel {
padding-left: .5rem;
padding-right: .5rem;
padding-bottom: .5rem;
}
}
@media (max-width: 575.98px) {
.series-block {
@@ -2051,6 +2159,12 @@
width: 56px;
height: 56px;
}
.series-summary-btn {
width: 100%;
}
.series-actions-panel-inner {
padding: .75rem;
}
}
@media (prefers-color-scheme: dark) {
.series-actions { background: rgba(255,255,255,0.02); border-color: rgba(255,255,255,0.04); }
+156 -156
View File
File diff suppressed because one or more lines are too long