Refactor code structure for improved readability and maintainability
This commit is contained in:
@@ -47,19 +47,19 @@
|
|||||||
</button>
|
</button>
|
||||||
<button type="button"
|
<button type="button"
|
||||||
class="btn btn-outline-info btn-sm series-open-primary-btn"
|
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-id="{{series.pk}}"
|
||||||
data-series-name="{{ series|escape }}">
|
data-series-name="{{ series|escape }}">
|
||||||
<i class="bi bi-eye"></i> Open
|
<i class="bi bi-eye"></i> View
|
||||||
</button>
|
</button>
|
||||||
<button type="button"
|
<button type="button"
|
||||||
class="btn btn-outline-info btn-sm series-open-new-viewport-btn d-none"
|
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-id="{{series.pk}}"
|
||||||
data-series-name="{{ series|escape }}"
|
data-series-name="{{ series|escape }}"
|
||||||
tabindex="-1"
|
tabindex="-1"
|
||||||
aria-hidden="true">
|
aria-hidden="true">
|
||||||
<i class="bi bi-layout-split"></i> Open + viewport
|
<i class="bi bi-layout-split"></i> View in new pane
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -505,20 +505,43 @@
|
|||||||
<details class="series-panel" id="case-series-panel" open>
|
<details class="series-panel" id="case-series-panel" open>
|
||||||
<summary class="series-panel-summary">
|
<summary class="series-panel-summary">
|
||||||
<span class="series-panel-title">Series</span>
|
<span class="series-panel-title">Series</span>
|
||||||
<span class="series-panel-badge">{{ case.get_ordered_series|length }} total</span>
|
<span class="series-panel-summary-actions">
|
||||||
</summary>
|
<span class="series-panel-badge">{{ case.get_ordered_series|length }} total</span>
|
||||||
<div class="series-panel-body">
|
|
||||||
<div class="series-toolbar">
|
|
||||||
<button type="button"
|
<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 %}'">
|
onclick="window.location.href='{% url 'atlas:user_uploads_case' case_id=case.pk %}'">
|
||||||
Import new uploads
|
Import new uploads
|
||||||
</button>
|
</button>
|
||||||
<details class="series-actions" id="series-actions">
|
<button type="button"
|
||||||
<summary class="series-actions-summary">
|
class="btn btn-outline-secondary btn-sm series-summary-btn"
|
||||||
<span class="btn btn-outline-secondary btn-sm series-toolbar-btn">Manage series</span>
|
id="series-manage-toggle-btn"
|
||||||
</summary>
|
aria-controls="series-actions-panel"
|
||||||
<div class="series-actions-body">
|
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>
|
<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">
|
<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>
|
<div class="text-muted small mt-2">Case move options are unavailable in this view.</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</details>
|
</details>
|
||||||
</div>
|
</div>
|
||||||
<div class="alert alert-info mt-2" id="series-action-alert" style="display:none;">
|
<div class="alert alert-info mt-2" id="series-action-alert" style="display:none;">
|
||||||
<span id="series-action-results" ></span>
|
<span id="series-action-results" ></span>
|
||||||
</div>
|
</div>
|
||||||
</details>
|
|
||||||
</div>
|
|
||||||
<form>
|
|
||||||
<div class="series-card-grid" id="case-series-row">
|
|
||||||
{% partial case-series %}
|
|
||||||
</div>
|
</div>
|
||||||
</form>
|
|
||||||
</div>
|
</div>
|
||||||
</details>
|
</div>
|
||||||
|
|
||||||
<div class="container">
|
<div class="container">
|
||||||
|
|
||||||
@@ -1028,10 +1045,10 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="modal-body d-flex flex-column gap-2">
|
<div class="modal-body d-flex flex-column gap-2">
|
||||||
<button type="button" class="btn btn-outline-info" id="series-modal-open-primary-btn">
|
<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>
|
||||||
<button type="button" class="btn btn-outline-info" id="series-modal-open-new-viewport-btn">
|
<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>
|
</button>
|
||||||
<a href="#" class="btn btn-outline-secondary" id="series-modal-page-link">
|
<a href="#" class="btn btn-outline-secondary" id="series-modal-page-link">
|
||||||
<i class="bi bi-box-arrow-up-right"></i> Open series page
|
<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 seriesCheckboxes = document.querySelectorAll("input[type='checkbox'][name='series-ids']");
|
||||||
const selectAllBtn = document.getElementById("series-select-all-btn");
|
const selectAllBtn = document.getElementById("series-select-all-btn");
|
||||||
const deselectAllBtn = document.getElementById("series-deselect-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 seriesActionsModalEl = document.getElementById("seriesActionsModal");
|
||||||
const seriesActionsModal = seriesActionsModalEl ? new bootstrap.Modal(seriesActionsModalEl) : null;
|
const seriesActionsModal = seriesActionsModalEl ? new bootstrap.Modal(seriesActionsModalEl) : null;
|
||||||
const seriesModalSubheading = document.getElementById("seriesActionsModalSubheading");
|
const seriesModalSubheading = document.getElementById("seriesActionsModalSubheading");
|
||||||
@@ -1181,8 +1201,26 @@
|
|||||||
const seriesModalSelectBtn = document.getElementById("series-modal-select-btn");
|
const seriesModalSelectBtn = document.getElementById("series-modal-select-btn");
|
||||||
let activeSeriesCard = null;
|
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() {
|
function toggleCheckboxes() {
|
||||||
const isOpen = detailsElement && detailsElement.hasAttribute("open");
|
const isOpen = isSeriesActionsPanelOpen();
|
||||||
buttons.forEach((button) => {
|
buttons.forEach((button) => {
|
||||||
button.style.display = isOpen ? "inline-block" : "none";
|
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) {
|
function toggleSeriesSelection(card) {
|
||||||
if (!card) {
|
if (!card) {
|
||||||
return;
|
return;
|
||||||
@@ -1379,11 +1437,6 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Listen for toggle events on the <details> element
|
|
||||||
if (detailsElement) {
|
|
||||||
detailsElement.addEventListener("toggle", toggleCheckboxes);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Display Set modal handler
|
// Display Set modal handler
|
||||||
const dsModal = new bootstrap.Modal(document.getElementById("displaysetModal"));
|
const dsModal = new bootstrap.Modal(document.getElementById("displaysetModal"));
|
||||||
const dsModalBody = document.getElementById("displaysetModalBody");
|
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: Math.max(1, Number(state.grid.rows)), cols: Math.max(1, Number(state.grid.cols)) }
|
||||||
: { rows: 1, cols: 1 };
|
: { rows: 1, cols: 1 };
|
||||||
|
|
||||||
if ((grid.rows * grid.cols) < 2) {
|
let numActive = Math.max(1, grid.rows * grid.cols);
|
||||||
grid.cols = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
const numActive = Math.max(2, grid.rows * grid.cols);
|
|
||||||
const stackIdx = Array.isArray(state.stackIdx) ? [...state.stackIdx] : [];
|
const stackIdx = Array.isArray(state.stackIdx) ? [...state.stackIdx] : [];
|
||||||
const modes = Array.isArray(state.modes) ? [...state.modes] : [];
|
const modes = Array.isArray(state.modes) ? [...state.modes] : [];
|
||||||
while (stackIdx.length < numActive) {
|
while (stackIdx.length < numActive) {
|
||||||
@@ -1722,9 +1771,35 @@
|
|||||||
while (modes.length < numActive) {
|
while (modes.length < numActive) {
|
||||||
modes.push('stack');
|
modes.push('stack');
|
||||||
}
|
}
|
||||||
stackIdx[1] = targetIdx;
|
|
||||||
modes[0] = 'stack';
|
const viewportStacks = Array.from({ length: numActive }, (_, viewportIndex) => {
|
||||||
modes[1] = 'stack';
|
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 = {
|
const nextState = {
|
||||||
...state,
|
...state,
|
||||||
@@ -1804,6 +1879,13 @@
|
|||||||
font-size: 0.95rem;
|
font-size: 0.95rem;
|
||||||
font-weight: 700;
|
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 {
|
.series-panel-badge {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -1822,13 +1904,8 @@
|
|||||||
gap: 0.75rem;
|
gap: 0.75rem;
|
||||||
padding: 0.5rem 0;
|
padding: 0.5rem 0;
|
||||||
}
|
}
|
||||||
.series-toolbar {
|
.series-summary-btn {
|
||||||
display: flex;
|
white-space: nowrap;
|
||||||
flex-wrap: wrap;
|
|
||||||
gap: .75rem;
|
|
||||||
}
|
|
||||||
.series-toolbar-btn {
|
|
||||||
min-width: 160px;
|
|
||||||
}
|
}
|
||||||
.series-card-grid {
|
.series-card-grid {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -1960,24 +2037,49 @@
|
|||||||
border-color: rgba(13,110,253,0.5);
|
border-color: rgba(13,110,253,0.5);
|
||||||
background: rgba(13,110,253,0.08);
|
background: rgba(13,110,253,0.08);
|
||||||
}
|
}
|
||||||
.series-actions[open] {
|
.series-actions-panel {
|
||||||
border: 2px solid #0d6efd;
|
position: fixed;
|
||||||
border-radius: .85rem;
|
left: 0;
|
||||||
padding: .85rem;
|
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 {
|
.series-actions {
|
||||||
flex: 1 1 auto;
|
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: .5rem;
|
gap: .5rem;
|
||||||
border: 1px solid rgba(0,0,0,0.06);
|
border: 1px solid rgba(255,255,255,0.08);
|
||||||
border-radius: .85rem;
|
border-radius: .85rem;
|
||||||
padding: .5rem;
|
padding: .85rem;
|
||||||
background: rgba(255,255,255,0.02);
|
background: rgba(255,255,255,0.02);
|
||||||
}
|
}
|
||||||
.series-actions-summary { list-style: none; }
|
|
||||||
.series-actions-summary::-webkit-details-marker { display:none; }
|
|
||||||
.series-actions-body {
|
.series-actions-body {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@@ -2029,11 +2131,12 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
@media (max-width: 991.98px) {
|
@media (max-width: 991.98px) {
|
||||||
.series-toolbar {
|
.series-panel-summary {
|
||||||
flex-direction: column;
|
align-items: flex-start;
|
||||||
}
|
}
|
||||||
.series-toolbar-btn {
|
.series-panel-summary-actions {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
justify-content: flex-start;
|
||||||
}
|
}
|
||||||
.case-detail-section .card-header {
|
.case-detail-section .card-header {
|
||||||
padding: .65rem .85rem;
|
padding: .65rem .85rem;
|
||||||
@@ -2041,6 +2144,11 @@
|
|||||||
.case-detail-section .card-body {
|
.case-detail-section .card-body {
|
||||||
padding: .75rem .85rem;
|
padding: .75rem .85rem;
|
||||||
}
|
}
|
||||||
|
.series-actions-panel {
|
||||||
|
padding-left: .5rem;
|
||||||
|
padding-right: .5rem;
|
||||||
|
padding-bottom: .5rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@media (max-width: 575.98px) {
|
@media (max-width: 575.98px) {
|
||||||
.series-block {
|
.series-block {
|
||||||
@@ -2051,6 +2159,12 @@
|
|||||||
width: 56px;
|
width: 56px;
|
||||||
height: 56px;
|
height: 56px;
|
||||||
}
|
}
|
||||||
|
.series-summary-btn {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.series-actions-panel-inner {
|
||||||
|
padding: .75rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@media (prefers-color-scheme: dark) {
|
@media (prefers-color-scheme: dark) {
|
||||||
.series-actions { background: rgba(255,255,255,0.02); border-color: rgba(255,255,255,0.04); }
|
.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