capture changes
This commit is contained in:
parent
64f9c4a387
commit
a323157c62
5
.vscode/extensions.json
vendored
5
.vscode/extensions.json
vendored
@ -5,6 +5,9 @@
|
||||
"dbaeumer.vscode-eslint",
|
||||
"mikestead.dotenv",
|
||||
"bungcip.better-toml",
|
||||
"silvenon.mdx"
|
||||
"silvenon.mdx",
|
||||
"gruntfuggly.todo-tree",
|
||||
"wayou.vscode-todo-highlight",
|
||||
"bradlc.vscode-tailwindcss"
|
||||
]
|
||||
}
|
||||
|
||||
@ -87,6 +87,9 @@ function createDicomWebApi(dicomWebConfig) {
|
||||
},
|
||||
retrieve: {
|
||||
series: {
|
||||
// TODO: change queryParams to `StudyInstanceUID` for now?
|
||||
// Conduct query, return a promise like others
|
||||
// Await this call and add to dicomMetadataStore after receiving result
|
||||
metadata: (queryParams, callback) => {
|
||||
let { StudyInstanceUIDs } = urlUtil.parse(queryParams, true);
|
||||
|
||||
@ -125,6 +128,32 @@ function createDicomWebApi(dicomWebConfig) {
|
||||
},
|
||||
},
|
||||
},
|
||||
retrieveSeriesMetadata: async ({ StudyInstanceUID } = {}) => {
|
||||
if (!StudyInstanceUID) {
|
||||
throw new Error(
|
||||
'Unable to query for SeriesMetadata without StudyInstanceUID'
|
||||
);
|
||||
}
|
||||
|
||||
// Get Series
|
||||
const seriesPromises = await retrieveStudyMetadata(
|
||||
naturalizeDataset,
|
||||
StudyInstanceUID,
|
||||
enableStudyLazyLoad
|
||||
);
|
||||
|
||||
// Async load series, store as retrieved
|
||||
function storeInstances(instances) {
|
||||
const naturalizedInstances = instances.map(naturalizeDataset);
|
||||
|
||||
dicomMetadataStore.addInstances(naturalizedInstances);
|
||||
}
|
||||
|
||||
seriesPromises.forEach(async seriesPromise => {
|
||||
const instances = await seriesPromise;
|
||||
storeInstances(instances);
|
||||
});
|
||||
},
|
||||
getImageIdsForDisplaySet(displaySet) {
|
||||
const images = displaySet.images;
|
||||
const imageIds = [];
|
||||
|
||||
@ -1,42 +1,53 @@
|
||||
import React, { useState, useEffect, useCallback } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { StudyBrowser } from '@ohif/ui';
|
||||
//
|
||||
import { StudyBrowser, useImageViewer } from '@ohif/ui';
|
||||
import { dicomMetadataStore } from '@ohif/core';
|
||||
// This has to import from somewhere else...
|
||||
|
||||
function PanelStudyBrowser({
|
||||
servicesManager,
|
||||
DisplaySetService,
|
||||
getImageSrc,
|
||||
getStudiesByPatientId,
|
||||
getStudiesForPatientByStudyInstanceUID,
|
||||
requestDisplaySetCreationForStudy,
|
||||
dataSource,
|
||||
}) {
|
||||
const { DisplaySetService } = servicesManager.services;
|
||||
const currentDisplaySets = DisplaySetService.activeDisplaySets || [];
|
||||
// TODO: Deep copy? Or By IDs?
|
||||
// TODO: May need to be mapped to a different shape?
|
||||
|
||||
const [displaySets, setDisplaySets] = useState(currentDisplaySets);
|
||||
// TODO: Grab from URL, or pass in as part of contract to panels
|
||||
const [primaryStudyInstanceUID, setPrimaryStudyInstanceUID] = useState();
|
||||
const [activeTabName, setActiveTabName] = useState('primary');
|
||||
const [studyData, setStudyData] = useState([]);
|
||||
const [studiesForPatient, setStudiesForPatient] = useState([]);
|
||||
const [thumbnailImageSrcMap, setThumbnailImageSrcMap] = useState(new Map());
|
||||
const [{ StudyInstanceUIDs }, dispatch] = useImageViewer();
|
||||
// vv What we want to render
|
||||
const [studyDisplayList, setStudyDisplayList] = useState([]);
|
||||
|
||||
useEffect(() => {
|
||||
const mappedStudiesFromDataSource = _mapDataSourceStudies(
|
||||
studiesForPatient
|
||||
);
|
||||
// Fetch all studies for the patient in each primary study
|
||||
async function fetchStudiesForPatient(StudyInstanceUID) {
|
||||
const qidoStudiesForPatient =
|
||||
(await getStudiesForPatientByStudyInstanceUID(StudyInstanceUID)) || [];
|
||||
|
||||
const newStudyData = _mergeStudyDataAndDataSourceStudies(
|
||||
studyData,
|
||||
mappedStudiesFromDataSource,
|
||||
displaySets
|
||||
);
|
||||
const mappedStudies = _mapDataSourceStudies(qidoStudiesForPatient);
|
||||
|
||||
addDisplaySetsToStudyData(newStudyData, displaySets, thumbnailImageSrcMap);
|
||||
const updatedStudyDisplayList = _mergeStudyDisplayListAndDataSourceStudies(
|
||||
studyDisplayList,
|
||||
mappedStudies,
|
||||
displaySets
|
||||
);
|
||||
|
||||
addDisplaySetsToStudyDisplayList(
|
||||
updatedStudyDisplayList,
|
||||
displaySets,
|
||||
thumbnailImageSrcMap
|
||||
);
|
||||
|
||||
setStudyDisplayList(updatedStudyDisplayList);
|
||||
}
|
||||
|
||||
StudyInstanceUIDs.forEach(sid => fetchStudiesForPatient(sid));
|
||||
}, [StudyInstanceUIDs, getStudiesForPatientByStudyInstanceUID]);
|
||||
|
||||
setStudyData(newStudyData);
|
||||
}, [studiesForPatient, thumbnailImageSrcMap]);
|
||||
|
||||
const updateThumbnailMap = (displaySetInstanceUID, imageSrc) => {
|
||||
setThumbnailImageSrcMap(
|
||||
@ -68,7 +79,7 @@ function PanelStudyBrowser({
|
||||
setDisplaySets(displaySetsUI);
|
||||
}
|
||||
|
||||
function addDisplaySetsToStudyData(
|
||||
function addDisplaySetsToStudyDisplayList(
|
||||
studies,
|
||||
displaySets,
|
||||
thumbnailImageSrcMap
|
||||
@ -101,40 +112,9 @@ function PanelStudyBrowser({
|
||||
const imageSrc = await getImageSrc(imageId);
|
||||
updateThumbnailMap(uid, imageSrc);
|
||||
});
|
||||
|
||||
const firstDisplaySet = newDisplaySets[0];
|
||||
if (!studiesForPatient.length && firstDisplaySet) {
|
||||
async function doAsyncStuff() {
|
||||
const qidoStuff = await _getStudiesByPatientId(
|
||||
firstDisplaySet,
|
||||
getStudiesByPatientId
|
||||
);
|
||||
|
||||
setStudiesForPatient(qidoStuff);
|
||||
setPrimaryStudyInstanceUID(firstDisplaySet.StudyInstanceUID);
|
||||
}
|
||||
|
||||
doAsyncStuff();
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
// TODO: IF WE HAVE OUR FIRST DISPLAY SET...
|
||||
// QIDO AWAY
|
||||
// IF WE DO NOT, WAIT FOR FIRST DISPLAY_SETS_ADDED
|
||||
const firstDisplaySet = displaySets[0];
|
||||
if (firstDisplaySet) {
|
||||
async function doAsyncStuff() {
|
||||
const qidoStuff = await _getStudiesByPatientId(
|
||||
firstDisplaySet,
|
||||
getStudiesByPatientId
|
||||
);
|
||||
setPrimaryStudyInstanceUID(firstDisplaySet.StudyInstanceUID);
|
||||
setStudiesForPatient(qidoStuff);
|
||||
}
|
||||
doAsyncStuff();
|
||||
}
|
||||
|
||||
const subscriptions = [
|
||||
DisplaySetService.subscribe(
|
||||
DisplaySetService.EVENTS.DISPLAY_SETS_ADDED,
|
||||
@ -151,12 +131,12 @@ function PanelStudyBrowser({
|
||||
};
|
||||
}, []);
|
||||
|
||||
const primary = studyData.find(
|
||||
study => study.studyInstanceUid === primaryStudyInstanceUID
|
||||
const primaryStudies = studyDisplayList.filter(study =>
|
||||
StudyInstanceUIDs.includes(study.studyInstanceUid)
|
||||
);
|
||||
|
||||
// TODO: Filter allStudies to dates within one year of current date
|
||||
const recentStudies = studyData.filter(study => {
|
||||
const recentStudies = studyDisplayList.filter(study => {
|
||||
return true; // TODO: check study.date
|
||||
});
|
||||
|
||||
@ -164,7 +144,7 @@ function PanelStudyBrowser({
|
||||
{
|
||||
name: 'primary',
|
||||
label: 'Primary',
|
||||
studies: primary ? [primary] : [],
|
||||
studies: primaryStudies || [],
|
||||
},
|
||||
{
|
||||
name: 'recent',
|
||||
@ -174,63 +154,42 @@ function PanelStudyBrowser({
|
||||
{
|
||||
name: 'all',
|
||||
label: 'All',
|
||||
studies: studyData,
|
||||
studies: studyDisplayList,
|
||||
},
|
||||
];
|
||||
|
||||
function onClickStudy(StudyInstanceUID) {
|
||||
const study = studyData.find(a => a.studyInstanceUid === StudyInstanceUID);
|
||||
if (study && study.displaySets && study.displaySets.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.warn(`onClickStudy: ${StudyInstanceUID}`);
|
||||
// TODO: This is weird, why can't the data source just be used as
|
||||
// as function that doesn't expect a query string?
|
||||
const queryParams = `?StudyInstanceUIDs=${StudyInstanceUID}`;
|
||||
|
||||
dataSource.retrieve.series.metadata(
|
||||
queryParams,
|
||||
DisplaySetService.makeDisplaySets
|
||||
);
|
||||
// TODO: Should "expand" appropriate study (already handled by component?)
|
||||
// TODO: Should not fire this on "close"
|
||||
function _handleStudyClick(StudyInstanceUID) {
|
||||
requestDisplaySetCreationForStudy(DisplaySetService, StudyInstanceUID);
|
||||
}
|
||||
|
||||
const memoOnClickStudy = useCallback(
|
||||
StudyInstanceUID => {
|
||||
onClickStudy(StudyInstanceUID);
|
||||
},
|
||||
[studyData]
|
||||
);
|
||||
|
||||
return (
|
||||
<StudyBrowser
|
||||
activeTabName={activeTabName}
|
||||
tabs={tabs}
|
||||
onClickStudy={memoOnClickStudy}
|
||||
onClickStudy={_handleStudyClick}
|
||||
onSetTabActive={setActiveTabName}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
PanelStudyBrowser.propTypes = {
|
||||
DisplaySetService: PropTypes.shape({
|
||||
EVENTS: PropTypes.object.isRequired,
|
||||
hasDisplaySetsForStudy: PropTypes.func.isRequired,
|
||||
subscribe: PropTypes.func.isRequired,
|
||||
}).isRequired,
|
||||
dataSource: PropTypes.shape({
|
||||
getImageIdsForDisplaySet: PropTypes.func.isRequired,
|
||||
}).isRequired,
|
||||
getImageSrc: PropTypes.func.isRequired,
|
||||
getStudiesByPatientId: PropTypes.func.isRequired,
|
||||
getStudiesForPatientByStudyInstanceUID: PropTypes.func.isRequired,
|
||||
requestDisplaySetCreationForStudy: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default PanelStudyBrowser;
|
||||
|
||||
async function _getStudiesByPatientId(displaySet, getStudiesByPatientId) {
|
||||
const study = dicomMetadataStore.getStudy(displaySet.StudyInstanceUID);
|
||||
const instance = study.series[0].instances[0];
|
||||
const PatientID = instance.PatientID;
|
||||
const studiesByPatientId = await getStudiesByPatientId(PatientID);
|
||||
|
||||
return studiesByPatientId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps from the DataSource's format to a naturalized object
|
||||
*
|
||||
@ -253,11 +212,14 @@ function _mapDataSourceStudies(studies) {
|
||||
});
|
||||
}
|
||||
|
||||
function _mergeStudyDataAndDataSourceStudies(studyData, mappedQidoStudies) {
|
||||
function _mergeStudyDisplayListAndDataSourceStudies(
|
||||
studyDisplayList,
|
||||
mappedQidoStudies
|
||||
) {
|
||||
return mappedQidoStudies.map(qidoStudy => {
|
||||
const { StudyInstanceUID } = qidoStudy;
|
||||
const existing =
|
||||
studyData.find(a => a.studyInstanceUid === StudyInstanceUID) || {};
|
||||
studyDisplayList.find(a => a.studyInstanceUid === StudyInstanceUID) || {};
|
||||
|
||||
return Object.assign(
|
||||
{},
|
||||
@ -275,39 +237,3 @@ function _mergeStudyDataAndDataSourceStudies(studyData, mappedQidoStudies) {
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
function _mergeStudyDataAndInstanceStudies(studyData, mappedInstanceStudies) {
|
||||
const merged = [];
|
||||
mappedInstanceStudies.map(studyFromInstances => {
|
||||
const existing = studyData.find(
|
||||
a => a.studyInstanceUid === studyFromInstances.studyInstanceUid
|
||||
);
|
||||
|
||||
if (existing) {
|
||||
merged.push(
|
||||
Object.assign({}, existing, {
|
||||
displaySets: studyFromInstances.displaySets,
|
||||
modalities: studyFromInstances.modalities,
|
||||
})
|
||||
);
|
||||
} else {
|
||||
merged.push(studyFromInstances);
|
||||
}
|
||||
});
|
||||
|
||||
return merged;
|
||||
}
|
||||
|
||||
function _mapStudyFromInstance(StudyInstanceUID) {
|
||||
const study = dicomMetadataStore.getStudy(StudyInstanceUID);
|
||||
const anInstance = study.series[0].instances[0];
|
||||
|
||||
return {
|
||||
studyInstanceUid: StudyInstanceUID,
|
||||
date: anInstance.StudyDate, // TODO: Format this date to DD-MMM-YYYY
|
||||
description: anInstance.StudyDescription,
|
||||
displaySets: [],
|
||||
numInstances: 0,
|
||||
modalitiesSet: new Set(),
|
||||
};
|
||||
}
|
||||
|
||||
@ -3,6 +3,8 @@ import PropTypes from 'prop-types';
|
||||
//
|
||||
import PanelStudyBrowser from './PanelStudyBrowser';
|
||||
import getImageSrcFromImageId from './getImageSrcFromImageId';
|
||||
import getStudiesForPatientByStudyInstanceUID from './getStudiesForPatientByStudyInstanceUID';
|
||||
import requestDisplaySetCreationForStudy from './requestDisplaySetCreationForStudy';
|
||||
|
||||
/**
|
||||
* Wraps the PanelStudyBrowser and provides features afforded by managers/services
|
||||
@ -16,20 +18,30 @@ function WrappedPanelStudyBrowser({
|
||||
extensionManager,
|
||||
servicesManager,
|
||||
}) {
|
||||
// Note: this feels odd
|
||||
// TODO: This should be made available a different way; route should have
|
||||
// already determined our datasource
|
||||
const dataSource = extensionManager.getDataSources('dicomweb')[0];
|
||||
const getStudiesByPatientId = patientId =>
|
||||
dataSource.query.studies.search({ patientId });
|
||||
const _getStudiesForPatientByStudyInstanceUID = getStudiesForPatientByStudyInstanceUID.bind(
|
||||
null,
|
||||
dataSource
|
||||
);
|
||||
const _getImageSrcFromImageId = _createGetImageSrcFromImageIdFn(
|
||||
commandsManager.getCommand.bind(commandsManager)
|
||||
);
|
||||
const _requestDisplaySetCreationForStudy = requestDisplaySetCreationForStudy.bind(
|
||||
null,
|
||||
dataSource
|
||||
);
|
||||
|
||||
return (
|
||||
<PanelStudyBrowser
|
||||
servicesManager={servicesManager}
|
||||
DisplaySetService={servicesManager.services.DisplaySetService}
|
||||
dataSource={dataSource}
|
||||
getImageSrc={_getImageSrcFromImageId}
|
||||
getStudiesByPatientId={getStudiesByPatientId}
|
||||
getStudiesForPatientByStudyInstanceUID={
|
||||
_getStudiesForPatientByStudyInstanceUID
|
||||
}
|
||||
requestDisplaySetCreationForStudy={_requestDisplaySetCreationForStudy}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@ -60,6 +72,7 @@ function _createGetImageSrcFromImageIdFn(getCommand) {
|
||||
WrappedPanelStudyBrowser.propTypes = {
|
||||
commandsManager: PropTypes.object.isRequired,
|
||||
extensionManager: PropTypes.object.isRequired,
|
||||
servicesManager: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
export default WrappedPanelStudyBrowser;
|
||||
|
||||
@ -0,0 +1,23 @@
|
||||
async function getStudiesForPatientByStudyInstanceUID(
|
||||
dataSource,
|
||||
StudyInstanceUID
|
||||
) {
|
||||
// TODO: The `dicomMetadataStore` should short-circuit both of these requests
|
||||
// Data _could_ be here from route query, or if using JSON data source
|
||||
// We could also force this to "await" these values being available in the DICOMStore?
|
||||
// Kind of like promise fulfillment in cornerstone-wado-image-loader when there are multiple
|
||||
// outgoing requests for the same data
|
||||
const getStudyResult = await dataSource.query.studies.search({
|
||||
studyInstanceUid: StudyInstanceUID,
|
||||
});
|
||||
|
||||
// TODO: To Erik's point, the data source likely shouldn't deviate from
|
||||
// Naturalized DICOM JSON when returning. It makes things like this awkward (mrn)
|
||||
if (getStudyResult && getStudyResult.length && getStudyResult[0].mrn) {
|
||||
return dataSource.query.studies.search({
|
||||
patientId: getStudyResult[0].mrn,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export default getStudiesForPatientByStudyInstanceUID;
|
||||
@ -0,0 +1,22 @@
|
||||
function requestDisplaySetCreationForStudy(
|
||||
dataSource,
|
||||
DisplaySetService,
|
||||
StudyInstanceUID
|
||||
) {
|
||||
// TODO: is this already short-circuited by the map of Retrieve promises?
|
||||
if (DisplaySetService.hasDisplaySetsForStudy(StudyInstanceUID)) {
|
||||
return;
|
||||
}
|
||||
|
||||
dataSource.retrieveSeriesMetadata({ StudyInstanceUID });
|
||||
|
||||
// 1. Update DICOMStore
|
||||
// 2. DICOMStore shoots out events
|
||||
// 3. IFF instance is in study/series that is "active" (being viewed?), displaySet is created?
|
||||
// 4. IFF not, just store is updated
|
||||
// 5. IFF active studies change, splitting logic is refired?
|
||||
|
||||
// DisplaySetService.makeDisplaySets
|
||||
}
|
||||
|
||||
export default requestDisplaySetCreationForStudy;
|
||||
@ -107,6 +107,12 @@ export default class DisplaySetService {
|
||||
}
|
||||
};
|
||||
|
||||
hasDisplaySetsForStudy(StudyInstanceUID) {
|
||||
return displaySetCache.some(
|
||||
displaySet => displaySet.StudyInstanceUID === StudyInstanceUID
|
||||
);
|
||||
}
|
||||
|
||||
makeDisplaySetForInstances(instances) {
|
||||
const instance = instances[0];
|
||||
|
||||
|
||||
@ -11,6 +11,9 @@ export {
|
||||
withModal,
|
||||
ViewportDialogProvider,
|
||||
useViewportDialog,
|
||||
ImageViewerContext,
|
||||
ImageViewerProvider,
|
||||
useImageViewer,
|
||||
} from './src/contextProviders';
|
||||
|
||||
/** COMPONENTS */
|
||||
|
||||
18
platform/ui/src/contextProviders/ImageViewerProvider.jsx
Normal file
18
platform/ui/src/contextProviders/ImageViewerProvider.jsx
Normal file
@ -0,0 +1,18 @@
|
||||
import React, { createContext, useContext, useReducer } from 'react';
|
||||
|
||||
// export const IMAGE_VIEWER_DEFAULT_VALUE = {
|
||||
// StudyInstanceUIDs: [],
|
||||
// setImageViewer: () => {},
|
||||
// };
|
||||
|
||||
export const ImageViewerContext = createContext();
|
||||
|
||||
export function ImageViewerProvider({ reducer, initialState, children }) {
|
||||
return (
|
||||
<ImageViewerContext.Provider value={useReducer(reducer, initialState)}>
|
||||
{children}
|
||||
</ImageViewerContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export const useImageViewer = () => useContext(ImageViewerContext);
|
||||
@ -10,4 +10,10 @@ export {
|
||||
useViewportDialog,
|
||||
} from './ViewportDialogProvider';
|
||||
|
||||
export {
|
||||
ImageViewerContext,
|
||||
ImageViewerProvider,
|
||||
useImageViewer,
|
||||
} from './ImageViewerProvider';
|
||||
|
||||
export { default as DragAndDropProvider } from './DragAndDropProvider';
|
||||
|
||||
@ -1,14 +1,13 @@
|
||||
import React, { useEffect, useCallback } from 'react';
|
||||
import React, { useEffect } from 'react';
|
||||
import { useParams } from 'react-router';
|
||||
import PropTypes from 'prop-types';
|
||||
//
|
||||
import { ToolBarManager } from '@ohif/core';
|
||||
import { DragAndDropProvider } from '@ohif/ui';
|
||||
import { DragAndDropProvider, ImageViewerProvider } from '@ohif/ui';
|
||||
//
|
||||
import { useQuery } from '@hooks';
|
||||
import ViewportGrid from '@components/ViewportGrid';
|
||||
import Compose from './Compose';
|
||||
//import DisplaySetCreator from './DisplaySetCreator';
|
||||
|
||||
export default function ModeRoute({
|
||||
location,
|
||||
@ -22,6 +21,10 @@ export default function ModeRoute({
|
||||
const queryStudyInstanceUIDs = query.get('StudyInstanceUIDs');
|
||||
const { StudyInstanceUIDs: paramsStudyInstanceUIDs } = useParams();
|
||||
const StudyInstanceUIDs = queryStudyInstanceUIDs || paramsStudyInstanceUIDs;
|
||||
const StudyInstanceUIDsAsArray =
|
||||
StudyInstanceUIDs && Array.isArray(StudyInstanceUIDs)
|
||||
? StudyInstanceUIDs
|
||||
: [StudyInstanceUIDs];
|
||||
|
||||
const { extensions, sopClassHandlers } = mode;
|
||||
// TODO:
|
||||
@ -89,19 +92,28 @@ export default function ModeRoute({
|
||||
);
|
||||
}, [mode, dataSourceName, location]);
|
||||
|
||||
const reducer = (state, action) => {
|
||||
console.log(state, action);
|
||||
}
|
||||
|
||||
return (
|
||||
<CombinedContextProvider>
|
||||
{/* TODO: extensionManager is already provided to the extension module.
|
||||
* Use it from there instead of passing as a prop here.
|
||||
*/}
|
||||
<DragAndDropProvider>
|
||||
<LayoutComponent
|
||||
{...layoutTemplateData.props}
|
||||
StudyInstanceUIDs={StudyInstanceUIDs}
|
||||
ViewportGridComp={ViewportGridWithDataSource}
|
||||
/>
|
||||
</DragAndDropProvider>
|
||||
</CombinedContextProvider>
|
||||
<ImageViewerProvider
|
||||
initialState={{ StudyInstanceUIDs: StudyInstanceUIDsAsArray }}
|
||||
reducer={reducer}
|
||||
>
|
||||
<CombinedContextProvider>
|
||||
{/* TODO: extensionManager is already provided to the extension module.
|
||||
* Use it from there instead of passing as a prop here.
|
||||
*/}
|
||||
<DragAndDropProvider>
|
||||
<LayoutComponent
|
||||
{...layoutTemplateData.props}
|
||||
StudyInstanceUIDs={StudyInstanceUIDs}
|
||||
ViewportGridComp={ViewportGridWithDataSource}
|
||||
/>
|
||||
</DragAndDropProvider>
|
||||
</CombinedContextProvider>
|
||||
</ImageViewerProvider>
|
||||
);
|
||||
}
|
||||
|
||||
@ -117,4 +129,5 @@ ModeRoute.propTypes = {
|
||||
mode: PropTypes.object.isRequired,
|
||||
dataSourceName: PropTypes.string,
|
||||
extensionManager: PropTypes.object,
|
||||
servicesManager: PropTypes.object,
|
||||
};
|
||||
|
||||
Loading…
Reference in New Issue
Block a user