Refactor code structure for improved readability and maintainability
This commit is contained in:
@@ -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
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user