a lot of fixes
This commit is contained in:
+131
-120
@@ -398,42 +398,42 @@ function App({ container_id, imageStacks, autoCacheStack, ...props }: AppProps)
|
|||||||
const [metaModalOpen, setMetaModalOpen] = useState(false);
|
const [metaModalOpen, setMetaModalOpen] = useState(false);
|
||||||
const [metaModalContent, setMetaModalContent] = useState<any>(null);
|
const [metaModalContent, setMetaModalContent] = useState<any>(null);
|
||||||
|
|
||||||
const handleSelectAnnotation = (viewportIdx: number, uid: string | null) => {
|
const handleSelectAnnotation = (viewportIdx: number, uid: string | null) => {
|
||||||
// Use the official selection API for Cornerstone3D Tools
|
// Use the official selection API for Cornerstone3D Tools
|
||||||
if (cornerstoneTools?.annotation?.selection?.setAnnotationSelected) {
|
if (cornerstoneTools?.annotation?.selection?.setAnnotationSelected) {
|
||||||
cornerstoneTools.annotation.selection.setAnnotationSelected(uid);
|
cornerstoneTools.annotation.selection.setAnnotationSelected(uid);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Optionally, update your own UI state
|
// Optionally, update your own UI state
|
||||||
setSelectedAnnUIDs(prev => {
|
setSelectedAnnUIDs(prev => {
|
||||||
const next = [...prev];
|
const next = [...prev];
|
||||||
next[viewportIdx] = uid;
|
next[viewportIdx] = uid;
|
||||||
return next;
|
return next;
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleDeleteAnnotation = (viewportIdx: number, uid: string, currentAnnotations: any[]) => {
|
|
||||||
const allAnnotations = cornerstoneTools.annotation.state.getAllAnnotations() || [];
|
|
||||||
allAnnotations.forEach(ann => {
|
|
||||||
if (ann.annotationUID === uid) ann.isSelected = false;
|
|
||||||
});
|
|
||||||
cornerstoneTools.annotation.state.removeAnnotation(uid);
|
|
||||||
renderingEngineRef.current.render();
|
|
||||||
//}
|
|
||||||
// Auto-select another annotation if available
|
|
||||||
const remaining = currentAnnotations.filter(ann => ann.annotationUID !== uid);
|
|
||||||
setSelectedAnnUIDs(prev => {
|
|
||||||
const next = [...prev];
|
|
||||||
const newSelected = remaining.length > 0 ? remaining[0].annotationUID : null;
|
|
||||||
next[viewportIdx] = newSelected;
|
|
||||||
allAnnotations.forEach(ann => {
|
|
||||||
ann.isSelected = ann.annotationUID === newSelected;
|
|
||||||
});
|
});
|
||||||
cornerstoneTools.annotation.selection.setAnnotationSelected(newSelected);
|
};
|
||||||
|
|
||||||
|
const handleDeleteAnnotation = (viewportIdx: number, uid: string, currentAnnotations: any[]) => {
|
||||||
|
const allAnnotations = cornerstoneTools.annotation.state.getAllAnnotations() || [];
|
||||||
|
allAnnotations.forEach(ann => {
|
||||||
|
if (ann.annotationUID === uid) ann.isSelected = false;
|
||||||
|
});
|
||||||
|
cornerstoneTools.annotation.state.removeAnnotation(uid);
|
||||||
renderingEngineRef.current.render();
|
renderingEngineRef.current.render();
|
||||||
return next;
|
//}
|
||||||
});
|
// Auto-select another annotation if available
|
||||||
};
|
const remaining = currentAnnotations.filter(ann => ann.annotationUID !== uid);
|
||||||
|
setSelectedAnnUIDs(prev => {
|
||||||
|
const next = [...prev];
|
||||||
|
const newSelected = remaining.length > 0 ? remaining[0].annotationUID : null;
|
||||||
|
next[viewportIdx] = newSelected;
|
||||||
|
allAnnotations.forEach(ann => {
|
||||||
|
ann.isSelected = ann.annotationUID === newSelected;
|
||||||
|
});
|
||||||
|
cornerstoneTools.annotation.selection.setAnnotationSelected(newSelected);
|
||||||
|
renderingEngineRef.current.render();
|
||||||
|
return next;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
// Add this function to gather and format metadata:
|
// Add this function to gather and format metadata:
|
||||||
const handleShowMetadata = () => {
|
const handleShowMetadata = () => {
|
||||||
@@ -510,27 +510,27 @@ const handleDeleteAnnotation = (viewportIdx: number, uid: string, currentAnnotat
|
|||||||
|
|
||||||
const [altPressed, setAltPressed] = useState(false);
|
const [altPressed, setAltPressed] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const mainToolGroup = ToolGroupManager.getToolGroup(MAIN_TOOL_GROUP_ID);
|
const mainToolGroup = ToolGroupManager.getToolGroup(MAIN_TOOL_GROUP_ID);
|
||||||
if (!mainToolGroup) return;
|
if (!mainToolGroup) return;
|
||||||
|
|
||||||
// List all annotation tools you use
|
// List all annotation tools you use
|
||||||
[
|
[
|
||||||
'ArrowAnnotate',
|
'ArrowAnnotate',
|
||||||
'NoLabelArrowAnnotate',
|
'NoLabelArrowAnnotate',
|
||||||
'Length',
|
'Length',
|
||||||
'RectangleROI',
|
'RectangleROI',
|
||||||
'EllipticalROI',
|
'EllipticalROI',
|
||||||
'PlanarFreehandROI',
|
'PlanarFreehandROI',
|
||||||
'PlanarFreehandContourSegmentation',
|
'PlanarFreehandContourSegmentation',
|
||||||
// Add more if needed
|
// Add more if needed
|
||||||
].forEach(toolName => {
|
].forEach(toolName => {
|
||||||
mainToolGroup.setToolConfiguration(toolName, {
|
mainToolGroup.setToolConfiguration(toolName, {
|
||||||
color: '#00e676', // normal color
|
color: '#00e676', // normal color
|
||||||
selectedColor: '#ffeb3b', // yellow for selected
|
selectedColor: '#ffeb3b', // yellow for selected
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
}, [MAIN_TOOL_GROUP_ID]);
|
||||||
}, [MAIN_TOOL_GROUP_ID]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleKeyDown = (e: KeyboardEvent) => {
|
const handleKeyDown = (e: KeyboardEvent) => {
|
||||||
@@ -1161,14 +1161,17 @@ useEffect(() => {
|
|||||||
const viewport = renderingEngine.getViewport(`CT_${viewportIdx}`);
|
const viewport = renderingEngine.getViewport(`CT_${viewportIdx}`);
|
||||||
if (!viewport || typeof viewport.getImageIds !== "function") return;
|
if (!viewport || typeof viewport.getImageIds !== "function") return;
|
||||||
const imageIds = viewport.getImageIds();
|
const imageIds = viewport.getImageIds();
|
||||||
const idx = imageIds.indexOf(imageId);
|
// Some stacks may have imageIds with or without "wadouri:" prefix, so normalize for comparison
|
||||||
|
const normImageIds = imageIds.map(id => id.replace(/^wadouri:/, ""));
|
||||||
|
const normTarget = imageId.replace(/^wadouri:/, "");
|
||||||
|
const idx = normImageIds.indexOf(normTarget);
|
||||||
if (idx !== -1 && typeof viewport.setImageIdIndex === "function") {
|
if (idx !== -1 && typeof viewport.setImageIdIndex === "function") {
|
||||||
csUtilities.jumpToSlice(viewport.element, { imageIndex: idx });
|
csUtilities.jumpToSlice(viewport.element, { imageIndex: idx });
|
||||||
updateOverlay(viewportIdx, viewport);
|
updateOverlay(viewportIdx, viewport);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Attach to window with a unique name
|
// Attach to window with a unique name
|
||||||
window[`jumpToSliceByImageId_${apiKey}`] = jumpToSliceByImageId;
|
window[`jumpToSliceByImageId_${apiKey}`] = jumpToSliceByImageId;
|
||||||
|
|
||||||
|
|
||||||
// Export the current viewer state as JSON
|
// Export the current viewer state as JSON
|
||||||
@@ -1351,6 +1354,11 @@ useEffect(() => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
window[`importLegacyAnnotations_${apiKey}`] = (json: string) => {
|
window[`importLegacyAnnotations_${apiKey}`] = (json: string) => {
|
||||||
|
// Remove all existing annotations
|
||||||
|
if (cornerstoneTools.annotation.state.removeAllAnnotations) {
|
||||||
|
cornerstoneTools.annotation.state.removeAllAnnotations();
|
||||||
|
}
|
||||||
|
|
||||||
let legacyData: any;
|
let legacyData: any;
|
||||||
try {
|
try {
|
||||||
legacyData = typeof json === "string" ? JSON.parse(json) : json;
|
legacyData = typeof json === "string" ? JSON.parse(json) : json;
|
||||||
@@ -1359,17 +1367,13 @@ useEffect(() => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove all existing annotations
|
|
||||||
if (cornerstoneTools.annotation.state.removeAllAnnotations) {
|
|
||||||
cornerstoneTools.annotation.state.removeAllAnnotations();
|
|
||||||
}
|
|
||||||
|
|
||||||
const toolNameMap: Record<string, string> = {
|
const toolNameMap: Record<string, string> = {
|
||||||
ArrowAnnotate: "ArrowAnnotate",
|
ArrowAnnotate: "ArrowAnnotate",
|
||||||
// Add more mappings if needed
|
// Add more mappings if needed
|
||||||
};
|
};
|
||||||
|
|
||||||
Object.entries(legacyData).forEach(([imageId, tools]) => {
|
Object.entries(legacyData).forEach(([imageId, tools]) => {
|
||||||
|
console.log("Importing legacy annotations for imageId:", imageId);
|
||||||
Object.entries(tools as any).forEach(([legacyTool, toolData]: [string, any]) => {
|
Object.entries(tools as any).forEach(([legacyTool, toolData]: [string, any]) => {
|
||||||
const cs3Tool = toolNameMap[legacyTool] || legacyTool;
|
const cs3Tool = toolNameMap[legacyTool] || legacyTool;
|
||||||
if (toolData.data && Array.isArray(toolData.data)) {
|
if (toolData.data && Array.isArray(toolData.data)) {
|
||||||
@@ -1410,8 +1414,11 @@ useEffect(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// --- Add rotation if present in legacy annotation ---
|
||||||
|
// Does this actually exist?
|
||||||
|
if (typeof ann.rotation === "number") {
|
||||||
|
metadata.rotation = ann.rotation;
|
||||||
|
}
|
||||||
|
|
||||||
const annotation = {
|
const annotation = {
|
||||||
annotationUID: ann.uuid, // || cornerstoneTools.utilities.uuidv4(),
|
annotationUID: ann.uuid, // || cornerstoneTools.utilities.uuidv4(),
|
||||||
@@ -1471,7 +1478,6 @@ useEffect(() => {
|
|||||||
}
|
}
|
||||||
if (typeof legacy.invert === "boolean") props.invert = legacy.invert;
|
if (typeof legacy.invert === "boolean") props.invert = legacy.invert;
|
||||||
if (typeof legacy.pixelReplication === "boolean") props.interpolationType = legacy.pixelReplication ? 0 : 1;
|
if (typeof legacy.pixelReplication === "boolean") props.interpolationType = legacy.pixelReplication ? 0 : 1;
|
||||||
if (typeof legacy.rotation === "number") props.rotation = legacy.rotation;
|
|
||||||
if (typeof legacy.hflip === "boolean") props.hflip = legacy.hflip;
|
if (typeof legacy.hflip === "boolean") props.hflip = legacy.hflip;
|
||||||
if (typeof legacy.vflip === "boolean") props.vflip = legacy.vflip;
|
if (typeof legacy.vflip === "boolean") props.vflip = legacy.vflip;
|
||||||
|
|
||||||
@@ -1493,6 +1499,10 @@ useEffect(() => {
|
|||||||
viewport.setCamera(camera);
|
viewport.setCamera(camera);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// --- Fix: Import rotation from legacy viewport ---
|
||||||
|
if (typeof legacy.rotation === "number") {
|
||||||
|
viewport.setRotation(legacy.rotation);
|
||||||
|
}
|
||||||
|
|
||||||
viewport.render();
|
viewport.render();
|
||||||
updateOverlay(activeViewport ?? 0, viewport);
|
updateOverlay(activeViewport ?? 0, viewport);
|
||||||
@@ -1732,7 +1742,8 @@ useEffect(() => {
|
|||||||
ann =>
|
ann =>
|
||||||
ann.metadata &&
|
ann.metadata &&
|
||||||
ann.metadata.referencedImageId &&
|
ann.metadata.referencedImageId &&
|
||||||
imageIds.includes(ann.metadata.referencedImageId)
|
imageIds.includes(ann.metadata.referencedImageId) &&
|
||||||
|
ann.metadata.toolName !== ReferenceCursors.toolName // Exclude ReferenceCursors
|
||||||
);
|
);
|
||||||
if (!stackAnnotations.length) return null;
|
if (!stackAnnotations.length) return null;
|
||||||
// Find the annotation(s) on the current image
|
// Find the annotation(s) on the current image
|
||||||
@@ -1786,23 +1797,23 @@ useEffect(() => {
|
|||||||
}}
|
}}
|
||||||
title="Previous Annotation"
|
title="Previous Annotation"
|
||||||
aria-label="Previous Annotation"
|
aria-label="Previous Annotation"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
const renderingEngine = renderingEngineRef.current;
|
const renderingEngine = renderingEngineRef.current;
|
||||||
const viewport = renderingEngine?.getViewport(`CT_${i}`);
|
const viewport = renderingEngine?.getViewport(`CT_${i}`);
|
||||||
if (!viewport || typeof viewport.getCurrentImageIdIndex !== "function") return;
|
if (!viewport || typeof viewport.getCurrentImageIdIndex !== "function") return;
|
||||||
const currentIdx = viewport.getCurrentImageIdIndex();
|
const currentIdx = viewport.getCurrentImageIdIndex();
|
||||||
const prevIdx = findNextAnnotationIdx(currentIdx, imageIds, stackAnnotations, -1);
|
const prevIdx = findNextAnnotationIdx(currentIdx, imageIds, stackAnnotations, -1);
|
||||||
if (prevIdx !== null && typeof viewport.setImageIdIndex === "function") {
|
if (prevIdx !== null && typeof viewport.setImageIdIndex === "function") {
|
||||||
csUtilities.jumpToSlice(viewport.element, { imageIndex: prevIdx });
|
csUtilities.jumpToSlice(viewport.element, { imageIndex: prevIdx });
|
||||||
updateOverlay(i, viewport);
|
updateOverlay(i, viewport);
|
||||||
// Select the first annotation on the new image
|
// Select the first annotation on the new image
|
||||||
const newImageId = imageIds[prevIdx];
|
const newImageId = imageIds[prevIdx];
|
||||||
const annsOnImage = stackAnnotations.filter(
|
const annsOnImage = stackAnnotations.filter(
|
||||||
ann => ann.metadata.referencedImageId === newImageId
|
ann => ann.metadata.referencedImageId === newImageId
|
||||||
);
|
);
|
||||||
handleSelectAnnotation(i, annsOnImage.length > 0 ? annsOnImage[0].annotationUID : null);
|
handleSelectAnnotation(i, annsOnImage.length > 0 ? annsOnImage[0].annotationUID : null);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<span aria-hidden="true">◀</span>
|
<span aria-hidden="true">◀</span>
|
||||||
</button>
|
</button>
|
||||||
@@ -1826,23 +1837,23 @@ onClick={() => {
|
|||||||
}}
|
}}
|
||||||
title="Next Annotation"
|
title="Next Annotation"
|
||||||
aria-label="Next Annotation"
|
aria-label="Next Annotation"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
const renderingEngine = renderingEngineRef.current;
|
const renderingEngine = renderingEngineRef.current;
|
||||||
const viewport = renderingEngine?.getViewport(`CT_${i}`);
|
const viewport = renderingEngine?.getViewport(`CT_${i}`);
|
||||||
if (!viewport || typeof viewport.getCurrentImageIdIndex !== "function") return;
|
if (!viewport || typeof viewport.getCurrentImageIdIndex !== "function") return;
|
||||||
const currentIdx = viewport.getCurrentImageIdIndex();
|
const currentIdx = viewport.getCurrentImageIdIndex();
|
||||||
const nextIdx = findNextAnnotationIdx(currentIdx, imageIds, stackAnnotations, 1);
|
const nextIdx = findNextAnnotationIdx(currentIdx, imageIds, stackAnnotations, 1);
|
||||||
if (nextIdx !== null && typeof viewport.setImageIdIndex === "function") {
|
if (nextIdx !== null && typeof viewport.setImageIdIndex === "function") {
|
||||||
csUtilities.jumpToSlice(viewport.element, { imageIndex: nextIdx });
|
csUtilities.jumpToSlice(viewport.element, { imageIndex: nextIdx });
|
||||||
updateOverlay(i, viewport);
|
updateOverlay(i, viewport);
|
||||||
// Select the first annotation on the new image
|
// Select the first annotation on the new image
|
||||||
const newImageId = imageIds[nextIdx];
|
const newImageId = imageIds[nextIdx];
|
||||||
const annsOnImage = stackAnnotations.filter(
|
const annsOnImage = stackAnnotations.filter(
|
||||||
ann => ann.metadata.referencedImageId === newImageId
|
ann => ann.metadata.referencedImageId === newImageId
|
||||||
);
|
);
|
||||||
handleSelectAnnotation(i, annsOnImage.length > 0 ? annsOnImage[0].annotationUID : null);
|
handleSelectAnnotation(i, annsOnImage.length > 0 ? annsOnImage[0].annotationUID : null);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<span aria-hidden="true">▶</span>
|
<span aria-hidden="true">▶</span>
|
||||||
</button>
|
</button>
|
||||||
@@ -1850,26 +1861,26 @@ onClick={() => {
|
|||||||
{currentAnnotations.length > 0 && (
|
{currentAnnotations.length > 0 && (
|
||||||
<div style={{ display: "flex", gap: 2, alignItems: "center" }}>
|
<div style={{ display: "flex", gap: 2, alignItems: "center" }}>
|
||||||
{currentAnnotations.map(ann => (
|
{currentAnnotations.map(ann => (
|
||||||
<div
|
<div
|
||||||
key={ann.annotationUID}
|
key={ann.annotationUID}
|
||||||
style={{
|
style={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
background: selectedAnnUID === ann.annotationUID ? "rgb(4, 225, 0)" : "#444",
|
background: selectedAnnUID === ann.annotationUID ? "rgb(4, 225, 0)" : "#444",
|
||||||
color: selectedAnnUID === ann.annotationUID ? "#222" : "#fff",
|
color: selectedAnnUID === ann.annotationUID ? "#222" : "#fff",
|
||||||
borderRadius: "4px",
|
borderRadius: "4px",
|
||||||
padding: "2px 6px",
|
padding: "2px 6px",
|
||||||
marginRight: 2,
|
marginRight: 2,
|
||||||
cursor: "pointer",
|
cursor: "pointer",
|
||||||
border: selectedAnnUID === ann.annotationUID ? "2px solid #1976d2" : "none",
|
border: selectedAnnUID === ann.annotationUID ? "2px solid #1976d2" : "none",
|
||||||
fontWeight: selectedAnnUID === ann.annotationUID ? "bold" : "normal",
|
fontWeight: selectedAnnUID === ann.annotationUID ? "bold" : "normal",
|
||||||
}}
|
}}
|
||||||
onClick={() => handleSelectAnnotation(i, ann.annotationUID)}
|
onClick={() => handleSelectAnnotation(i, ann.annotationUID)}
|
||||||
>
|
>
|
||||||
<span style={{ marginRight: 4 }}>
|
<span style={{ marginRight: 4 }}>
|
||||||
{selectedAnnUID === ann.annotationUID ? "★ " : ""}
|
{selectedAnnUID === ann.annotationUID ? "★ " : ""}
|
||||||
{ann.metadata.toolName || "Annotation"}
|
{ann.metadata.toolName || "Annotation"}
|
||||||
</span>
|
</span>
|
||||||
<button
|
<button
|
||||||
style={{
|
style={{
|
||||||
background: "#b71c1c",
|
background: "#b71c1c",
|
||||||
@@ -1891,7 +1902,7 @@ onClick={() => {
|
|||||||
aria-label="Delete annotation"
|
aria-label="Delete annotation"
|
||||||
onClick={e => {
|
onClick={e => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
handleDeleteAnnotation(i, ann.annotationUID, currentAnnotations);
|
handleDeleteAnnotation(i, ann.annotationUID, currentAnnotations);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
🗑️
|
🗑️
|
||||||
@@ -2123,7 +2134,7 @@ onClick={() => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{isVisible && (
|
{isVisible && availableStacks.length > 1 && (
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
position: "absolute",
|
position: "absolute",
|
||||||
|
|||||||
+56
-49
@@ -6,60 +6,67 @@ import Nifti from './Nifti.tsx'
|
|||||||
import './index.css'
|
import './index.css'
|
||||||
import { createImageIds, availableImageIds } from "./lib/createImageIds.ts"
|
import { createImageIds, availableImageIds } from "./lib/createImageIds.ts"
|
||||||
|
|
||||||
const test_containers = document.querySelectorAll(".dicom-viewer-test-root");
|
// Helper to convert URLs to wadouri
|
||||||
test_containers.forEach((container) => {
|
const toWadouri = (arr: string[]) =>
|
||||||
const id = container.id || '';
|
arr.map(url => url.startsWith("wadouri:") ? url : `wadouri:${url}`);
|
||||||
const imageStacks = () => availableImageIds();
|
|
||||||
createRoot(container).render(
|
|
||||||
<App
|
|
||||||
container_id={id}
|
|
||||||
imageStacks={imageStacks}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
const containers = document.querySelectorAll(".dicom-viewer-root");
|
export function mountDicomViewers() {
|
||||||
containers.forEach((container) => {
|
// Test containers
|
||||||
const id = container.id || '';
|
const test_containers = document.querySelectorAll(".dicom-viewer-test-root");
|
||||||
let imageStacks;
|
test_containers.forEach((container) => {
|
||||||
|
const id = container.id || '';
|
||||||
|
const imageStacks = () => availableImageIds();
|
||||||
|
createRoot(container).render(
|
||||||
|
<App
|
||||||
|
container_id={id}
|
||||||
|
imageStacks={imageStacks}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
// Helper to convert URLs to wadouri
|
// Main containers
|
||||||
const toWadouri = (arr: string[]) =>
|
const containers = document.querySelectorAll(".dicom-viewer-root");
|
||||||
arr.map(url => url.startsWith("wadouri:") ? url : `wadouri:${url}`);
|
containers.forEach((container) => {
|
||||||
|
const id = container.id || '';
|
||||||
|
let imageStacks;
|
||||||
|
|
||||||
// Check for data-images attribute
|
const dataImages = container.getAttribute('data-images');
|
||||||
const dataImages = container.getAttribute('data-images');
|
if (dataImages) {
|
||||||
if (dataImages) {
|
let parsed;
|
||||||
let parsed;
|
try {
|
||||||
try {
|
parsed = JSON.parse(dataImages);
|
||||||
parsed = JSON.parse(dataImages);
|
} catch (e) {
|
||||||
} catch (e) {
|
console.error("Invalid data-images JSON:", e);
|
||||||
console.error("Invalid data-images JSON:", e);
|
parsed = [];
|
||||||
parsed = [];
|
}
|
||||||
}
|
if (Array.isArray(parsed) && parsed.length > 0 && typeof parsed[0] === "string") {
|
||||||
// If it's a flat list, wrap in an array to make it a stack list and convert to wadouri
|
imageStacks = () => Promise.resolve([toWadouri(parsed)]);
|
||||||
if (Array.isArray(parsed) && parsed.length > 0 && typeof parsed[0] === "string") {
|
} else if (Array.isArray(parsed)) {
|
||||||
imageStacks = () => Promise.resolve([toWadouri(parsed)]);
|
imageStacks = () => Promise.resolve(parsed.map(
|
||||||
} else if (Array.isArray(parsed)) {
|
stack => Array.isArray(stack) ? toWadouri(stack) : []
|
||||||
imageStacks = () => Promise.resolve(parsed.map(
|
));
|
||||||
stack => Array.isArray(stack) ? toWadouri(stack) : []
|
} else {
|
||||||
));
|
imageStacks = () => Promise.resolve([[]]);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
imageStacks = () => Promise.resolve([[]]);
|
imageStacks = () => Promise.resolve([[]]);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
imageStacks = () => Promise.resolve([[]]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read autoCacheStack from data-auto-cache-stack attribute
|
const autoCacheStackAttr = container.getAttribute('data-auto-cache-stack');
|
||||||
const autoCacheStackAttr = container.getAttribute('data-auto-cache-stack');
|
const autoCacheStack = autoCacheStackAttr === "true" || autoCacheStackAttr === "1";
|
||||||
const autoCacheStack = autoCacheStackAttr === "true" || autoCacheStackAttr === "1";
|
|
||||||
|
|
||||||
createRoot(container).render(
|
createRoot(container).render(
|
||||||
<App
|
<App
|
||||||
container_id={id}
|
container_id={id}
|
||||||
imageStacks={imageStacks}
|
imageStacks={imageStacks}
|
||||||
autoCacheStack={autoCacheStack}
|
autoCacheStack={autoCacheStack}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Optionally, call it immediately for static containers:
|
||||||
|
mountDicomViewers();
|
||||||
|
|
||||||
|
// Optionally, expose globally for dynamic use:
|
||||||
|
(window as any).mountDicomViewers = mountDicomViewers;
|
||||||
Reference in New Issue
Block a user