feat: Add White Labelling, Hanging Protocols, and tons of Extensions / Mode fixes
* modified * new files in existing extensions/libraries/modes * fixed bugs and tests * Clean up * fix: viewport display fixed * Fixed configs for deployment * Removed unnecessary files and functions * Fixed default HP module * Added white labelling * Fixed hash routing * Removed unnecessary routers Co-authored-by: Alireza Sedghi <ar.sedghi@gmail.com>
This commit is contained in:
1 parent
0db81b30f3
commit
99b8dc4759
93 files changed
+3825
-1293
No files matched your search
+16
-21
@@ -3,7 +3,7 @@ import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import i18n from '@ohif/i18n';
|
||||
import { I18nextProvider } from 'react-i18next';
|
||||
import { BrowserRouter, HashRouter } from 'react-router-dom';
|
||||
import { Router } from 'react-router-dom';
|
||||
import {
|
||||
DialogProvider,
|
||||
Modal,
|
||||
@@ -12,7 +12,6 @@ import {
|
||||
ThemeWrapper,
|
||||
ViewportDialogProvider,
|
||||
ViewportGridProvider,
|
||||
HangingProtocolProvider,
|
||||
CineProvider,
|
||||
} from '@ohif/ui';
|
||||
// Viewer Project
|
||||
@@ -20,6 +19,7 @@ import {
|
||||
import { AppConfigProvider } from '@state';
|
||||
import createRoutes from './routes';
|
||||
import appInit from './appInit.js';
|
||||
import history from './history'
|
||||
|
||||
// TODO: Temporarily for testing
|
||||
import '@ohif/mode-longitudinal';
|
||||
@@ -27,9 +27,7 @@ import '@ohif/mode-longitudinal';
|
||||
/**
|
||||
* ENV Variable to determine routing behavior
|
||||
*/
|
||||
const Router = JSON.parse(process.env.USE_HASH_ROUTER)
|
||||
? HashRouter
|
||||
: BrowserRouter;
|
||||
const OHIFRouter = Router
|
||||
|
||||
let commandsManager, extensionManager, servicesManager, hotkeysManager;
|
||||
|
||||
@@ -59,32 +57,29 @@ function App({ config, defaultExtensions }) {
|
||||
UINotificationService,
|
||||
UIViewportDialogService,
|
||||
ViewportGridService, // TODO: Should this be a "UI" Service?
|
||||
HangingProtocolService,
|
||||
CineService
|
||||
} = servicesManager.services;
|
||||
|
||||
return (
|
||||
<AppConfigProvider value={appConfigState}>
|
||||
<I18nextProvider i18n={i18n}>
|
||||
<Router basename={routerBasename}>
|
||||
<OHIFRouter basename={routerBasename} history={history}>
|
||||
<ThemeWrapper>
|
||||
<ViewportGridProvider service={ViewportGridService}>
|
||||
<HangingProtocolProvider service={HangingProtocolService}>
|
||||
<ViewportDialogProvider service={UIViewportDialogService}>
|
||||
<CineProvider service={CineService}>
|
||||
<SnackbarProvider service={UINotificationService}>
|
||||
<DialogProvider service={UIDialogService}>
|
||||
<ModalProvider modal={Modal} service={UIModalService}>
|
||||
{appRoutes}
|
||||
</ModalProvider>
|
||||
</DialogProvider>
|
||||
</SnackbarProvider>
|
||||
</CineProvider>
|
||||
</ViewportDialogProvider>
|
||||
</HangingProtocolProvider>
|
||||
<ViewportDialogProvider service={UIViewportDialogService}>
|
||||
<CineProvider service={CineService}>
|
||||
<SnackbarProvider service={UINotificationService}>
|
||||
<DialogProvider service={UIDialogService}>
|
||||
<ModalProvider modal={Modal} service={UIModalService}>
|
||||
{appRoutes}
|
||||
</ModalProvider>
|
||||
</DialogProvider>
|
||||
</SnackbarProvider>
|
||||
</CineProvider>
|
||||
</ViewportDialogProvider>
|
||||
</ViewportGridProvider>
|
||||
</ThemeWrapper>
|
||||
</Router>
|
||||
</OHIFRouter>
|
||||
</I18nextProvider>
|
||||
</AppConfigProvider>
|
||||
);
|
||||
|
||||
@@ -81,6 +81,7 @@ function appInit(appConfigOrFunc, defaultExtensions) {
|
||||
// appConfig.modes.push(window.segmentationMode);
|
||||
}
|
||||
|
||||
|
||||
return {
|
||||
appConfig,
|
||||
commandsManager,
|
||||
|
||||
@@ -26,38 +26,38 @@ function ViewerViewportGrid(props) {
|
||||
HangingProtocolService,
|
||||
} = servicesManager.services;
|
||||
|
||||
// This is a placeholder for applying hanging protocols
|
||||
// It probably shouldn't be done here
|
||||
// For now it just hangs the first display set in the study in 1x1
|
||||
// as sorted by SeriesNumber
|
||||
|
||||
// Using Hanging protocol engine to match the displaysets
|
||||
useEffect(() => {
|
||||
const { unsubscribe } = DisplaySetService.subscribe(
|
||||
DisplaySetService.EVENTS.DISPLAY_SETS_ADDED,
|
||||
eventData => {
|
||||
const { displaySetsAdded } = eventData;
|
||||
|
||||
const data = HangingProtocolService.getState();
|
||||
|
||||
// TODO: Sometimes this is undefined?
|
||||
const { hpAlreadyApplied } = data;
|
||||
const [
|
||||
matchDetails,
|
||||
hpAlreadyApplied,
|
||||
] = HangingProtocolService.getState();
|
||||
|
||||
if (!matchDetails.length) return;
|
||||
// Match each viewport individually
|
||||
const numViewports = numRows * numCols;
|
||||
|
||||
const numViewports = viewportGrid.numRows * viewportGrid.numCols;
|
||||
for (let i = 0; i < numViewports; i++) {
|
||||
if (hpAlreadyApplied[i] === true) {
|
||||
return;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Temporary until matching is ported back over from the Meteor version.
|
||||
const reqSeriesInstanceUID =
|
||||
data.hangingProtocol.stages[0].viewports[0].seriesMatchingRules[0]
|
||||
.constraint.equals.value;
|
||||
// if current viewport doesn't have a match
|
||||
if (matchDetails[i] === undefined) return
|
||||
|
||||
const { SeriesInstanceUID } = matchDetails[i];
|
||||
const matchingDisplaySet = displaySetsAdded.find(ds => {
|
||||
return ds.SeriesInstanceUID === reqSeriesInstanceUID;
|
||||
return ds.SeriesInstanceUID === SeriesInstanceUID;
|
||||
});
|
||||
|
||||
if (!matchingDisplaySet) {
|
||||
return;
|
||||
continue;
|
||||
}
|
||||
|
||||
viewportGridService.setDisplaysetForViewport({
|
||||
@@ -73,7 +73,23 @@ function ViewerViewportGrid(props) {
|
||||
return () => {
|
||||
unsubscribe();
|
||||
};
|
||||
}, []);
|
||||
}, [numRows, numCols]);
|
||||
|
||||
|
||||
// Layout change based on hanging protocols
|
||||
useEffect(() => {
|
||||
const { unsubscribe } = HangingProtocolService.subscribe(
|
||||
HangingProtocolService.EVENTS.NEW_LAYOUT,
|
||||
({ numRows, numCols }) => {
|
||||
viewportGridService.setLayout({ numRows, numCols });
|
||||
}
|
||||
);
|
||||
|
||||
return () => {
|
||||
unsubscribe();
|
||||
};
|
||||
}, [viewports]);
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
const { unsubscribe } = MeasurementService.subscribe(
|
||||
@@ -82,16 +98,24 @@ function ViewerViewportGrid(props) {
|
||||
const referencedDisplaySetInstanceUID =
|
||||
measurement.displaySetInstanceUID;
|
||||
|
||||
// If the viewport does not contain the displaySet, then hang that displaySet.
|
||||
const viewportsDisplaySetInstanceUIDs = viewports.map(
|
||||
vp => vp.displaySetInstanceUID
|
||||
);
|
||||
|
||||
// if we already have the displayset in one of the viewports
|
||||
if (
|
||||
viewports[viewportIndex].displaySetInstanceUID !==
|
||||
referencedDisplaySetInstanceUID
|
||||
viewportsDisplaySetInstanceUIDs.indexOf(
|
||||
referencedDisplaySetInstanceUID
|
||||
) > -1
|
||||
) {
|
||||
viewportGridService.setDisplaysetForViewport({
|
||||
viewportIndex,
|
||||
displaySetInstanceUID: referencedDisplaySetInstanceUID,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// If not in any of the viewports, hang it inside the active viewport
|
||||
viewportGridService.setDisplaysetForViewport({
|
||||
viewportIndex,
|
||||
displaySetInstanceUID: referencedDisplaySetInstanceUID,
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
@@ -208,11 +232,9 @@ function ViewerViewportGrid(props) {
|
||||
return viewportPanes;
|
||||
};
|
||||
|
||||
// const ViewportPanes = React.useMemo(getViewportPanes, [
|
||||
// viewportComponents,
|
||||
// activeViewportIndex,
|
||||
// viewportGrid,
|
||||
// ]);
|
||||
if (!numCols || !numCols) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<ViewportGrid numRows={numRows} numCols={numCols}>
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
import { createBrowserHistory, createHashHistory } from 'history';
|
||||
const useHashRouting = JSON.parse(process.env.USE_HASH_ROUTER);
|
||||
const router = useHashRouting ? createHashHistory() : createBrowserHistory();
|
||||
|
||||
export default router;
|
||||
@@ -1,14 +1,57 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import React, { useEffect, useState, useRef } from 'react';
|
||||
import { useParams } from 'react-router';
|
||||
import PropTypes from 'prop-types';
|
||||
// TODO: DicomMetadataStore should be injected?
|
||||
import { DicomMetadataStore, utils } from '@ohif/core';
|
||||
import { DicomMetadataStore } from '@ohif/core';
|
||||
import { DragAndDropProvider, ImageViewerProvider } from '@ohif/ui';
|
||||
import { useQuery } from '@hooks';
|
||||
import ViewportGrid from '@components/ViewportGrid';
|
||||
import Compose from './Compose';
|
||||
|
||||
const { isLowPriorityModality } = utils;
|
||||
async function defaultRouteInit({
|
||||
servicesManager,
|
||||
studyInstanceUIDs,
|
||||
dataSource,
|
||||
}) {
|
||||
const {
|
||||
DisplaySetService,
|
||||
HangingProtocolService,
|
||||
} = servicesManager.services;
|
||||
|
||||
const unsubscriptions = [];
|
||||
// TODO: This should be baked into core, not manuall?
|
||||
// DisplaySetService would wire this up?
|
||||
const {
|
||||
unsubscribe: instanceAddedUnsubscribe,
|
||||
} = DicomMetadataStore.subscribe(
|
||||
DicomMetadataStore.EVENTS.INSTANCES_ADDED,
|
||||
({ StudyInstanceUID, SeriesInstanceUID, madeInClient = false }) => {
|
||||
const seriesMetadata = DicomMetadataStore.getSeries(
|
||||
StudyInstanceUID,
|
||||
SeriesInstanceUID
|
||||
);
|
||||
|
||||
DisplaySetService.makeDisplaySets(seriesMetadata.instances, madeInClient);
|
||||
}
|
||||
);
|
||||
|
||||
unsubscriptions.push(instanceAddedUnsubscribe);
|
||||
|
||||
studyInstanceUIDs.forEach(StudyInstanceUID => {
|
||||
dataSource.retrieveSeriesMetadata({ StudyInstanceUID });
|
||||
});
|
||||
|
||||
const { unsubscribe: seriesAddedUnsubscribe } = DicomMetadataStore.subscribe(
|
||||
DicomMetadataStore.EVENTS.SERIES_ADDED,
|
||||
({ StudyInstanceUID }) => {
|
||||
const studyMetadata = DicomMetadataStore.getStudy(StudyInstanceUID);
|
||||
HangingProtocolService.run(studyMetadata);
|
||||
}
|
||||
);
|
||||
unsubscriptions.push(seriesAddedUnsubscribe);
|
||||
|
||||
return unsubscriptions;
|
||||
}
|
||||
|
||||
export default function ModeRoute({
|
||||
location,
|
||||
@@ -20,15 +63,25 @@ export default function ModeRoute({
|
||||
}) {
|
||||
// Parse route params/querystring
|
||||
const query = useQuery();
|
||||
const queryStudyInstanceUIDs = query.get('StudyInstanceUIDs');
|
||||
const { StudyInstanceUIDs: paramsStudyInstanceUIDs } = useParams();
|
||||
const StudyInstanceUIDs = queryStudyInstanceUIDs || paramsStudyInstanceUIDs;
|
||||
const StudyInstanceUIDsAsArray =
|
||||
StudyInstanceUIDs && Array.isArray(StudyInstanceUIDs)
|
||||
? StudyInstanceUIDs
|
||||
: [StudyInstanceUIDs];
|
||||
const params = useParams();
|
||||
|
||||
const { extensions, sopClassHandlers, hotkeys } = mode;
|
||||
const [studyInstanceUIDs, setStudyInstanceUIDs] = useState();
|
||||
|
||||
const [refresh, setRefresh] = useState(false);
|
||||
const layoutTemplateData = useRef(false);
|
||||
const locationRef = useRef(null);
|
||||
const isMounted = useRef(false);
|
||||
|
||||
if (location !== locationRef.current) {
|
||||
layoutTemplateData.current = null;
|
||||
locationRef.current = location;
|
||||
}
|
||||
|
||||
const {
|
||||
DisplaySetService,
|
||||
HangingProtocolService,
|
||||
} = servicesManager.services;
|
||||
const { extensions, sopClassHandlers, hotkeys, hangingProtocols } = mode;
|
||||
|
||||
if (dataSourceName === undefined) {
|
||||
dataSourceName = extensionManager.defaultDataSourceName;
|
||||
@@ -43,19 +96,6 @@ export default function ModeRoute({
|
||||
// Only handling one route per mode for now
|
||||
const route = mode.routes[0];
|
||||
|
||||
const {
|
||||
DisplaySetService,
|
||||
MeasurementService,
|
||||
ViewportGridService,
|
||||
HangingProtocolService,
|
||||
} = servicesManager.services;
|
||||
|
||||
const layoutTemplateData = route.layoutTemplate({ location });
|
||||
const layoutTemplateModuleEntry = extensionManager.getModuleEntry(
|
||||
layoutTemplateData.id
|
||||
);
|
||||
const LayoutComponent = layoutTemplateModuleEntry.component;
|
||||
|
||||
// For each extension, look up their context modules
|
||||
// TODO: move to extension manager.
|
||||
let contextModules = [];
|
||||
@@ -81,9 +121,52 @@ export default function ModeRoute({
|
||||
return ViewportGrid({ ...props, dataSource });
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
// Preventing state update for unmounted component
|
||||
isMounted.current = true;
|
||||
return () => {
|
||||
isMounted.current = false;
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
// Todo: this should not be here, data source should not care about params
|
||||
const initializeDataSource = async (params, query) => {
|
||||
const studyInstanceUIDs = await dataSource.parseRouteParams({
|
||||
params,
|
||||
query,
|
||||
});
|
||||
setStudyInstanceUIDs(studyInstanceUIDs);
|
||||
};
|
||||
|
||||
initializeDataSource(params, query);
|
||||
return () => {
|
||||
layoutTemplateData.current = null;
|
||||
};
|
||||
}, [location]);
|
||||
|
||||
useEffect(() => {
|
||||
const retrieveLayoutData = async () => {
|
||||
const layoutData = await route.layoutTemplate({
|
||||
location,
|
||||
servicesManager,
|
||||
studyInstanceUIDs,
|
||||
});
|
||||
if (isMounted.current) {
|
||||
layoutTemplateData.current = layoutData;
|
||||
setRefresh(!refresh);
|
||||
}
|
||||
};
|
||||
if (studyInstanceUIDs?.length && studyInstanceUIDs[0] !== undefined) {
|
||||
retrieveLayoutData();
|
||||
}
|
||||
return () => {
|
||||
layoutTemplateData.current = null;
|
||||
};
|
||||
}, [studyInstanceUIDs]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!hotkeys) {
|
||||
console.warn('[hotkeys] No bindings defined for hotkeys hook!');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -96,19 +179,61 @@ export default function ModeRoute({
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!layoutTemplateData.current) {
|
||||
return;
|
||||
}
|
||||
// TODO: For some reason this is running before the Providers
|
||||
// are calling setServiceImplementation
|
||||
// are calling setServiceImplementationf
|
||||
// TOOD -> iterate through services.
|
||||
|
||||
// Extension
|
||||
|
||||
// Add SOPClassHandlers to a new SOPClassManager.
|
||||
DisplaySetService.init(extensionManager, sopClassHandlers);
|
||||
|
||||
extensionManager.onModeEnter();
|
||||
mode?.onModeEnter({ servicesManager, extensionManager });
|
||||
// Mode
|
||||
route.init({ servicesManager, extensionManager });
|
||||
|
||||
// Adding hanging protocols of extensions after onModeEnter since
|
||||
// it will reset the protocols
|
||||
hangingProtocols.forEach(extentionProtocols => {
|
||||
const hangingProtocolModule = extensionManager.getModuleEntry(extentionProtocols);
|
||||
if (hangingProtocolModule?.protocols) {
|
||||
HangingProtocolService.addProtocols(hangingProtocolModule.protocols);
|
||||
}
|
||||
});
|
||||
|
||||
const setupRouteInit = async () => {
|
||||
if (route.init) {
|
||||
return await route.init({
|
||||
servicesManager,
|
||||
extensionManager,
|
||||
hotkeysManager,
|
||||
studyInstanceUIDs,
|
||||
dataSource,
|
||||
});
|
||||
}
|
||||
|
||||
return await defaultRouteInit({
|
||||
servicesManager,
|
||||
extensionManager,
|
||||
hotkeysManager,
|
||||
studyInstanceUIDs,
|
||||
dataSource,
|
||||
});
|
||||
};
|
||||
|
||||
let unsubscriptions;
|
||||
setupRouteInit().then(unsubs => {
|
||||
unsubscriptions = unsubs;
|
||||
});
|
||||
|
||||
return () => {
|
||||
extensionManager.onModeExit();
|
||||
mode?.onModeExit({ servicesManager, extensionManager });
|
||||
unsubscriptions.forEach(unsub => {
|
||||
unsub();
|
||||
});
|
||||
};
|
||||
}, [
|
||||
mode,
|
||||
@@ -118,140 +243,35 @@ export default function ModeRoute({
|
||||
servicesManager,
|
||||
extensionManager,
|
||||
hotkeysManager,
|
||||
studyInstanceUIDs,
|
||||
refresh,
|
||||
hangingProtocols,
|
||||
]);
|
||||
|
||||
// This queries for series, but... What does it do with them?
|
||||
useEffect(() => {
|
||||
// Add SOPClassHandlers to a new SOPClassManager.
|
||||
DisplaySetService.init(extensionManager, sopClassHandlers);
|
||||
|
||||
// TODO: This should be baked into core, not manuel?
|
||||
// DisplaySetService would wire this up?
|
||||
const { unsubscribe } = DicomMetadataStore.subscribe(
|
||||
DicomMetadataStore.EVENTS.INSTANCES_ADDED,
|
||||
({ StudyInstanceUID, SeriesInstanceUID, madeInClient = false }) => {
|
||||
const seriesMetadata = DicomMetadataStore.getSeries(
|
||||
StudyInstanceUID,
|
||||
SeriesInstanceUID
|
||||
);
|
||||
|
||||
DisplaySetService.makeDisplaySets(
|
||||
seriesMetadata.instances,
|
||||
madeInClient
|
||||
);
|
||||
}
|
||||
const renderLayoutData = props => {
|
||||
const layoutTemplateModuleEntry = extensionManager.getModuleEntry(
|
||||
layoutTemplateData.current.id
|
||||
);
|
||||
const LayoutComponent = layoutTemplateModuleEntry.component;
|
||||
|
||||
StudyInstanceUIDsAsArray.forEach(StudyInstanceUID => {
|
||||
dataSource.retrieveSeriesMetadata({ StudyInstanceUID });
|
||||
});
|
||||
|
||||
return unsubscribe;
|
||||
}, [
|
||||
mode,
|
||||
dataSourceName,
|
||||
location,
|
||||
DisplaySetService,
|
||||
extensionManager,
|
||||
sopClassHandlers,
|
||||
StudyInstanceUIDsAsArray,
|
||||
dataSource,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
const { unsubscribe } = DicomMetadataStore.subscribe(
|
||||
DicomMetadataStore.EVENTS.SERIES_ADDED,
|
||||
({ StudyInstanceUID }) => {
|
||||
const studyMetadata = DicomMetadataStore.getStudy(StudyInstanceUID);
|
||||
|
||||
const sortedSeries = studyMetadata.series.sort((a, b) => {
|
||||
const aLowPriority = isLowPriorityModality(a.Modality);
|
||||
const bLowPriority = isLowPriorityModality(b.Modality);
|
||||
if (!aLowPriority && bLowPriority) {
|
||||
return -1;
|
||||
}
|
||||
if (aLowPriority && !bLowPriority) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return a.SeriesNumber - b.SeriesNumber;
|
||||
});
|
||||
|
||||
const { SeriesInstanceUID } = sortedSeries[0];
|
||||
|
||||
HangingProtocolService.setHangingProtocol({
|
||||
/*protocolMatchingRules: [
|
||||
{
|
||||
id: '7tmuq7KzDMCWFeapc',
|
||||
weight: 2,
|
||||
required: false,
|
||||
attribute: 'x00081030',
|
||||
constraint: {
|
||||
contains: {
|
||||
value: 'DFCI CT CHEST',
|
||||
},
|
||||
},
|
||||
},
|
||||
],*/
|
||||
stages: [
|
||||
{
|
||||
/*id: 'v5PfGt9F6mffZPif5',
|
||||
viewportStructure: {
|
||||
type: 'grid',
|
||||
properties: {
|
||||
Rows: 1,
|
||||
Columns: 1,
|
||||
},
|
||||
layoutTemplateName: 'gridLayout',
|
||||
},*/
|
||||
viewports: [
|
||||
{
|
||||
viewportSettings: {},
|
||||
imageMatchingRules: [],
|
||||
seriesMatchingRules: [
|
||||
{
|
||||
id: 'mXnsCcNzZL56z7mTZ',
|
||||
weight: 1,
|
||||
required: true,
|
||||
attribute: 'SeriesInstanceUID',
|
||||
constraint: {
|
||||
equals: {
|
||||
value: SeriesInstanceUID,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
studyMatchingRules: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
);
|
||||
return unsubscribe;
|
||||
}, [
|
||||
mode,
|
||||
dataSourceName,
|
||||
location,
|
||||
DisplaySetService,
|
||||
extensionManager,
|
||||
sopClassHandlers,
|
||||
StudyInstanceUIDsAsArray,
|
||||
dataSource,
|
||||
]);
|
||||
return <LayoutComponent {...props} />;
|
||||
};
|
||||
|
||||
return (
|
||||
<ImageViewerProvider
|
||||
initialState={{ StudyInstanceUIDs: StudyInstanceUIDsAsArray }}
|
||||
// initialState={{ StudyInstanceUIDs: StudyInstanceUIDs }}
|
||||
StudyInstanceUIDs={studyInstanceUIDs}
|
||||
// reducer={reducer}
|
||||
>
|
||||
<CombinedContextProvider>
|
||||
<DragAndDropProvider>
|
||||
<LayoutComponent
|
||||
{...layoutTemplateData.props}
|
||||
StudyInstanceUIDs={StudyInstanceUIDs}
|
||||
ViewportGridComp={ViewportGridWithDataSource}
|
||||
/>
|
||||
{layoutTemplateData.current &&
|
||||
studyInstanceUIDs?.length &&
|
||||
studyInstanceUIDs[0] !== undefined &&
|
||||
renderLayoutData({
|
||||
...layoutTemplateData.current.props,
|
||||
ViewportGridComp: ViewportGridWithDataSource,
|
||||
})}
|
||||
</DragAndDropProvider>
|
||||
</CombinedContextProvider>
|
||||
</ImageViewerProvider>
|
||||
|
||||
@@ -408,7 +408,7 @@ function WorkList({
|
||||
'h-screen': !hasStudies,
|
||||
})}
|
||||
>
|
||||
<Header isSticky menuOptions={menuOptions} isReturnEnabled={false} />
|
||||
<Header isSticky menuOptions={menuOptions} isReturnEnabled={false} WhiteLabeling={appConfig.whiteLabeling} />
|
||||
<StudyListFilter
|
||||
numOfStudies={pageNumber * resultsPerPage > 100 ? 101 : numOfStudies}
|
||||
filtersMeta={filtersMeta}
|
||||
|
||||
Reference in new issue
Block a user