Refactor code structure for improved readability and maintainability

This commit is contained in:
Ross
2026-05-18 14:03:15 +01:00
parent 682bde82b9
commit e016175d2b
2 changed files with 302 additions and 177 deletions
+127 -2
View File
@@ -2,13 +2,15 @@
{% partialdef case-series %}
{% for series in ordered_series %}
<article class="series-block series-block-interactive"
<article class="series-block series-block-interactive series-block-draggable"
id="series-block-{{ series.pk }}"
data-series-id="{{series.pk}}"
data-series-label="Series {{ forloop.counter }}"
data-series-name="{{ series|escape }}"
data-image-ids='{{ series.get_image_url_array_and_count.0 }}'
data-series-detail-url="{% url 'atlas:series_detail' pk=series.pk %}"
data-series-popup-url="/atlas/series/{{series.pk}}">
data-series-popup-url="/atlas/series/{{series.pk}}"
draggable="true">
<input type="checkbox" class="hide" name="series-ids" value="{{series.pk}}">
<div class="series-card-shell">
<button type="button"
@@ -216,6 +218,29 @@
border-color: rgba(13, 110, 253, 0.3);
}
/* Draggable series block */
.series-block-draggable {
cursor: grab;
position: relative;
}
.series-block-draggable:active {
cursor: grabbing;
}
.series-block-draggable::after {
content: '\2807';
position: absolute;
top: 0.35rem;
right: 0.35rem;
font-size: 0.85rem;
color: rgba(120, 150, 200, 0.45);
pointer-events: none;
line-height: 1;
}
.series-block-draggable.dragging {
opacity: 0.55;
box-shadow: 0 4px 16px rgba(25, 118, 210, 0.35);
}
.series-block-actions {
display: flex;
flex-wrap: wrap;
@@ -1836,6 +1861,106 @@
});
})();
// ── Series-block drag-and-drop to viewer viewports ──────────────────
(function initSeriesBlockDrag() {
if (window.__seriesBlockDragBound) return;
window.__seriesBlockDragBound = true;
function getApiKey(viewerEl) {
if (!viewerEl) return null;
return viewerEl.id ? viewerEl.id.replace(/[^a-zA-Z0-9_]/g, '_') : null;
}
function getMainViewer() {
return document.getElementById('main_viewer') || document.querySelector('.dicom-viewer-root');
}
document.querySelectorAll('.series-block-draggable[data-image-ids]').forEach(function (block) {
block.addEventListener('dragstart', function (e) {
const rawIds = block.getAttribute('data-image-ids') || '[]';
const name = block.getAttribute('data-series-name') || block.getAttribute('data-series-label') || 'Series';
try {
// Validate it parses
JSON.parse(rawIds);
} catch (_) {
e.preventDefault();
return;
}
e.dataTransfer.setData('text/dv3d-imageids', rawIds);
e.dataTransfer.setData('text/dv3d-stack-name', name);
e.dataTransfer.effectAllowed = 'copy';
block.classList.add('dragging');
// Ensure viewer is open so viewports can receive the drop.
const viewerDetails = document.getElementById('dicom-viewer-details');
if (viewerDetails && !viewerDetails.hasAttribute('open')) {
viewerDetails.setAttribute('open', 'open');
viewerDetails.dispatchEvent(new Event('toggle', { bubbles: true }));
}
});
block.addEventListener('dragend', function () {
block.classList.remove('dragging');
});
});
// Update View / View-in-new-pane buttons to use the direct external
// stack API when image IDs are embedded on the series block, falling
// back to the legacy state-import path when they are not.
document.querySelectorAll('.series-open-primary-btn').forEach(function (btn) {
btn._origClick = btn.onclick;
btn.addEventListener('click', async function (e) {
e.stopImmediatePropagation();
const seriesId = btn.getAttribute('data-series-id');
const block = document.querySelector(`.series-block-draggable[data-series-id='${seriesId}']`);
const rawIds = block ? block.getAttribute('data-image-ids') : null;
if (!rawIds) return; // fall through to legacy handler
let imageIds;
try { imageIds = JSON.parse(rawIds); } catch (_) { return; }
if (!Array.isArray(imageIds) || imageIds.length === 0) return;
e.preventDefault();
const viewerEl = getMainViewer();
const apiKey = getApiKey(viewerEl);
const loadFn = apiKey ? window[`loadExternalStack_${apiKey}`] : null;
if (loadFn) {
const name = btn.getAttribute('data-series-name') || 'Series';
// Ensure viewer is visible first
const viewerDetails = document.getElementById('dicom-viewer-details');
if (viewerDetails && !viewerDetails.hasAttribute('open')) {
viewerDetails.setAttribute('open', 'open');
viewerDetails.dispatchEvent(new Event('toggle', { bubbles: true }));
}
try { await loadFn(imageIds, name); } catch (err) { console.error('loadExternalStack failed', err); }
}
}, true);
});
document.querySelectorAll('.series-open-new-viewport-btn').forEach(function (btn) {
btn.addEventListener('click', async function (e) {
e.stopImmediatePropagation();
const seriesId = btn.getAttribute('data-series-id');
const block = document.querySelector(`.series-block-draggable[data-series-id='${seriesId}']`);
const rawIds = block ? block.getAttribute('data-image-ids') : null;
if (!rawIds) return;
let imageIds;
try { imageIds = JSON.parse(rawIds); } catch (_) { return; }
if (!Array.isArray(imageIds) || imageIds.length === 0) return;
e.preventDefault();
const viewerEl = getMainViewer();
const apiKey = getApiKey(viewerEl);
const loadFn = apiKey ? window[`loadExternalStackNewViewport_${apiKey}`] : null;
if (loadFn) {
const name = btn.getAttribute('data-series-name') || 'Series';
const viewerDetails = document.getElementById('dicom-viewer-details');
if (viewerDetails && !viewerDetails.hasAttribute('open')) {
viewerDetails.setAttribute('open', 'open');
viewerDetails.dispatchEvent(new Event('toggle', { bubbles: true }));
}
try { await loadFn(imageIds, name); } catch (err) { console.error('loadExternalStackNewViewport failed', err); }
}
}, true);
});
})();
});
+175 -175
View File
File diff suppressed because one or more lines are too long