chore(prettier): full repo linting and code reformatting once and for all (#3627)

This commit is contained in:
Alireza authored and GitHub committed 2023-09-01 16:19:39 -04:00
1 parent 01500a2d4c
commit 9045ddaedc
656 files changed
+21737 -20628

No files matched your search

@@ -7,7 +7,6 @@ const ENTRY = {
app: `${SRC_DIR}/index.tsx`,
};
module.exports = (env, argv) => {
return webpackCommon(env, argv, { SRC_DIR, DIST_DIR, ENTRY });
};
+1 -7
View File
@@ -40,13 +40,7 @@ module.exports = (env, argv) => {
libraryTarget: 'umd',
filename: pkg.main,
},
externals: [
/\b(vtk.js)/,
/\b(dcmjs)/,
/\b(gl-matrix)/,
/^@ohif/,
/^@cornerstonejs/,
],
externals: [/\b(vtk.js)/, /\b(dcmjs)/, /\b(gl-matrix)/, /^@ohif/, /^@cornerstonejs/],
plugins: [
new webpack.optimize.LimitChunkCountPlugin({
maxChunks: 1,
@@ -16,11 +16,7 @@ async function createReportAsync(
measurements,
options
) {
const {
displaySetService,
uiNotificationService,
uiDialogService,
} = servicesManager.services;
const { displaySetService, uiNotificationService, uiDialogService } = servicesManager.services;
const loadingDialogId = uiDialogService.create({
showOverlay: true,
isDraggable: false,
@@ -18,13 +18,10 @@ function DataSourceConfigurationComponent({
const { customizationService } = servicesManager.services;
const [configurationAPI, setConfigurationAPI] = useState<
Types.BaseDataSourceConfigurationAPI
>();
const [configurationAPI, setConfigurationAPI] = useState<Types.BaseDataSourceConfigurationAPI>();
const [configuredItems, setConfiguredItems] = useState<
Array<Types.BaseDataSourceConfigurationAPIItem>
>();
const [configuredItems, setConfiguredItems] =
useState<Array<Types.BaseDataSourceConfigurationAPIItem>>();
useEffect(() => {
let shouldUpdate = true;
@@ -37,9 +34,7 @@ function DataSourceConfigurationComponent({
}
const { factory: configurationAPIFactory } =
customizationService.get(
activeDataSourceDef.configuration.configurationAPI
) ?? {};
customizationService.get(activeDataSourceDef.configuration.configurationAPI) ?? {};
if (!configurationAPIFactory) {
return;
@@ -95,24 +90,25 @@ function DataSourceConfigurationComponent({
}, [configurationAPI, configuredItems, showConfigurationModal]);
return configuredItems ? (
<div className="flex text-aqua-pale overflow-hidden items-center">
<div className="text-aqua-pale flex items-center overflow-hidden">
<Icon
name="settings"
className="cursor-pointer shrink-0 w-3.5 h-3.5 mr-2.5"
className="mr-2.5 h-3.5 w-3.5 shrink-0 cursor-pointer"
onClick={showConfigurationModal}
></Icon>
{configuredItems.map((item, itemIndex) => {
return (
<div key={itemIndex} className="flex overflow-hidden">
<div
key={itemIndex}
className="flex overflow-hidden"
>
<div
key={itemIndex}
className="text-ellipsis whitespace-nowrap overflow-hidden"
className="overflow-hidden text-ellipsis whitespace-nowrap"
>
{item.name}
</div>
{itemIndex !== configuredItems.length - 1 && (
<div className="px-2.5">|</div>
)}
{itemIndex !== configuredItems.length - 1 && <div className="px-2.5">|</div>}
</div>
);
})}
@@ -5,8 +5,7 @@ import { Icon } from '@ohif/ui';
import { Types } from '@ohif/core';
import ItemListComponent from './ItemListComponent';
const NO_WRAP_ELLIPSIS_CLASS_NAMES =
'text-ellipsis whitespace-nowrap overflow-hidden';
const NO_WRAP_ELLIPSIS_CLASS_NAMES = 'text-ellipsis whitespace-nowrap overflow-hidden';
type DataSourceConfigurationModalComponentProps = {
configurationAPI: Types.BaseDataSourceConfigurationAPI;
@@ -21,9 +20,7 @@ function DataSourceConfigurationModalComponent({
}: DataSourceConfigurationModalComponentProps) {
const { t } = useTranslation('DataSourceConfiguration');
const [itemList, setItemList] = useState<
Array<Types.BaseDataSourceConfigurationAPIItem>
>();
const [itemList, setItemList] = useState<Array<Types.BaseDataSourceConfigurationAPIItem>>();
const [selectedItems, setSelectedItems] = useState(configuredItems);
@@ -104,9 +101,7 @@ function DataSourceConfigurationModalComponent({
itemIndex < selectedItems.length
? classNames(
'bg-black/[.4]',
itemIndex !== itemLabels.length - 1
? 'hover:bg-transparent active:bg-secondary-dark'
: ''
itemIndex !== itemLabels.length - 1 ? 'hover:bg-transparent active:bg-secondary-dark' : ''
)
: 'bg-transparent';
@@ -118,17 +113,15 @@ function DataSourceConfigurationModalComponent({
: 'border border-dashed border-secondary-light';
const getSelectedItemTextClasses = itemIndex =>
itemIndex <= selectedItems.length
? 'text-primary-light'
: 'text-primary-active';
itemIndex <= selectedItems.length ? 'text-primary-light' : 'text-primary-active';
const getErrorComponent = (): ReactElement => {
return (
<div className="flex flex-col gap-4 min-h-[1px] grow">
<div className="flex min-h-[1px] grow flex-col gap-4">
<div className="text-primary-light text-[20px]">
{t(`Error fetching ${itemLabels[selectedItems.length]} list`)}
</div>
<div className="bg-black text-[14px] grow p-4">{errorMessage}</div>
<div className="grow bg-black p-4 text-[14px]">{errorMessage}</div>
</div>
);
};
@@ -141,7 +134,7 @@ function DataSourceConfigurationModalComponent({
<div
key={itemLabel}
className={classNames(
'rounded-md p-3.5 flex flex-col gap-1 shrink min-w-[1px] basis-[200px]',
'flex min-w-[1px] shrink basis-[200px] flex-col gap-1 rounded-md p-3.5',
getSelectedItemCursorClasses(itemLabelIndex),
getSelectedItemBackgroundClasses(itemLabelIndex),
getSelectedItemBorderClasses(itemLabelIndex),
@@ -152,30 +145,21 @@ function DataSourceConfigurationModalComponent({
itemLabelIndex <= currentSelectedItemIndex
? () => {
setShowFullConfig(false);
setSelectedItems(theList =>
theList.slice(0, itemLabelIndex)
);
setSelectedItems(theList => theList.slice(0, itemLabelIndex));
}
: undefined
}
>
<div className="flex gap-2 items-center text-">
<div className="text- flex items-center gap-2">
{itemLabelIndex < selectedItems.length ? (
<Icon name="status-tracked" />
) : (
<Icon name="status-untracked" />
)}
<div className={classNames(NO_WRAP_ELLIPSIS_CLASS_NAMES)}>
{t(itemLabel)}
</div>
<div className={classNames(NO_WRAP_ELLIPSIS_CLASS_NAMES)}>{t(itemLabel)}</div>
</div>
{itemLabelIndex < selectedItems.length ? (
<div
className={classNames(
'text-white text-[14px]',
NO_WRAP_ELLIPSIS_CLASS_NAMES
)}
>
<div className={classNames('text-[14px] text-white', NO_WRAP_ELLIPSIS_CLASS_NAMES)}>
{selectedItems[itemLabelIndex].name}
</div>
) : (
@@ -189,9 +173,9 @@ function DataSourceConfigurationModalComponent({
};
return (
<div className="h-[calc(100vh-300px)] flex flex-col pt-0.5 gap-4 select-none">
<div className="flex h-[calc(100vh-300px)] select-none flex-col gap-4 pt-0.5">
{getSelectedItemsComponent()}
<div className="w-full h-0.5 shrink-0 bg-black"></div>
<div className="h-0.5 w-full shrink-0 bg-black"></div>
{errorMessage ? (
getErrorComponent()
) : (
@@ -200,10 +184,7 @@ function DataSourceConfigurationModalComponent({
itemList={itemList}
onItemClicked={item => {
setShowFullConfig(false);
setSelectedItems(theList => [
...theList.slice(0, currentSelectedItemIndex + 1),
item,
]);
setSelectedItems(theList => [...theList.slice(0, currentSelectedItemIndex + 1), item]);
}}
></ItemListComponent>
)}
@@ -1,12 +1,7 @@
import classNames from 'classnames';
import React, { ReactElement, useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import {
Button,
Icon,
InputFilterText,
LoadingIndicatorProgress,
} from '@ohif/ui';
import { Button, Icon, InputFilterText, LoadingIndicatorProgress } from '@ohif/ui';
import { Types } from '@ohif/core';
type ItemListComponentProps = {
@@ -28,37 +23,35 @@ function ItemListComponent({
}, [itemList]);
return (
<div className="flex flex-col gap-4 min-h-[1px] grow">
<div className="flex justify-between items-center">
<div className="text-primary-light text-[20px]">
{t(`Select ${itemLabel}`)}
</div>
<div className="flex min-h-[1px] grow flex-col gap-4">
<div className="flex items-center justify-between">
<div className="text-primary-light text-[20px]">{t(`Select ${itemLabel}`)}</div>
<InputFilterText
className="grow max-w-[40%]"
className="max-w-[40%] grow"
value={filterValue}
onDebounceChange={setFilterValue}
placeholder={t(`Search ${itemLabel} list`)}
></InputFilterText>
</div>
<div className="flex flex-col relative min-h-[1px] grow text-[14px] bg-black">
<div className="relative flex min-h-[1px] grow flex-col bg-black text-[14px]">
{itemList == null ? (
<LoadingIndicatorProgress className={'w-full h-full'} />
<LoadingIndicatorProgress className={'h-full w-full'} />
) : itemList.length === 0 ? (
<div className="flex flex-col h-full px-6 py-4 items-center justify-center text-primary-light">
<Icon name="magnifier" className="mb-4" />
<div className="text-primary-light flex h-full flex-col items-center justify-center px-6 py-4">
<Icon
name="magnifier"
className="mb-4"
/>
<span>{t(`No ${itemLabel} available`)}</span>
</div>
) : (
<>
<div className="bg-secondary-dark text-white px-3 py-1.5">
{t(itemLabel)}
</div>
<div className="overflow-auto ohif-scrollbar">
<div className="bg-secondary-dark px-3 py-1.5 text-white">{t(itemLabel)}</div>
<div className="ohif-scrollbar overflow-auto">
{itemList
.filter(
item =>
!filterValue ||
item.name.toLowerCase().includes(filterValue.toLowerCase())
!filterValue || item.name.toLowerCase().includes(filterValue.toLowerCase())
)
.map(item => {
const border =
@@ -66,7 +59,7 @@ function ItemListComponent({
return (
<div
className={classNames(
'group mx-2 px-6 py-2 flex justify-between items-center hover:text-primary-light hover:bg-primary-dark',
'hover:text-primary-light hover:bg-primary-dark group mx-2 flex items-center justify-between px-6 py-2',
border
)}
key={item.id}
@@ -19,10 +19,7 @@ export default class ContextMenuController {
services: Types.Services;
menuItems: Menu[] | MenuItem[];
constructor(
servicesManager: ServicesManager,
commandsManager: CommandsManager
) {
constructor(servicesManager: ServicesManager, commandsManager: CommandsManager) {
this.services = servicesManager.services as Obj;
this.commandsManager = commandsManager;
}
@@ -74,8 +71,7 @@ export default class ContextMenuController {
// This naming is part of hte uiDialogService convention
// Clicking outside simpy closes the dialog box.
onClickOutside: () =>
this.services.uiDialogService.dismiss({ id: 'context-menu' }),
onClickOutside: () => this.services.uiDialogService.dismiss({ id: 'context-menu' }),
contentProps: {
items,
@@ -170,9 +166,7 @@ export default class ContextMenuController {
};
static _isValidPosition = (source): boolean => {
return (
source && typeof source.x === 'number' && typeof source.y === 'number'
);
return source && typeof source.x === 'number' && typeof source.y === 'number';
};
/**
@@ -180,10 +174,7 @@ export default class ContextMenuController {
*/
static _getDefaultPosition = (canvasPoints, eventDetail, viewerElement) => {
function* getPositionIterator() {
yield ContextMenuController._getCanvasPointsPosition(
canvasPoints,
viewerElement
);
yield ContextMenuController._getCanvasPointsPosition(canvasPoints, viewerElement);
yield ContextMenuController._getEventDefaultPosition(eventDetail);
yield ContextMenuController._getElementDefaultPosition(viewerElement);
yield ContextMenuController.getDefaultPosition();
@@ -1,4 +1,4 @@
import ContextMenuItemsBuilder from "./ContextMenuItemsBuilder";
import ContextMenuItemsBuilder from './ContextMenuItemsBuilder';
const menus = [
{
@@ -31,16 +31,11 @@ export function findMenuById(menus: Menu[], menuId?: string): Menu {
* @param {*} subProps
* @returns
*/
export function findMenuDefault(
menus: Menu[],
subProps: Record<string, unknown>
): Menu {
export function findMenuDefault(menus: Menu[], subProps: Record<string, unknown>): Menu {
if (!menus) {
return null;
}
return menus.find(
menu => !menu.selector || menu.selector(subProps.selectorProps)
);
return menus.find(menu => !menu.selector || menu.selector(subProps.selectorProps));
}
/**
@@ -53,11 +48,7 @@ export function findMenuDefault(
* @param menuIdFilter - menu id identifier (to be considered on selection)
* This is intended to support other types of filtering in the future.
*/
export function findMenu(
menus: Menu[],
props?: Types.IProps,
menuIdFilter?: string
) {
export function findMenu(menus: Menu[], props?: Types.IProps, menuIdFilter?: string) {
const { subMenu } = props;
function* findMenuIterator() {
@@ -137,10 +128,7 @@ export function getMenuItems(
if (!selector || selector(selectorProps)) {
if (delegating) {
menuItems = [
...menuItems,
...getMenuItems(selectorProps, event, menus, subMenu),
];
menuItems = [...menuItems, ...getMenuItems(selectorProps, event, menus, subMenu)];
} else {
const toAdd = adaptItem(item, subProps);
menuItems.push(toAdd);
@@ -161,10 +149,7 @@ export function getMenuItems(
* @returns a MenuItem that is compatible with the base ContextMenu
* This requires having a label and set of actions to be called.
*/
export function adaptItem(
item: MenuItem,
subProps: ContextMenuProps
): ContextMenuItem {
export function adaptItem(item: MenuItem, subProps: ContextMenuProps): ContextMenuItem {
const newItem: ContextMenuItem = {
...item,
value: subProps.selectorProps?.value,
@@ -29,15 +29,15 @@ const initialUrl = 'https://cloudresourcemanager.googleapis.com/v1';
const baseHealthcareUrl = 'https://healthcare.googleapis.com/v1';
class GoogleCloudDataSourceConfigurationAPIItem
implements Types.BaseDataSourceConfigurationAPIItem {
implements Types.BaseDataSourceConfigurationAPIItem
{
id: string;
name: string;
url: string;
itemType: ItemType;
}
class GoogleCloudDataSourceConfigurationAPI
implements Types.BaseDataSourceConfigurationAPI {
class GoogleCloudDataSourceConfigurationAPI implements Types.BaseDataSourceConfigurationAPI {
private _extensionManager: ExtensionManager;
private _fetchOptions: { method: string; headers: unknown };
private _dataSourceName: string;
@@ -45,8 +45,7 @@ class GoogleCloudDataSourceConfigurationAPI
constructor(dataSourceName, servicesManager, extensionManager) {
this._dataSourceName = dataSourceName;
this._extensionManager = extensionManager;
const userAuthenticationService =
servicesManager.services.userAuthenticationService;
const userAuthenticationService = servicesManager.services.userAuthenticationService;
this._fetchOptions = {
method: 'GET',
headers: userAuthenticationService.getAuthorizationHeader(),
@@ -89,9 +88,7 @@ class GoogleCloudDataSourceConfigurationAPI
// Last configurable item, so update the data source configuration.
const url = `${googleCloudItem.url}/dicomWeb`;
const dataSourceDefCopy = JSON.parse(
JSON.stringify(
this._extensionManager.getDataSourceDefinition(this._dataSourceName)
)
JSON.stringify(this._extensionManager.getDataSourceDefinition(this._dataSourceName))
);
dataSourceDefCopy.configuration = {
...dataSourceDefCopy.configuration,
@@ -136,9 +133,7 @@ class GoogleCloudDataSourceConfigurationAPI
return subItems;
}
async getConfiguredItems(): Promise<
Array<GoogleCloudDataSourceConfigurationAPIItem>
> {
async getConfiguredItems(): Promise<Array<GoogleCloudDataSourceConfigurationAPIItem>> {
const dataSourceDefinition = this._extensionManager.getDataSourceDefinition(
this._dataSourceName
);
@@ -216,9 +211,7 @@ class GoogleCloudDataSourceConfigurationAPI
fetchOptions,
fetchSearchParams
);
data[ItemType[fetchItemType]] = data[ItemType[fetchItemType]].concat(
subPageData
);
data[ItemType[fetchItemType]] = data[ItemType[fetchItemType]].concat(subPageData);
}
if (data[ItemType[fetchItemType]]) {
return data[ItemType[fetchItemType]];
@@ -152,15 +152,9 @@ function createDicomJSONApi(dicomJsonConfig) {
return getDirectURL(wadoRoot, params);
},
series: {
metadata: ({
StudyInstanceUID,
madeInClient = false,
customSort,
} = {}) => {
metadata: ({ StudyInstanceUID, madeInClient = false, customSort } = {}) => {
if (!StudyInstanceUID) {
throw new Error(
'Unable to query for SeriesMetadata without StudyInstanceUID'
);
throw new Error('Unable to query for SeriesMetadata without StudyInstanceUID');
}
const study = findStudies('StudyInstanceUID', StudyInstanceUID)[0];
@@ -186,16 +180,10 @@ function createDicomJSONApi(dicomJsonConfig) {
DicomMetadataStore.addInstances(naturalizedInstances, madeInClient);
}
DicomMetadataStore.addSeriesMetadata(
seriesSummaryMetadata,
madeInClient
);
DicomMetadataStore.addSeriesMetadata(seriesSummaryMetadata, madeInClient);
function setSuccessFlag() {
const study = DicomMetadataStore.getStudy(
StudyInstanceUID,
madeInClient
);
const study = DicomMetadataStore.getStudy(StudyInstanceUID, madeInClient);
study.isLoaded = true;
}
@@ -127,16 +127,11 @@ function createDicomLocalApi(dicomLocalConfig) {
series: {
metadata: async ({ StudyInstanceUID, madeInClient = false } = {}) => {
if (!StudyInstanceUID) {
throw new Error(
'Unable to query for SeriesMetadata without StudyInstanceUID'
);
throw new Error('Unable to query for SeriesMetadata without StudyInstanceUID');
}
// Instances metadata already added via local upload
const study = DicomMetadataStore.getStudy(
StudyInstanceUID,
madeInClient
);
const study = DicomMetadataStore.getStudy(StudyInstanceUID, madeInClient);
// Series metadata already added via local upload
DicomMetadataStore._broadcastEvent(EVENTS.SERIES_ADDED, {
@@ -236,8 +231,7 @@ function createDicomLocalApi(dicomLocalConfig) {
const { StudyInstanceUIDs: paramsStudyInstanceUIDs } = params;
const queryStudyInstanceUIDs = query.getAll('StudyInstanceUIDs');
const StudyInstanceUIDs =
queryStudyInstanceUIDs || paramsStudyInstanceUIDs;
const StudyInstanceUIDs = queryStudyInstanceUIDs || paramsStudyInstanceUIDs;
const StudyInstanceUIDsAsArray =
StudyInstanceUIDs && Array.isArray(StudyInstanceUIDs)
? StudyInstanceUIDs
@@ -4,7 +4,7 @@
}
.dicom-tag-browser-table-wrapper {
/* height: 500px;*/
/* height: 500px;*/
/*overflow-y: scroll;*/
overflow-x: scroll;
}
@@ -45,7 +45,7 @@
padding-left: 10px;
padding-right: 10px;
text-align: center;
color: "#20A5D6";
color: '#20A5D6';
}
.dicom-tag-browser-table th.dicom-tag-browser-table-left {
@@ -21,10 +21,8 @@ const DicomTagBrowser = ({ displaySets, displaySetInstanceUID }) => {
// 3: Value
const excludedColumnIndicesForFilter: Set<number> = new Set([1]);
const [
selectedDisplaySetInstanceUID,
setSelectedDisplaySetInstanceUID,
] = useState(displaySetInstanceUID);
const [selectedDisplaySetInstanceUID, setSelectedDisplaySetInstanceUID] =
useState(displaySetInstanceUID);
const [instanceNumber, setInstanceNumber] = useState(1);
const [filterValue, setFilterValue] = useState('');
@@ -110,27 +108,31 @@ const DicomTagBrowser = ({ displaySets, displaySetInstanceUID }) => {
return (
<div className="dicom-tag-browser-content">
<div className="flex flex-row mb-6 items-center pl-1">
<div className="flex flex-row items-center w-1/2">
<Typography variant="subtitle" className="mr-4">
<div className="mb-6 flex flex-row items-center pl-1">
<div className="flex w-1/2 flex-row items-center">
<Typography
variant="subtitle"
className="mr-4"
>
Series
</Typography>
<div className="grow mr-8">
<div className="mr-8 grow">
<Select
id="display-set-selector"
isClearable={false}
onChange={onSelectChange}
options={displaySetList}
value={displaySetList.find(
ds => ds.value === selectedDisplaySetInstanceUID
)}
value={displaySetList.find(ds => ds.value === selectedDisplaySetInstanceUID)}
className="text-white"
/>
</div>
</div>
<div className="flex flex-row items-center w-1/2">
<div className="flex w-1/2 flex-row items-center">
{showInstanceList && (
<Typography variant="subtitle" className="mr-4">
<Typography
variant="subtitle"
className="mr-4"
>
Instance Number
</Typography>
)}
@@ -153,10 +155,10 @@ const DicomTagBrowser = ({ displaySets, displaySetInstanceUID }) => {
)}
</div>
</div>
<div className="w-full h-1 bg-black"></div>
<div className="flex flex-row my-3 w-1/2">
<div className="h-1 w-full bg-black"></div>
<div className="my-3 flex w-1/2 flex-row">
<InputFilterText
className="block w-full mr-8"
className="mr-8 block w-full"
placeholder="Search metadata..."
onDebounceChange={setFilterValue}
></InputFilterText>
@@ -171,24 +173,14 @@ function getFormattedRowsFromTags(tags, metadata) {
tags.forEach(tagInfo => {
if (tagInfo.vr === 'SQ') {
rows.push([
`${tagInfo.tagIndent}${tagInfo.tag}`,
tagInfo.vr,
tagInfo.keyword,
'',
]);
rows.push([`${tagInfo.tagIndent}${tagInfo.tag}`, tagInfo.vr, tagInfo.keyword, '']);
const { values } = tagInfo;
values.forEach((item, index) => {
const formatedRowsFromTags = getFormattedRowsFromTags(item, metadata);
rows.push([
`${item[0].tagIndent}(FFFE,E000)`,
'',
`Item #${index}`,
'',
]);
rows.push([`${item[0].tagIndent}(FFFE,E000)`, '', `Item #${index}`, '']);
rows.push(...formatedRowsFromTags);
});
@@ -199,17 +191,10 @@ function getFormattedRowsFromTags(tags, metadata) {
const originalTagInfo = metadata[tag];
tagInfo.vr = originalTagInfo.vr;
} catch (error) {
console.error(
`Failed to parse value representation for tag '${tagInfo.keyword}'`
);
console.error(`Failed to parse value representation for tag '${tagInfo.keyword}'`);
}
}
rows.push([
`${tagInfo.tagIndent}${tagInfo.tag}`,
tagInfo.vr,
tagInfo.keyword,
tagInfo.value,
]);
rows.push([`${tagInfo.tagIndent}${tagInfo.tag}`, tagInfo.vr, tagInfo.keyword, tagInfo.value]);
}
});
@@ -17,48 +17,40 @@ function ColumnHeaders({ tagRef, vrRef, keywordRef, valueRef }) {
return (
<div
className={classNames(
'flex flex-row w-full bg-secondary-dark ohif-scrollbar overflow-y-scroll'
'bg-secondary-dark ohif-scrollbar flex w-full flex-row overflow-y-scroll'
)}
style={rowVerticalPaddingStyle}
>
<div className="px-3 w-4/24">
<div className="w-4/24 px-3">
<label
ref={tagRef}
className="flex flex-col flex-1 text-white text-lg pl-1 select-none"
className="flex flex-1 select-none flex-col pl-1 text-lg text-white"
>
<span className="flex flex-row items-center focus:outline-none">
Tag
</span>
<span className="flex flex-row items-center focus:outline-none">Tag</span>
</label>
</div>
<div className="px-3 w-2/24">
<div className="w-2/24 px-3">
<label
ref={vrRef}
className="flex flex-col flex-1 text-white text-lg pl-1 select-none"
className="flex flex-1 select-none flex-col pl-1 text-lg text-white"
>
<span className="flex flex-row items-center focus:outline-none">
VR
</span>
<span className="flex flex-row items-center focus:outline-none">VR</span>
</label>
</div>
<div className="px-3 w-6/24">
<div className="w-6/24 px-3">
<label
ref={keywordRef}
className="flex flex-col flex-1 text-white text-lg pl-1 select-none"
className="flex flex-1 select-none flex-col pl-1 text-lg text-white"
>
<span className="flex flex-row items-center focus:outline-none">
Keyword
</span>
<span className="flex flex-row items-center focus:outline-none">Keyword</span>
</label>
</div>
<div className="px-3 w-5/24 grow">
<div className="w-5/24 grow px-3">
<label
ref={valueRef}
className="flex flex-col flex-1 text-white text-lg pl-1 select-none"
className="flex flex-1 select-none flex-col pl-1 text-lg text-white"
>
<span className="flex flex-row items-center focus:outline-none">
Value
</span>
<span className="flex flex-row items-center focus:outline-none">Value</span>
</label>
</div>
</div>
@@ -114,10 +106,7 @@ function DicomTagTable({ rows }) {
* When the browser window resizes, update the row virtualization (i.e. row heights)
*/
useEffect(() => {
const debouncedResize = debounce(
() => listRef.current.resetAfterIndex(0),
100
);
const debouncedResize = debounce(() => listRef.current.resetAfterIndex(0), 100);
window.addEventListener('resize', debouncedResize);
@@ -135,15 +124,15 @@ function DicomTagTable({ rows }) {
<div
style={{ ...style, ...rowStyle }}
className={classNames(
'hover:bg-secondary-main transition duration-300 bg-black flex flex-row w-full border-secondary-light items-center text-base break-all',
'hover:bg-secondary-main border-secondary-light flex w-full flex-row items-center break-all bg-black text-base transition duration-300',
lineHeightClassName
)}
key={`DICOMTagRow-${index}`}
>
<div className="px-3 w-4/24">{row[0]}</div>
<div className="px-3 w-2/24">{row[1]}</div>
<div className="px-3 w-6/24">{row[2]}</div>
<div className="px-3 w-5/24 grow">{row[3]}</div>
<div className="w-4/24 px-3">{row[0]}</div>
<div className="w-2/24 px-3">{row[1]}</div>
<div className="w-6/24 px-3">{row[2]}</div>
<div className="w-5/24 grow px-3">{row[3]}</div>
</div>
);
},
@@ -154,9 +143,7 @@ function DicomTagTable({ rows }) {
* Whenever any one of the column headers is set, then the header is rendered.
* Here we chose the tag header.
*/
const isHeaderRendered = useCallback(() => tagHeaderElem !== null, [
tagHeaderElem,
]);
const isHeaderRendered = useCallback(() => tagHeaderElem !== null, [tagHeaderElem]);
/**
* Get the item/row size. We use the header column widths to calculate the various row heights.
@@ -179,11 +166,7 @@ function DicomTagTable({ rows }) {
.map((colText, index) => {
const colOneLineWidth = context.measureText(colText).width;
const numLines = Math.ceil(colOneLineWidth / headerWidths[index]);
return (
numLines * lineHeightPx +
2 * rowVerticalPaddingPx +
rowBottomBorderPx
);
return numLines * lineHeightPx + 2 * rowVerticalPaddingPx + rowBottomBorderPx;
})
.reduce((maxHeight, colHeight) => Math.max(maxHeight, colHeight));
},
@@ -204,7 +187,7 @@ function DicomTagTable({ rows }) {
valueRef={valueRef}
/>
<div
className="m-auto relative border-2 border-black bg-black"
className="relative m-auto border-2 border-black bg-black"
style={{ height: '32rem' }}
>
{isHeaderRendered() && (
@@ -1,4 +1,4 @@
export default function(wadoRoot) {
export default function (wadoRoot) {
return {
series: (StudyInstanceUID, SeriesInstanceUID) => {
return new Promise((resolve, reject) => {
@@ -15,7 +15,7 @@ export default function(wadoRoot) {
console.log(xhr);
xhr.onreadystatechange = function() {
xhr.onreadystatechange = function () {
//Call a function when the state changes.
if (xhr.readyState == 4) {
switch (xhr.status) {
@@ -1,11 +1,5 @@
import { api } from 'dicomweb-client';
import {
DicomMetadataStore,
IWebApiDataSource,
utils,
errorHandler,
classes,
} from '@ohif/core';
import { DicomMetadataStore, IWebApiDataSource, utils, errorHandler, classes } from '@ohif/core';
import {
mapParams,
@@ -18,10 +12,7 @@ import dcm4cheeReject from './dcm4cheeReject';
import getImageId from './utils/getImageId';
import dcmjs from 'dcmjs';
import {
retrieveStudyMetadata,
deleteStudyMetadataPromise,
} from './retrieveStudyMetadata.js';
import { retrieveStudyMetadata, deleteStudyMetadataPromise } from './retrieveStudyMetadata.js';
import StaticWadoClient from './utils/StaticWadoClient';
import getDirectURL from '../utils/getDirectURL';
import { fixBulkDataURI } from './utils/fixBulkDataURI';
@@ -30,8 +21,7 @@ const { DicomMetaDictionary, DicomDict } = dcmjs.data;
const { naturalizeDataset, denaturalizeDataset } = DicomMetaDictionary;
const ImplementationClassUID =
'2.25.270695996825855179949881587723571202391.2.0.0';
const ImplementationClassUID = '2.25.270695996825855179949881587723571202391.2.0.0';
const ImplementationVersionName = 'OHIF-VIEWER-2.0.0';
const EXPLICIT_VR_LITTLE_ENDIAN = '1.2.840.10008.1.2.1';
@@ -61,10 +51,7 @@ function createDicomWebApi(dicomWebConfig, userAuthenticationService) {
const implementation = {
initialize: ({ params, query }) => {
if (
dicomWebConfig.onConfiguration &&
typeof dicomWebConfig.onConfiguration === 'function'
) {
if (dicomWebConfig.onConfiguration && typeof dicomWebConfig.onConfiguration === 'function') {
dicomWebConfig = dicomWebConfig.onConfiguration(dicomWebConfig, {
params,
query,
@@ -126,7 +113,7 @@ function createDicomWebApi(dicomWebConfig, userAuthenticationService) {
query: {
studies: {
mapParams: mapParams.bind(),
search: async function(origParams) {
search: async function (origParams) {
qidoDicomWebClient.headers = getAuthrorizationHeader();
const { studyInstanceUid, seriesInstanceUid, ...mappedParams } =
mapParams(origParams, {
@@ -134,12 +121,7 @@ function createDicomWebApi(dicomWebConfig, userAuthenticationService) {
supportsWildcard: dicomWebConfig.supportsWildcard,
}) || {};
const results = await qidoSearch(
qidoDicomWebClient,
undefined,
undefined,
mappedParams
);
const results = await qidoSearch(qidoDicomWebClient, undefined, undefined, mappedParams);
return processResults(results);
},
@@ -147,12 +129,9 @@ function createDicomWebApi(dicomWebConfig, userAuthenticationService) {
},
series: {
// mapParams: mapParams.bind(),
search: async function(studyInstanceUid) {
search: async function (studyInstanceUid) {
qidoDicomWebClient.headers = getAuthrorizationHeader();
const results = await seriesInStudy(
qidoDicomWebClient,
studyInstanceUid
);
const results = await seriesInStudy(qidoDicomWebClient, studyInstanceUid);
return processSeriesResults(results);
},
@@ -161,13 +140,7 @@ function createDicomWebApi(dicomWebConfig, userAuthenticationService) {
instances: {
search: (studyInstanceUid, queryParameters) => {
qidoDicomWebClient.headers = getAuthrorizationHeader();
qidoSearch.call(
undefined,
qidoDicomWebClient,
studyInstanceUid,
null,
queryParameters
);
qidoSearch.call(undefined, qidoDicomWebClient, studyInstanceUid, null, queryParameters);
},
},
},
@@ -213,9 +186,7 @@ function createDicomWebApi(dicomWebConfig, userAuthenticationService) {
madeInClient = false,
} = {}) => {
if (!StudyInstanceUID) {
throw new Error(
'Unable to query for SeriesMetadata without StudyInstanceUID'
);
throw new Error('Unable to query for SeriesMetadata without StudyInstanceUID');
}
if (dicomWebConfig.enableStudyLazyLoad) {
@@ -250,8 +221,7 @@ function createDicomWebApi(dicomWebConfig, userAuthenticationService) {
await wadoDicomWebClient.storeInstances(options);
} else {
const meta = {
FileMetaInformationVersion:
dataset._meta.FileMetaInformationVersion.Value,
FileMetaInformationVersion: dataset._meta.FileMetaInformationVersion.Value,
MediaStorageSOPClassUID: dataset.SOPClassUID,
MediaStorageSOPInstanceUID: dataset.SOPInstanceUID,
TransferSyntaxUID: EXPLICIT_VR_LITTLE_ENDIAN,
@@ -340,10 +310,7 @@ function createDicomWebApi(dicomWebConfig, userAuthenticationService) {
DicomMetadataStore.addSeriesMetadata(seriesMetadata, madeInClient);
Object.keys(instancesPerSeries).forEach(seriesInstanceUID =>
DicomMetadataStore.addInstances(
instancesPerSeries[seriesInstanceUID],
madeInClient
)
DicomMetadataStore.addInstances(instancesPerSeries[seriesInstanceUID], madeInClient)
);
},
@@ -357,17 +324,15 @@ function createDicomWebApi(dicomWebConfig, userAuthenticationService) {
const enableStudyLazyLoad = true;
wadoDicomWebClient.headers = generateWadoHeader();
// Get Series
const {
preLoadData: seriesSummaryMetadata,
promises: seriesPromises,
} = await retrieveStudyMetadata(
wadoDicomWebClient,
StudyInstanceUID,
enableStudyLazyLoad,
filters,
sortCriteria,
sortFunction
);
const { preLoadData: seriesSummaryMetadata, promises: seriesPromises } =
await retrieveStudyMetadata(
wadoDicomWebClient,
StudyInstanceUID,
enableStudyLazyLoad,
filters,
sortCriteria,
sortFunction
);
/**
* naturalizes the dataset, and adds a retrieve bulkdata method
@@ -412,8 +377,7 @@ function createDicomWebApi(dicomWebConfig, userAuthenticationService) {
// the bulk data and DICOM video cases where the second ArrayBuffer is
// the bulk data. Here we play it safe and do a find.
const ret =
(val instanceof Array &&
val.find(arrayBuffer => arrayBuffer?.byteLength)) ||
(val instanceof Array && val.find(arrayBuffer => arrayBuffer?.byteLength)) ||
undefined;
value.Value = ret;
return ret;
@@ -456,10 +420,7 @@ function createDicomWebApi(dicomWebConfig, userAuthenticationService) {
}
function setSuccessFlag() {
const study = DicomMetadataStore.getStudy(
StudyInstanceUID,
madeInClient
);
const study = DicomMetadataStore.getStudy(StudyInstanceUID, madeInClient);
study.isLoaded = true;
}
@@ -520,13 +481,10 @@ function createDicomWebApi(dicomWebConfig, userAuthenticationService) {
},
getStudyInstanceUIDs({ params, query }) {
const { StudyInstanceUIDs: paramsStudyInstanceUIDs } = params;
const queryStudyInstanceUIDs = utils.splitComma(
query.getAll('StudyInstanceUIDs')
);
const queryStudyInstanceUIDs = utils.splitComma(query.getAll('StudyInstanceUIDs'));
const StudyInstanceUIDs =
(queryStudyInstanceUIDs.length && queryStudyInstanceUIDs) ||
paramsStudyInstanceUIDs;
(queryStudyInstanceUIDs.length && queryStudyInstanceUIDs) || paramsStudyInstanceUIDs;
const StudyInstanceUIDsAsArray =
StudyInstanceUIDs && Array.isArray(StudyInstanceUIDs)
? StudyInstanceUIDs
@@ -54,10 +54,7 @@ function processResults(qidoStudies) {
patientName: utils.formatPN(getName(qidoStudy['00100010'])) || '',
instances: Number(getString(qidoStudy['00201208'])) || 0, // number
description: getString(qidoStudy['00081030']) || '',
modalities:
getString(
getModalities(qidoStudy['00080060'], qidoStudy['00080061'])
) || '',
modalities: getString(getModalities(qidoStudy['00080060'], qidoStudy['00080061'])) || '',
})
);
@@ -105,12 +102,7 @@ export function processSeriesResults(qidoSeries) {
* @param {string} [queryParamaters]
* @returns {Promise<results>} - Promise that resolves results
*/
async function search(
dicomWebClient,
studyInstanceUid,
seriesInstanceUid,
queryParameters
) {
async function search(dicomWebClient, studyInstanceUid, seriesInstanceUid, queryParameters) {
let searchResult = await dicomWebClient.searchForStudies({
studyInstanceUid: undefined,
queryParams: queryParameters,
@@ -136,10 +128,7 @@ export function seriesInStudy(dicomWebClient, studyInstanceUID) {
}
export default function searchStudies(server, filter) {
const queryParams = getQIDOQueryParams(
filter,
server.qidoSupportsIncludeField
);
const queryParams = getQIDOQueryParams(filter, server.qidoSupportsIncludeField);
const options = {
queryParams,
};
@@ -28,14 +28,10 @@ export function retrieveStudyMetadata(
// corresponding promise from the "StudyMetaDataPromises" map...
if (!dicomWebClient) {
throw new Error(
`${moduleName}: Required 'dicomWebClient' parameter not provided.`
);
throw new Error(`${moduleName}: Required 'dicomWebClient' parameter not provided.`);
}
if (!StudyInstanceUID) {
throw new Error(
`${moduleName}: Required 'StudyInstanceUID' parameter not provided.`
);
throw new Error(`${moduleName}: Required 'StudyInstanceUID' parameter not provided.`);
}
// Already waiting on result? Return cached promise
@@ -52,7 +48,7 @@ export function retrieveStudyMetadata(
filters,
sortCriteria,
sortFunction
).then(function(data) {
).then(function (data) {
resolve(data);
}, reject);
});
@@ -50,14 +50,7 @@ export default class StaticWadoClient extends api.DICOMwebClient {
const lowerParams = this.toLowerParams(queryParams);
const filtered = searchResult.filter(study => {
for (const key of Object.keys(StaticWadoClient.studyFilterKeys)) {
if (
!this.filterItem(
key,
lowerParams,
study,
StaticWadoClient.studyFilterKeys
)
) {
if (!this.filterItem(key, lowerParams, study, StaticWadoClient.studyFilterKeys)) {
return false;
}
}
@@ -80,14 +73,7 @@ export default class StaticWadoClient extends api.DICOMwebClient {
const filtered = searchResult.filter(series => {
for (const key of Object.keys(StaticWadoClient.seriesFilterKeys)) {
if (
!this.filterItem(
key,
lowerParams,
series,
StaticWadoClient.seriesFilterKeys
)
) {
if (!this.filterItem(key, lowerParams, series, StaticWadoClient.seriesFilterKeys)) {
return false;
}
}
@@ -132,10 +118,7 @@ export default class StaticWadoClient extends api.DICOMwebClient {
} else if (desired[desired.length - 1] === '*') {
return actual.indexOf(desired.substring(0, desired.length - 1)) != -1;
} else if (desired[0] === '*') {
return (
actual.indexOf(desired.substring(1)) ===
actual.length - desired.length + 1
);
return actual.indexOf(desired.substring(1)) === actual.length - desired.length + 1;
}
}
return desired === actual;
@@ -20,10 +20,7 @@ function fixBulkDataURI(value, instance, dicomWebConfig) {
// in case of the relative path, make it absolute. The current DICOM standard says
// the bulkdataURI is relative to the series. However, there are situations where
// it can be relative to the study too
if (
!value.BulkDataURI.startsWith('http') &&
!value.BulkDataURI.startsWith('/')
) {
if (!value.BulkDataURI.startsWith('http') && !value.BulkDataURI.startsWith('/')) {
if (dicomWebConfig.bulkDataURI?.relativeResolution === 'studies') {
value.BulkDataURI = `${dicomWebConfig.wadoRoot}/studies/${instance.StudyInstanceUID}/${value.BulkDataURI}`;
} else if (
@@ -24,12 +24,7 @@ function buildInstanceWadoUrl(config, instance) {
* @param thumbnail
* @returns {string} The imageId to be used by Cornerstone
*/
export default function getImageId({
instance,
frame,
config,
thumbnail = false,
}) {
export default function getImageId({ instance, frame, config, thumbnail = false }) {
if (!instance) {
return;
}
@@ -20,9 +20,7 @@ async function RetrieveMetadata(
sortFunction
) {
const RetrieveMetadataLoader =
enableStudyLazyLoad !== false
? RetrieveMetadataLoaderAsync
: RetrieveMetadataLoaderSync;
enableStudyLazyLoad !== false ? RetrieveMetadataLoaderAsync : RetrieveMetadataLoaderSync;
const retrieveMetadataLoader = new RetrieveMetadataLoader(
dicomWebClient,
@@ -14,13 +14,7 @@ export default class RetrieveMetadataLoader {
* @param {string} [filter.seriesInstanceUID] - series instance uid to filter results against
* @param {Function} [sortSeries] - Custom sort function for series
*/
constructor(
client,
studyInstanceUID,
filters = {},
sortCriteria,
sortFunction
) {
constructor(client, studyInstanceUID, filters = {}, sortCriteria, sortFunction) {
this.client = client;
this.studyInstanceUID = studyInstanceUID;
this.filters = filters;
@@ -1,8 +1,5 @@
import dcmjs from 'dcmjs';
import {
sortStudySeries,
sortingCriteria,
} from '@ohif/core/src/utils/sortStudy';
import { sortStudySeries, sortingCriteria } from '@ohif/core/src/utils/sortStudy';
import RetrieveMetadataLoader from './retrieveMetadataLoader';
/**
@@ -12,11 +9,7 @@ import RetrieveMetadataLoader from './retrieveMetadataLoader';
* @param {Array} seriesInstanceUIDList A list of Series Instance UIDs
* @returns {Object} Returns an object which supports loading of instances from each of given Series Instance UID
*/
function makeSeriesAsyncLoader(
client,
studyInstanceUID,
seriesInstanceUIDList
) {
function makeSeriesAsyncLoader(client, studyInstanceUID, seriesInstanceUIDList) {
return Object.freeze({
hasNext() {
return seriesInstanceUIDList.length > 0;
@@ -43,11 +36,7 @@ export default class RetrieveMetadataLoaderAsync extends RetrieveMetadataLoader
*/
*getPreLoaders() {
const preLoaders = [];
const {
studyInstanceUID,
filters: { seriesInstanceUID } = {},
client,
} = this;
const { studyInstanceUID, filters: { seriesInstanceUID } = {}, client } = this;
if (seriesInstanceUID) {
const options = {
@@ -73,8 +62,7 @@ export default class RetrieveMetadataLoaderAsync extends RetrieveMetadataLoader
return sortStudySeries(
naturalized,
sortCriteria ||
sortingCriteria.seriesSortCriteria.seriesInfoSortingCriteria,
sortCriteria || sortingCriteria.seriesSortCriteria.seriesInfoSortingCriteria,
sortFunction
);
}
@@ -84,11 +72,7 @@ export default class RetrieveMetadataLoaderAsync extends RetrieveMetadataLoader
const seriesInstanceUIDs = preLoadData.map(s => s.SeriesInstanceUID);
const seriesAsyncLoader = makeSeriesAsyncLoader(
client,
studyInstanceUID,
seriesInstanceUIDs
);
const seriesAsyncLoader = makeSeriesAsyncLoader(client, studyInstanceUID, seriesInstanceUIDs);
const promises = [];
@@ -31,11 +31,7 @@ export default class RetrieveMetadataLoaderSync extends RetrieveMetadataLoader {
*/
*getLoaders() {
const loaders = [];
const {
studyInstanceUID,
filters: { seriesInstanceUID } = {},
client,
} = this;
const { studyInstanceUID, filters: { seriesInstanceUID } = {}, client } = this;
if (seriesInstanceUID) {
loaders.push(
@@ -46,9 +42,7 @@ export default class RetrieveMetadataLoaderSync extends RetrieveMetadataLoader {
);
}
loaders.push(
client.retrieveStudyMetadata.bind(client, { studyInstanceUID })
);
loaders.push(client.retrieveStudyMetadata.bind(client, { studyInstanceUID }));
yield* loaders;
}
@@ -9,10 +9,7 @@ import { createDicomWebApi } from '../DicomWebDataSource/index';
* dicomWeb configuration array
*
*/
function createDicomWebProxyApi(
dicomWebProxyConfig,
UserAuthenticationService
) {
function createDicomWebProxyApi(dicomWebProxyConfig, UserAuthenticationService) {
const { name } = dicomWebProxyConfig;
let dicomWebDelegate = undefined;
@@ -45,28 +42,21 @@ function createDicomWebProxyApi(
},
instances: {
search: (studyInstanceUid, queryParameters) =>
dicomWebDelegate.query.instances.search(
studyInstanceUid,
queryParameters
),
dicomWebDelegate.query.instances.search(studyInstanceUid, queryParameters),
},
},
retrieve: {
directURL: (...args) => dicomWebDelegate.retrieve.directURL(...args),
series: {
metadata: (...args) =>
dicomWebDelegate.retrieve.series.metadata(...args),
metadata: (...args) => dicomWebDelegate.retrieve.series.metadata(...args),
},
},
store: {
dicom: (...args) => dicomWebDelegate.store(...args),
},
deleteStudyMetadataPromise: (...args) =>
dicomWebDelegate.deleteStudyMetadataPromise(...args),
getImageIdsForDisplaySet: (...args) =>
dicomWebDelegate.getImageIdsForDisplaySet(...args),
getImageIdsForInstance: (...args) =>
dicomWebDelegate.getImageIdsForInstance(...args),
deleteStudyMetadataPromise: (...args) => dicomWebDelegate.deleteStudyMetadataPromise(...args),
getImageIdsForDisplaySet: (...args) => dicomWebDelegate.getImageIdsForDisplaySet(...args),
getImageIdsForInstance: (...args) => dicomWebDelegate.getImageIdsForInstance(...args),
getStudyInstanceUIDs({ params, query }) {
let studyInstanceUIDs = [];
@@ -9,9 +9,15 @@ function ActionButtons({ onExportClick, onCreateReportClick }) {
return (
<React.Fragment>
<ButtonGroup color="black" size="inherit">
<ButtonGroup
color="black"
size="inherit"
>
{/* TODO Revisit design of ButtonGroup later - for now use LegacyButton for its children.*/}
<LegacyButton className="px-2 py-2 text-base" onClick={onExportClick}>
<LegacyButton
className="px-2 py-2 text-base"
onClick={onExportClick}
>
{t('Export CSV')}
</LegacyButton>
<LegacyButton
@@ -15,20 +15,16 @@ function DataSourceSelector() {
return (
<div style={{ width: '100%', height: '100%' }}>
<div className="h-screen w-screen flex justify-center items-center ">
<div className="py-8 px-8 mx-auto bg-secondary-dark drop-shadow-md space-y-2 rounded-lg">
<div className="flex h-screen w-screen items-center justify-center ">
<div className="bg-secondary-dark mx-auto space-y-2 rounded-lg py-8 px-8 drop-shadow-md">
<img
className="block mx-auto h-14"
className="mx-auto block h-14"
src="./ohif-logo.svg"
alt="OHIF"
/>
<div className="text-center space-y-2 pt-4">
<div className="space-y-2 pt-4 text-center">
{dsConfigs
.filter(
it =>
it.sourceName !== 'dicomjson' &&
it.sourceName !== 'dicomlocal'
)
.filter(it => it.sourceName !== 'dicomjson' && it.sourceName !== 'dicomlocal')
.map(ds => (
<div key={ds.sourceName}>
<h1 className="text-white">
@@ -1,13 +1,7 @@
import React, { useEffect, useState } from 'react';
import PropTypes from 'prop-types';
import { utils, ServicesManager } from '@ohif/core';
import {
MeasurementTable,
Dialog,
Input,
useViewportGrid,
ButtonEnums,
} from '@ohif/ui';
import { MeasurementTable, Dialog, Input, useViewportGrid, ButtonEnums } from '@ohif/ui';
import ActionButtons from './ActionButtons';
import debounce from 'lodash.debounce';
@@ -26,19 +20,13 @@ export default function PanelMeasurementTable({
}): React.FunctionComponent {
const [viewportGrid, viewportGridService] = useViewportGrid();
const { activeViewportId, viewports } = viewportGrid;
const {
measurementService,
uiDialogService,
uiNotificationService,
displaySetService,
} = (servicesManager as ServicesManager).services;
const { measurementService, uiDialogService, uiNotificationService, displaySetService } = (
servicesManager as ServicesManager
).services;
const [displayMeasurements, setDisplayMeasurements] = useState([]);
useEffect(() => {
const debouncedSetDisplayMeasurements = debounce(
setDisplayMeasurements,
100
);
const debouncedSetDisplayMeasurements = debounce(setDisplayMeasurements, 100);
// ~~ Initial
setDisplayMeasurements(_getMappedMeasurements(measurementService));
@@ -53,9 +41,7 @@ export default function PanelMeasurementTable({
[added, addedRaw, updated, removed, cleared].forEach(evt => {
subscriptions.push(
measurementService.subscribe(evt, () => {
debouncedSetDisplayMeasurements(
_getMappedMeasurements(measurementService)
);
debouncedSetDisplayMeasurements(_getMappedMeasurements(measurementService));
}).unsubscribe
);
});
@@ -104,9 +90,7 @@ export default function PanelMeasurementTable({
});
if (promptResult.action === CREATE_REPORT_DIALOG_RESPONSE.CREATE_REPORT) {
const dataSources = extensionManager.getDataSources(
promptResult.dataSourceName
);
const dataSources = extensionManager.getDataSources(promptResult.dataSourceName);
const dataSource = dataSources[0];
const SeriesDescription =
@@ -117,10 +101,7 @@ export default function PanelMeasurementTable({
// Re-use an existing series having the same series description to avoid
// creating too many series instances.
const options = findSRWithSameSeriesDescription(
SeriesDescription,
displaySetService
);
const options = findSRWithSameSeriesDescription(SeriesDescription, displaySetService);
return createReportAsync(
servicesManager,
@@ -186,7 +167,7 @@ export default function PanelMeasurementTable({
labelClassName="text-white text-[14px] leading-[1.2]"
autoFocus
id="annotation"
className="bg-black border-primary-main"
className="border-primary-main bg-black"
type="text"
value={value.label}
onChange={onChangeHandler}
@@ -217,7 +198,7 @@ export default function PanelMeasurementTable({
return (
<>
<div
className="overflow-x-hidden overflow-y-auto ohif-scrollbar"
className="ohif-scrollbar overflow-y-auto overflow-x-hidden"
data-cy={'measurements-panel'}
>
<MeasurementTable
@@ -17,21 +17,15 @@ function PanelStudyBrowser({
requestDisplaySetCreationForStudy,
dataSource,
}) {
const {
hangingProtocolService,
displaySetService,
uiNotificationService,
} = servicesManager.services;
const { hangingProtocolService, displaySetService, uiNotificationService } =
servicesManager.services;
const navigate = useNavigate();
// Normally you nest the components so the tree isn't so deep, and the data
// doesn't have to have such an intense shape. This works well enough for now.
// Tabs --> Studies --> DisplaySets --> Thumbnails
const { StudyInstanceUIDs } = useImageViewer();
const [
{ activeViewportId, viewports },
viewportGridService,
] = useViewportGrid();
const [{ activeViewportId, viewports }, viewportGridService] = useViewportGrid();
const [activeTabName, setActiveTabName] = useState('primary');
const [expandedStudyInstanceUIDs, setExpandedStudyInstanceUIDs] = useState([
...StudyInstanceUIDs,
@@ -53,8 +47,7 @@ function PanelStudyBrowser({
console.warn(error);
uiNotificationService.show({
title: 'Thumbnail Double Click',
message:
'The selected display sets could not be added to the viewport.',
message: 'The selected display sets could not be added to the viewport.',
type: 'info',
duration: 3000,
});
@@ -82,9 +75,7 @@ function PanelStudyBrowser({
// try to fetch the prior studies based on the patientID if the
// server can respond.
try {
qidoStudiesForPatient = await getStudiesForPatientByMRN(
qidoForStudyUID
);
qidoStudiesForPatient = await getStudiesForPatientByMRN(qidoForStudyUID);
} catch (error) {
console.warn(error);
}
@@ -103,11 +94,7 @@ function PanelStudyBrowser({
setStudyDisplayList(prevArray => {
const ret = [...prevArray];
for (const study of actuallyMappedStudies) {
if (
!prevArray.find(
it => it.studyInstanceUid === study.studyInstanceUid
)
) {
if (!prevArray.find(it => it.studyInstanceUid === study.studyInstanceUid)) {
ret.push(study);
}
}
@@ -124,18 +111,14 @@ function PanelStudyBrowser({
const currentDisplaySets = displaySetService.activeDisplaySets;
currentDisplaySets.forEach(async dSet => {
const newImageSrcEntry = {};
const displaySet = displaySetService.getDisplaySetByUID(
dSet.displaySetInstanceUID
);
const displaySet = displaySetService.getDisplaySetByUID(dSet.displaySetInstanceUID);
const imageIds = dataSource.getImageIdsForDisplaySet(displaySet);
const imageId = imageIds[Math.floor(imageIds.length / 2)];
// TODO: Is it okay that imageIds are not returned here for SR displaySets?
if (imageId && !displaySet?.unsupported) {
// When the image arrives, render it and store the result in the thumbnailImgSrcMap
newImageSrcEntry[dSet.displaySetInstanceUID] = await getImageSrc(
imageId
);
newImageSrcEntry[dSet.displaySetInstanceUID] = await getImageSrc(imageId);
if (isMounted.current) {
setThumbnailImageSrcMap(prevState => {
return { ...prevState, ...newImageSrcEntry };
@@ -153,10 +136,7 @@ function PanelStudyBrowser({
useEffect(() => {
// TODO: Are we sure `activeDisplaySets` will always be accurate?
const currentDisplaySets = displaySetService.activeDisplaySets;
const mappedDisplaySets = _mapDisplaySets(
currentDisplaySets,
thumbnailImageSrcMap
);
const mappedDisplaySets = _mapDisplaySets(currentDisplaySets, thumbnailImageSrcMap);
sortStudyInstances(mappedDisplaySets);
setDisplaySets(mappedDisplaySets);
@@ -172,9 +152,7 @@ function PanelStudyBrowser({
const { displaySetsAdded } = data;
displaySetsAdded.forEach(async dSet => {
const newImageSrcEntry = {};
const displaySet = displaySetService.getDisplaySetByUID(
dSet.displaySetInstanceUID
);
const displaySet = displaySetService.getDisplaySetByUID(dSet.displaySetInstanceUID);
if (!displaySet?.unsupported) {
const imageIds = dataSource.getImageIdsForDisplaySet(displaySet);
const imageId = imageIds[Math.floor(imageIds.length / 2)];
@@ -202,10 +180,7 @@ function PanelStudyBrowser({
const SubscriptionDisplaySetsChanged = displaySetService.subscribe(
displaySetService.EVENTS.DISPLAY_SETS_CHANGED,
changedDisplaySets => {
const mappedDisplaySets = _mapDisplaySets(
changedDisplaySets,
thumbnailImageSrcMap
);
const mappedDisplaySets = _mapDisplaySets(changedDisplaySets, thumbnailImageSrcMap);
setDisplaySets(mappedDisplaySets);
}
);
@@ -230,40 +205,25 @@ function PanelStudyBrowser({
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const tabs = _createStudyBrowserTabs(
StudyInstanceUIDs,
studyDisplayList,
displaySets
);
const tabs = _createStudyBrowserTabs(StudyInstanceUIDs, studyDisplayList, displaySets);
// TODO: Should not fire this on "close"
function _handleStudyClick(StudyInstanceUID) {
const shouldCollapseStudy = expandedStudyInstanceUIDs.includes(
StudyInstanceUID
);
const shouldCollapseStudy = expandedStudyInstanceUIDs.includes(StudyInstanceUID);
const updatedExpandedStudyInstanceUIDs = shouldCollapseStudy
? // eslint-disable-next-line prettier/prettier
[
...expandedStudyInstanceUIDs.filter(
stdyUid => stdyUid !== StudyInstanceUID
),
]
[...expandedStudyInstanceUIDs.filter(stdyUid => stdyUid !== StudyInstanceUID)]
: [...expandedStudyInstanceUIDs, StudyInstanceUID];
setExpandedStudyInstanceUIDs(updatedExpandedStudyInstanceUIDs);
if (!shouldCollapseStudy) {
const madeInClient = true;
requestDisplaySetCreationForStudy(
displaySetService,
StudyInstanceUID,
madeInClient
);
requestDisplaySetCreationForStudy(displaySetService, StudyInstanceUID, madeInClient);
}
}
const activeDisplaySetInstanceUIDs = viewports.get(activeViewportId)
?.displaySetInstanceUIDs;
const activeDisplaySetInstanceUIDs = viewports.get(activeViewportId)?.displaySetInstanceUIDs;
return (
<StudyBrowser
@@ -326,9 +286,7 @@ function _mapDisplaySets(displaySets, thumbnailImageSrcMap) {
const componentType = _getComponentType(ds);
const array =
componentType === 'thumbnail'
? thumbnailDisplaySets
: thumbnailNoImageDisplaySets;
componentType === 'thumbnail' ? thumbnailDisplaySets : thumbnailNoImageDisplaySets;
array.push({
displaySetInstanceUID: ds.displaySetInstanceUID,
@@ -355,14 +313,7 @@ function _mapDisplaySets(displaySets, thumbnailImageSrcMap) {
return [...thumbnailDisplaySets, ...thumbnailNoImageDisplaySets];
}
const thumbnailNoImageModalities = [
'SR',
'SEG',
'SM',
'RTSTRUCT',
'RTPLAN',
'RTDOSE',
];
const thumbnailNoImageModalities = ['SR', 'SEG', 'SM', 'RTSTRUCT', 'RTPLAN', 'RTDOSE'];
function _getComponentType(ds) {
if (thumbnailNoImageModalities.includes(ds.Modality) || ds?.unsupported) {
@@ -385,11 +336,7 @@ function _getComponentType(ds) {
* @param {object[]} displaySets
* @returns tabs - The prop object expected by the StudyBrowser component
*/
function _createStudyBrowserTabs(
primaryStudyInstanceUIDs,
studyDisplayList,
displaySets
) {
function _createStudyBrowserTabs(primaryStudyInstanceUIDs, studyDisplayList, displaySets) {
const primaryStudies = [];
const recentStudies = [];
const allStudies = [];
@@ -13,21 +13,12 @@ import requestDisplaySetCreationForStudy from './requestDisplaySetCreationForStu
* @param {object} commandsManager
* @param {object} extensionManager
*/
function WrappedPanelStudyBrowser({
commandsManager,
extensionManager,
servicesManager,
}) {
function WrappedPanelStudyBrowser({ commandsManager, extensionManager, servicesManager }) {
// TODO: This should be made available a different way; route should have
// already determined our datasource
const dataSource = extensionManager.getDataSources()[0];
const _getStudiesForPatientByMRN = getStudiesForPatientByMRN.bind(
null,
dataSource
);
const _getImageSrcFromImageId = _createGetImageSrcFromImageIdFn(
extensionManager
);
const _getStudiesForPatientByMRN = getStudiesForPatientByMRN.bind(null, dataSource);
const _getImageSrcFromImageId = _createGetImageSrcFromImageIdFn(extensionManager);
const _requestDisplaySetCreationForStudy = requestDisplaySetCreationForStudy.bind(
null,
dataSource
@@ -7,11 +7,8 @@ export const CREATE_REPORT_DIALOG_RESPONSE = {
CREATE_REPORT: 1,
};
export default function createReportDialogPrompt(
uiDialogService,
{ extensionManager }
) {
return new Promise(function(resolve, reject) {
export default function createReportDialogPrompt(uiDialogService, { extensionManager }) {
return new Promise(function (resolve, reject) {
let dialogId = undefined;
const _handleClose = () => {
@@ -52,10 +49,8 @@ export default function createReportDialogPrompt(
const dataSourcesOpts = Object.keys(extensionManager.dataSourceMap)
.filter(ds => {
const configuration =
extensionManager.dataSourceDefs[ds]?.configuration;
const supportsStow =
configuration?.supportsStow ?? configuration?.wadoRoot;
const configuration = extensionManager.dataSourceDefs[ds]?.configuration;
const supportsStow = configuration?.supportsStow ?? configuration?.wadoRoot;
return supportsStow;
})
.map(ds => {
@@ -105,12 +100,11 @@ export default function createReportDialogPrompt(
{dataSourcesOpts.length > 1 && (
<Select
closeMenuOnSelect={true}
className="mr-2 bg-black border-primary-main"
className="border-primary-main mr-2 bg-black"
options={dataSourcesOpts}
placeholder={
dataSourcesOpts.find(
option => option.value === value.dataSourceName
).placeHolder
dataSourcesOpts.find(option => option.value === value.dataSourceName)
.placeHolder
}
value={value.dataSourceName}
onChange={evt => {
@@ -123,7 +117,7 @@ export default function createReportDialogPrompt(
autoFocus
label="Enter the report name"
labelClassName="text-white text-[14px] leading-[1.2]"
className="bg-black border-primary-main"
className="border-primary-main bg-black"
type="text"
value={value.label}
onChange={onChangeHandler}
+2 -2
View File
@@ -4,10 +4,10 @@
// leading edge, instead of the trailing.
function debounce(func, wait, immediate) {
var timeout;
return function() {
return function () {
var context = this,
args = arguments;
var later = function() {
var later = function () {
timeout = null;
if (!immediate) {
func.apply(context, args);
+4 -1
View File
@@ -49,7 +49,10 @@ export default function Toolbar({ servicesManager }) {
return (
// The margin for separating the tools on the toolbar should go here and NOT in each individual component (button) item.
// This allows for the individual items to be included in other UI components where perhaps alternative margins are desired.
<div key={id} className={classnames('mr-1')}>
<div
key={id}
className={classnames('mr-1')}
>
<Component
id={id}
{...componentProps}
@@ -1,7 +1,5 @@
import React from 'react';
export default function ToolbarDivider() {
return (
<span className="self-center w-4 h-8 mx-2 border-l border-common-dark" />
);
return <span className="border-common-dark mx-2 h-8 w-4 self-center border-l" />;
}
@@ -4,19 +4,10 @@ import { LayoutSelector as OHIFLayoutSelector, ToolbarButton } from '@ohif/ui';
import { ServicesManager } from '@ohif/core';
function LayoutSelector({
rows,
columns,
className,
servicesManager,
...rest
}) {
function LayoutSelector({ rows, columns, className, servicesManager, ...rest }) {
const [isOpen, setIsOpen] = useState(false);
const {
hangingProtocolService,
toolbarService,
} = (servicesManager as ServicesManager).services;
const { hangingProtocolService, toolbarService } = (servicesManager as ServicesManager).services;
const closeOnOutsideClick = () => {
if (isOpen) {
+8 -21
View File
@@ -14,12 +14,7 @@ import {
LoadingIndicatorProgress,
} from '@ohif/ui';
import i18n from '@ohif/i18n';
import {
ServicesManager,
HangingProtocolService,
hotkeys,
CommandsManager,
} from '@ohif/core';
import { ServicesManager, HangingProtocolService, hotkeys, CommandsManager } from '@ohif/core';
import { useAppConfig } from '@state';
import Toolbar from '../Toolbar/Toolbar';
@@ -76,9 +71,7 @@ function ViewerLayout({
const { t } = useTranslation();
const { show, hide } = useModal();
const [showLoadingIndicator, setShowLoadingIndicator] = useState(
appConfig.showLoadingIndicator
);
const [showLoadingIndicator, setShowLoadingIndicator] = useState(appConfig.showLoadingIndicator);
const { hangingProtocolService } = servicesManager.services;
@@ -105,9 +98,7 @@ function ViewerLayout({
title: t('UserPreferencesModal:User Preferences'),
content: UserPreferences,
contentProps: {
hotkeyDefaults: hotkeysManager.getValidHotkeyDefinitions(
hotkeyDefaults
),
hotkeyDefaults: hotkeysManager.getValidHotkeyDefinitions(hotkeyDefaults),
hotkeyDefinitions,
currentLanguage: currentLanguage(),
availableLanguages,
@@ -134,9 +125,7 @@ function ViewerLayout({
title: t('Header:Logout'),
icon: 'power-off',
onClick: async () => {
navigate(
`/logout?redirect_uri=${encodeURIComponent(window.location.href)}`
);
navigate(`/logout?redirect_uri=${encodeURIComponent(window.location.href)}`);
},
});
}
@@ -234,13 +223,11 @@ function ViewerLayout({
</ErrorBoundary>
</Header>
<div
className="bg-black flex flex-row items-stretch w-full overflow-hidden flex-nowrap relative"
className="relative flex w-full flex-row flex-nowrap items-stretch overflow-hidden bg-black"
style={{ height: 'calc(100vh - 52px' }}
>
<React.Fragment>
{showLoadingIndicator && (
<LoadingIndicatorProgress className="h-full w-full bg-black" />
)}
{showLoadingIndicator && <LoadingIndicatorProgress className="h-full w-full bg-black" />}
{/* LEFT SIDEPANELS */}
{leftPanelComponents.length ? (
<ErrorBoundary context="Left Panel">
@@ -253,8 +240,8 @@ function ViewerLayout({
</ErrorBoundary>
) : null}
{/* TOOLBAR + GRID */}
<div className="flex flex-col flex-1 h-full">
<div className="flex items-center justify-center flex-1 h-full overflow-hidden bg-black relative">
<div className="flex h-full flex-1 flex-col">
<div className="relative flex h-full flex-1 items-center justify-center overflow-hidden bg-black">
<ErrorBoundary context="Grid">
<ViewportGridComp
servicesManager={servicesManager}
+39 -103
View File
@@ -1,9 +1,6 @@
import { ServicesManager, utils, Types } from '@ohif/core';
import {
ContextMenuController,
defaultContextMenu,
} from './CustomizableContextMenu';
import { ContextMenuController, defaultContextMenu } from './CustomizableContextMenu';
import DicomTagBrowser from './DicomTagBrowser/DicomTagBrowser';
import reuseCachedLayouts from './utils/reuseCachedLayouts';
import findViewportsByPosition, {
@@ -35,8 +32,7 @@ export type UpdateViewportDisplaySetParams = {
*/
const isHangingProtocolCommand = command =>
command &&
(command.commandName === 'setHangingProtocol' ||
command.commandName === 'toggleHangingProtocol');
(command.commandName === 'setHangingProtocol' || command.commandName === 'toggleHangingProtocol');
const commandsModule = ({
servicesManager,
@@ -54,10 +50,7 @@ const commandsModule = ({
} = (servicesManager as ServicesManager).services;
// Define a context menu controller for use with any context menus
const contextMenuController = new ContextMenuController(
servicesManager,
commandsManager
);
const contextMenuController = new ContextMenuController(servicesManager, commandsManager);
const actions = {
/**
@@ -95,11 +88,7 @@ const commandsModule = ({
...selectorProps,
};
contextMenuController.showContextMenu(
optionsToUse,
element,
defaultPointsPosition
);
contextMenuController.showContextMenu(optionsToUse, element, defaultPointsPosition);
},
/** Close a context menu currently displayed */
@@ -187,19 +176,10 @@ const commandsModule = ({
// the activeViewportId
const state = viewportGridService.getState();
const hpInfo = hangingProtocolService.getState();
const {
protocol: oldProtocol,
} = hangingProtocolService.getActiveProtocol();
const stateSyncReduce = reuseCachedLayouts(
state,
hangingProtocolService,
stateSyncService
);
const {
hangingProtocolStageIndexMap,
viewportGridStore,
displaySetSelectorMap,
} = stateSyncReduce;
const { protocol: oldProtocol } = hangingProtocolService.getActiveProtocol();
const stateSyncReduce = reuseCachedLayouts(state, hangingProtocolService, stateSyncService);
const { hangingProtocolStageIndexMap, viewportGridStore, displaySetSelectorMap } =
stateSyncReduce;
if (!protocolId) {
// Re-use the previous protocol id, and optionally stage
@@ -209,8 +189,7 @@ const commandsModule = ({
}
} else if (stageIndex === undefined && stageId === undefined) {
// Re-set the same stage as was previously used
const hangingId = `${activeStudyUID ||
hpInfo.activeStudyUID}:${protocolId}`;
const hangingId = `${activeStudyUID || hpInfo.activeStudyUID}:${protocolId}`;
stageIndex = hangingProtocolStageIndexMap[hangingId]?.stageIndex;
}
@@ -225,9 +204,9 @@ const commandsModule = ({
hangingProtocolService.setActiveStudyUID(activeStudyUID);
}
const storedHanging = `${
hangingProtocolService.getState().activeStudyUID
}:${protocolId}:${useStageIdx || 0}`;
const storedHanging = `${hangingProtocolService.getState().activeStudyUID}:${protocolId}:${
useStageIdx || 0
}`;
const restoreProtocol = !reset && viewportGridStore[storedHanging];
@@ -285,19 +264,14 @@ const commandsModule = ({
}
},
toggleHangingProtocol: ({
protocolId,
stageIndex,
}: HangingProtocolParams): boolean => {
toggleHangingProtocol: ({ protocolId, stageIndex }: HangingProtocolParams): boolean => {
const {
protocol,
stageIndex: desiredStageIndex,
activeStudy,
} = hangingProtocolService.getActiveProtocol();
const { toggleHangingProtocol } = stateSyncService.getState();
const storedHanging = `${
activeStudy.StudyInstanceUID
}:${protocolId}:${stageIndex | 0}`;
const storedHanging = `${activeStudy.StudyInstanceUID}:${protocolId}:${stageIndex | 0}`;
if (
protocol.id === protocolId &&
(stageIndex === undefined || stageIndex === desiredStageIndex)
@@ -326,10 +300,7 @@ const commandsModule = ({
},
deltaStage: ({ direction }) => {
const {
protocolId,
stageIndex: oldStageIndex,
} = hangingProtocolService.getState();
const { protocolId, stageIndex: oldStageIndex } = hangingProtocolService.getState();
const { protocol } = hangingProtocolService.getActiveProtocol();
for (
let stageIndex = oldStageIndex + direction;
@@ -358,23 +329,14 @@ const commandsModule = ({
const { protocol } = hangingProtocolService.getActiveProtocol();
const onLayoutChange = protocol.callbacks?.onLayoutChange;
if (commandsManager.run(onLayoutChange, { numRows, numCols }) === false) {
console.log(
'setViewportGridLayout running',
onLayoutChange,
numRows,
numCols
);
console.log('setViewportGridLayout running', onLayoutChange, numRows, numCols);
// Don't apply the layout if the run command returns false
return;
}
const completeLayout = () => {
const state = viewportGridService.getState();
const stateReduce = findViewportsByPosition(
state,
{ numRows, numCols },
stateSyncService
);
const stateReduce = findViewportsByPosition(state, { numRows, numCols }, stateSyncService);
const findOrCreateViewport = layoutFindOrCreate.bind(
null,
hangingProtocolService,
@@ -395,11 +357,8 @@ const commandsModule = ({
toggleOneUp() {
const viewportGridState = viewportGridService.getState();
const { activeViewportId, viewports, layout } = viewportGridState;
const {
displaySetInstanceUIDs,
displaySetOptions,
viewportOptions,
} = viewports.get(activeViewportId);
const { displaySetInstanceUIDs, displaySetOptions, viewportOptions } =
viewports.get(activeViewportId);
if (layout.numCols === 1 && layout.numRows === 1) {
// The viewer is in one-up. Check if there is a state to restore/toggle back to.
@@ -410,8 +369,7 @@ const commandsModule = ({
}
// There is a state to toggle back to. The viewport that was
// originally toggled to one up was the former active viewport.
const viewportIdToUpdate =
toggleOneUpViewportGridStore.activeViewportId;
const viewportIdToUpdate = toggleOneUpViewportGridStore.activeViewportId;
const updatedViewportsViaHP =
displaySetInstanceUIDs.length > 1
@@ -436,23 +394,22 @@ const commandsModule = ({
// the current one up viewport might have a new displaySet dragged and dropped on it
// so we should prioritize the current one in the old grid store layout viewports
const newViewports = Array.from(
toggleOneUpViewportGridStore.viewports.values()
).map(viewport => {
if (viewport.viewportId === currentOneUpViewport.viewportId) {
return {
...currentOneUpViewport,
};
}
const newViewports = Array.from(toggleOneUpViewportGridStore.viewports.values()).map(
viewport => {
if (viewport.viewportId === currentOneUpViewport.viewportId) {
return {
...currentOneUpViewport,
};
}
return viewport;
});
return viewport;
}
);
// However, we also need to take into account that the current one up viewport
// might have been part of a bigger hanging protocol layout, so going back
// from one up we should apply those viewports as well.
return updatedViewportsViaHP.length > 1 &&
updatedViewportsViaHP[position]
return updatedViewportsViaHP.length > 1 && updatedViewportsViaHP[position]
? {
viewportOptions,
displaySetOptions,
@@ -509,10 +466,7 @@ const commandsModule = ({
});
};
subscribeToNextViewportGridChange(
viewportGridService,
clearToggleOneUpViewportGridStore
);
subscribeToNextViewportGridChange(viewportGridService, clearToggleOneUpViewportGridStore);
}
},
@@ -574,8 +528,7 @@ const commandsModule = ({
const { activeViewportId, viewports } = viewportGridService.getState();
const activeViewport = viewports.get(activeViewportId);
const activeDisplaySetInstanceUID =
activeViewport.displaySetInstanceUIDs[0];
const activeDisplaySetInstanceUID = activeViewport.displaySetInstanceUIDs[0];
const thumbnailList = document.querySelector('#ohif-thumbnail-list');
@@ -585,9 +538,7 @@ const commandsModule = ({
const thumbnailListBounds = thumbnailList.getBoundingClientRect();
const thumbnail = document.querySelector(
`#thumbnail-${activeDisplaySetInstanceUID}`
);
const thumbnail = document.querySelector(`#thumbnail-${activeDisplaySetInstanceUID}`);
if (!thumbnail) {
return;
@@ -610,14 +561,7 @@ const commandsModule = ({
direction,
excludeNonImageModalities,
}: UpdateViewportDisplaySetParams) => {
const nonImageModalities = [
'SR',
'SEG',
'SM',
'RTSTRUCT',
'RTPLAN',
'RTDOSE',
];
const nonImageModalities = ['SR', 'SEG', 'SM', 'RTSTRUCT', 'RTPLAN', 'RTDOSE'];
// Sort the display sets as per the hanging protocol service viewport/display set scoring system.
// The thumbnail list uses the same sorting.
@@ -638,30 +582,22 @@ const commandsModule = ({
for (
displaySetIndexToShow = activeDisplaySetIndex + direction;
displaySetIndexToShow > -1 &&
displaySetIndexToShow < currentDisplaySets.length;
displaySetIndexToShow > -1 && displaySetIndexToShow < currentDisplaySets.length;
displaySetIndexToShow += direction
) {
if (
!excludeNonImageModalities ||
!nonImageModalities.includes(
currentDisplaySets[displaySetIndexToShow].Modality
)
!nonImageModalities.includes(currentDisplaySets[displaySetIndexToShow].Modality)
) {
break;
}
}
if (
displaySetIndexToShow < 0 ||
displaySetIndexToShow >= currentDisplaySets.length
) {
if (displaySetIndexToShow < 0 || displaySetIndexToShow >= currentDisplaySets.length) {
return;
}
const { displaySetInstanceUID } = currentDisplaySets[
displaySetIndexToShow
];
const { displaySetInstanceUID } = currentDisplaySets[displaySetIndexToShow];
let updatedViewports = [];
@@ -32,21 +32,13 @@ export const findOrCreateViewport = (
options.inDisplay = [...viewportsByPosition.initialInDisplay];
}
// See if there is a default viewport for new views.
const missing = hangingProtocolService.getMissingViewport(
protocolId,
stageIndex,
options
);
const missing = hangingProtocolService.getMissingViewport(protocolId, stageIndex, options);
if (missing) {
const displaySetInstanceUIDs = missing.displaySetsInfo.map(
it => it.displaySetInstanceUID
);
const displaySetInstanceUIDs = missing.displaySetsInfo.map(it => it.displaySetInstanceUID);
options.inDisplay.push(...displaySetInstanceUIDs);
return {
displaySetInstanceUIDs,
displaySetOptions: missing.displaySetsInfo.map(
it => it.displaySetOptions
),
displaySetOptions: missing.displaySetsInfo.map(it => it.displaySetOptions),
viewportOptions: {
...missing.viewportOptions,
},
@@ -13,10 +13,7 @@ import { GoogleCloudDataSourceConfigurationAPI } from './DataSourceConfiguration
* custom page for the user to view their profile, or to add a custom
* page for login etc.
*/
export default function getCustomizationModule({
servicesManager,
extensionManager,
}) {
export default function getCustomizationModule({ servicesManager, extensionManager }) {
return [
{
name: 'helloPage',
@@ -25,9 +22,7 @@ export default function getCustomizationModule({
routes: [
{
path: '/custom',
children: () => (
<h1 style={{ color: 'white' }}>Hello Custom Route</h1>
),
children: () => <h1 style={{ color: 'white' }}>Hello Custom Route</h1>,
},
],
},
@@ -88,7 +83,7 @@ export default function getCustomizationModule({
*/
{
id: 'ohif.overlayItem',
content: function(props) {
content: function (props) {
if (this.condition && !this.condition(props)) {
return null;
}
@@ -110,9 +105,7 @@ export default function getCustomizationModule({
style={{ color: this.color || undefined }}
title={this.title || ''}
>
{this.label && (
<span className="mr-1 shrink-0">{this.label}</span>
)}
{this.label && <span className="mr-1 shrink-0">{this.label}</span>}
<span className="font-light">{value}</span>
</span>
);
@@ -126,7 +119,7 @@ export default function getCustomizationModule({
* This function clones the object and child objects to prevent
* changes to the original customization object.
*/
transform: function(customizationService: CustomizationService) {
transform: function (customizationService: CustomizationService) {
// Don't modify the children, as those are copied by reference
const clonedObject = { ...this };
clonedObject.menus = this.menus.map(menu => ({ ...menu }));
@@ -30,10 +30,7 @@ export default function getDisplaySetMessages(
const isMultiframe = firstInstance.NumberOfFrames > 1;
// Can't reconstruct if all instances don't have the ImagePositionPatient.
if (
!isMultiframe &&
!instances.every(instance => instance.ImagePositionPatient)
) {
if (!isMultiframe && !instances.every(instance => instance.ImagePositionPatient)) {
messages.addMessage(DisplaySetMessage.CODES.NO_POSITION_INFORMATION);
}
@@ -5,12 +5,7 @@ import ViewerLayout from './ViewerLayout';
- Init layout based on the displaySets and the objects.
*/
export default function({
servicesManager,
extensionManager,
commandsManager,
hotkeysManager,
}) {
export default function ({ servicesManager, extensionManager, commandsManager, hotkeysManager }) {
function ViewerLayoutWithServices(props) {
return ViewerLayout({
servicesManager,
@@ -1,15 +1,10 @@
import OHIF from '@ohif/core';
import {
InstanceMetadata,
PhilipsPETPrivateGroup,
} from '@cornerstonejs/calculate-suv/src/types';
import { InstanceMetadata, PhilipsPETPrivateGroup } from '@cornerstonejs/calculate-suv/src/types';
const metadataProvider = OHIF.classes.MetadataProvider;
export default function getPTImageIdInstanceMetadata(
imageId: string
): InstanceMetadata {
export default function getPTImageIdInstanceMetadata(imageId: string): InstanceMetadata {
const dicomMetaData = metadataProvider.get('instance', imageId);
if (!dicomMetaData) {
@@ -22,17 +17,15 @@ export default function getPTImageIdInstanceMetadata(
dicomMetaData.CorrectedImage === undefined ||
dicomMetaData.Units === undefined ||
!dicomMetaData.RadiopharmaceuticalInformationSequence ||
dicomMetaData.RadiopharmaceuticalInformationSequence[0]
.RadionuclideHalfLife === undefined ||
dicomMetaData.RadiopharmaceuticalInformationSequence[0]
.RadionuclideTotalDose === undefined ||
dicomMetaData.RadiopharmaceuticalInformationSequence[0].RadionuclideHalfLife === undefined ||
dicomMetaData.RadiopharmaceuticalInformationSequence[0].RadionuclideTotalDose === undefined ||
dicomMetaData.DecayCorrection === undefined ||
dicomMetaData.AcquisitionDate === undefined ||
dicomMetaData.AcquisitionTime === undefined ||
(dicomMetaData.RadiopharmaceuticalInformationSequence[0]
.RadiopharmaceuticalStartDateTime === undefined &&
dicomMetaData.RadiopharmaceuticalInformationSequence[0]
.RadiopharmaceuticalStartTime === undefined)
(dicomMetaData.RadiopharmaceuticalInformationSequence[0].RadiopharmaceuticalStartDateTime ===
undefined &&
dicomMetaData.RadiopharmaceuticalInformationSequence[0].RadiopharmaceuticalStartTime ===
undefined)
) {
throw new Error('required metadata are missing');
}
@@ -45,17 +38,13 @@ export default function getPTImageIdInstanceMetadata(
CorrectedImage: dicomMetaData.CorrectedImage,
Units: dicomMetaData.Units,
RadionuclideHalfLife:
dicomMetaData.RadiopharmaceuticalInformationSequence[0]
.RadionuclideHalfLife,
dicomMetaData.RadiopharmaceuticalInformationSequence[0].RadionuclideHalfLife,
RadionuclideTotalDose:
dicomMetaData.RadiopharmaceuticalInformationSequence[0]
.RadionuclideTotalDose,
dicomMetaData.RadiopharmaceuticalInformationSequence[0].RadionuclideTotalDose,
RadiopharmaceuticalStartDateTime:
dicomMetaData.RadiopharmaceuticalInformationSequence[0]
.RadiopharmaceuticalStartDateTime,
dicomMetaData.RadiopharmaceuticalInformationSequence[0].RadiopharmaceuticalStartDateTime,
RadiopharmaceuticalStartTime:
dicomMetaData.RadiopharmaceuticalInformationSequence[0]
.RadiopharmaceuticalStartTime,
dicomMetaData.RadiopharmaceuticalInformationSequence[0].RadiopharmaceuticalStartTime,
DecayCorrection: dicomMetaData.DecayCorrection,
PatientWeight: dicomMetaData.PatientWeight,
SeriesDate: dicomMetaData.SeriesDate,
@@ -81,17 +70,11 @@ export default function getPTImageIdInstanceMetadata(
instanceMetadata.GEPrivatePostInjectionDateTime = dicomMetaData['0009100d'];
}
if (
dicomMetaData.FrameReferenceTime &&
dicomMetaData.FrameReferenceTime !== undefined
) {
if (dicomMetaData.FrameReferenceTime && dicomMetaData.FrameReferenceTime !== undefined) {
instanceMetadata.FrameReferenceTime = dicomMetaData.FrameReferenceTime;
}
if (
dicomMetaData.ActualFrameDuration &&
dicomMetaData.ActualFrameDuration !== undefined
) {
if (dicomMetaData.ActualFrameDuration && dicomMetaData.ActualFrameDuration !== undefined) {
instanceMetadata.ActualFrameDuration = dicomMetaData.ActualFrameDuration;
}
@@ -111,10 +94,7 @@ function convertInterfaceTimeToString(time): string {
const minutes = `${time.minutes || '00'}`.padStart(2, '0');
const seconds = `${time.seconds || '00'}`.padStart(2, '0');
const fractionalSeconds = `${time.fractionalSeconds || '000000'}`.padEnd(
6,
'0'
);
const fractionalSeconds = `${time.fractionalSeconds || '000000'}`.padEnd(6, '0');
const timeString = `${hours}${minutes}${seconds}.${fractionalSeconds}`;
return timeString;
+1 -5
View File
@@ -6,11 +6,7 @@ import { WrappedPanelStudyBrowser, PanelMeasurementTable } from './Panels';
// - cancel promises when component is destroyed
// - show errors in UI for thumbnails if promise fails
function getPanelModule({
commandsManager,
extensionManager,
servicesManager,
}) {
function getPanelModule({ commandsManager, extensionManager, servicesManager }) {
const wrappedMeasurementPanel = () => {
return (
<PanelMeasurementTable
@@ -16,10 +16,8 @@ const makeDisplaySet = instances => {
const instance = instances[0];
const imageSet = new ImageSet(instances);
const {
value: isReconstructable,
averageSpacingBetweenFrames,
} = isDisplaySetReconstructable(instances);
const { value: isReconstructable, averageSpacingBetweenFrames } =
isDisplaySetReconstructable(instances);
// set appropriate attributes to image set...
const messages = getDisplaySetMessages(instances, isReconstructable);
@@ -48,9 +46,7 @@ const makeDisplaySet = instances => {
if (shallSort) {
imageSet.sortBy((a, b) => {
// Sort by InstanceNumber (0020,0013)
return (
(parseInt(a.InstanceNumber) || 0) - (parseInt(b.InstanceNumber) || 0)
);
return (parseInt(a.InstanceNumber) || 0) - (parseInt(b.InstanceNumber) || 0);
});
}
+1 -6
View File
@@ -163,12 +163,7 @@ const hpMNCompare: Types.HangingProtocol.Protocol = {
columns: 2,
},
},
viewports: [
currentViewport0,
priorViewport0,
currentViewport1,
priorViewport1,
],
viewports: [currentViewport0, priorViewport0, currentViewport1, priorViewport1],
},
{
+1 -4
View File
@@ -11,10 +11,7 @@ import getStudiesForPatientByMRN from './Panels/getStudiesForPatientByMRN';
import getCustomizationModule from './getCustomizationModule';
import { id } from './id.js';
import preRegistration from './init';
import {
ContextMenuController,
CustomizableContextMenuTypes,
} from './CustomizableContextMenu';
import { ContextMenuController, CustomizableContextMenuTypes } from './CustomizableContextMenu';
import * as dicomWebUtils from './DicomWebDataSource/utils';
const defaultExtension: Types.Extensions.Extension = {
+4 -17
View File
@@ -13,17 +13,11 @@ const metadataProvider = classes.MetadataProvider;
export default function init({ servicesManager, configuration = {} }): void {
const { stateSyncService } = servicesManager.services;
// Add
DicomMetadataStore.subscribe(
DicomMetadataStore.EVENTS.INSTANCES_ADDED,
handlePETImageMetadata
);
DicomMetadataStore.subscribe(DicomMetadataStore.EVENTS.INSTANCES_ADDED, handlePETImageMetadata);
// If the metadata for PET has changed by the user (e.g. manually changing the PatientWeight)
// we need to recalculate the SUV Scaling Factors
DicomMetadataStore.subscribe(
DicomMetadataStore.EVENTS.SERIES_UPDATED,
handlePETImageMetadata
);
DicomMetadataStore.subscribe(DicomMetadataStore.EVENTS.SERIES_UPDATED, handlePETImageMetadata);
// viewportGridStore is a sync state which stores the entire
// ViewportGridService getState, by the keys `<activeStudyUID>:<protocolId>:<stageIndex>`
@@ -55,10 +49,7 @@ export default function init({ servicesManager, configuration = {} }): void {
}
const handlePETImageMetadata = ({ SeriesInstanceUID, StudyInstanceUID }) => {
const { instances } = DicomMetadataStore.getSeries(
StudyInstanceUID,
SeriesInstanceUID
);
const { instances } = DicomMetadataStore.getSeries(StudyInstanceUID, SeriesInstanceUID);
const modality = instances[0].Modality;
if (modality !== 'PT') {
@@ -90,10 +81,6 @@ const handlePETImageMetadata = ({ SeriesInstanceUID, StudyInstanceUID }) => {
}
instanceMetadataArray.forEach((instanceMetadata, index) => {
metadataProvider.addCustomMetadata(
imageIds[index],
'scalingModule',
suvScalingFactors[index]
);
metadataProvider.addCustomMetadata(imageIds[index], 'scalingModule', suvScalingFactors[index]);
});
};
@@ -19,20 +19,12 @@ export default function findSRWithSameSeriesDescription(
): Types.SeriesMetadata {
const activeDisplaySets = displaySetService.getActiveDisplaySets();
const srDisplaySets = activeDisplaySets.filter(ds => ds.Modality === 'SR');
const sameSeries = srDisplaySets.find(
ds => ds.SeriesDescription === SeriesDescription
);
const sameSeries = srDisplaySets.find(ds => ds.SeriesDescription === SeriesDescription);
if (sameSeries) {
console.log('Storing to same series', sameSeries);
const { instance } = sameSeries;
const {
SeriesInstanceUID,
SeriesDescription,
SeriesDate,
SeriesTime,
SeriesNumber,
Modality,
} = instance;
const { SeriesInstanceUID, SeriesDescription, SeriesDate, SeriesTime, SeriesNumber, Modality } =
instance;
return {
SeriesInstanceUID,
SeriesDescription,
+3 -9
View File
@@ -35,15 +35,10 @@ const getDirectURL = (config, params) => {
value.DirectRetrieveURL = URL.createObjectURL(blob);
return value.DirectRetrieveURL;
}
if (
!singlepart ||
(singlepart !== true && singlepart.indexOf(fetchPart) === -1)
) {
if (!singlepart || (singlepart !== true && singlepart.indexOf(fetchPart) === -1)) {
if (value.retrieveBulkData) {
return value.retrieveBulkData().then(arr => {
value.DirectRetrieveURL = URL.createObjectURL(
new Blob([arr], { type: defaultType })
);
value.DirectRetrieveURL = URL.createObjectURL(new Blob([arr], { type: defaultType }));
return value.DirectRetrieveURL;
});
}
@@ -58,8 +53,7 @@ const getDirectURL = (config, params) => {
const hasQuery = BulkDataURI.indexOf('?') !== -1;
const hasAccept = BulkDataURI.indexOf('accept=') !== -1;
const acceptUri =
BulkDataURI +
(hasAccept ? '' : (hasQuery ? '&' : '?') + `accept=${defaultType}`);
BulkDataURI + (hasAccept ? '' : (hasQuery ? '&' : '?') + `accept=${defaultType}`);
if (tag === 'PixelData' || tag === 'EncapsulatedDocument') {
return `${wadoRoot}/studies/${StudyInstanceUID}/series/${SeriesInstanceUID}/instances/${SOPInstanceUID}/rendered`;
@@ -57,14 +57,13 @@ const reuseCachedLayout = (
continue;
}
if (viewportId === activeViewportId && i === 0) {
displaySetSelectorMap[
`${activeStudyUID}:activeDisplaySet:0`
] = displaySetUID;
displaySetSelectorMap[`${activeStudyUID}:activeDisplaySet:0`] = displaySetUID;
}
if (displaySetOptions[i]?.id) {
displaySetSelectorMap[
`${activeStudyUID}:${displaySetOptions[i].id}:${displaySetOptions[i]
.matchedDisplaySetsIndex || 0}`
`${activeStudyUID}:${displaySetOptions[i].id}:${
displaySetOptions[i].matchedDisplaySetsIndex || 0
}`
] = displaySetUID;
}
}
@@ -5,9 +5,7 @@ import toNumber from '@ohif/core/src/utils/toNumber';
* @param {*} instances
* @returns
*/
export default function areAllImageComponentsEqual(
instances: Array<any>
): boolean {
export default function areAllImageComponentsEqual(instances: Array<any>): boolean {
if (!instances?.length) {
return false;
}
@@ -5,9 +5,7 @@ import toNumber from '@ohif/core/src/utils/toNumber';
* @param {*} instances
* @returns
*/
export default function areAllImageDimensionsEqual(
instances: Array<any>
): boolean {
export default function areAllImageDimensionsEqual(instances: Array<any>): boolean {
if (!instances?.length) {
return false;
}
@@ -6,24 +6,18 @@ import { _isSameOrientation } from '@ohif/core/src/utils/isDisplaySetReconstruct
* @param {*} instances
* @returns
*/
export default function areAllImageOrientationsEqual(
instances: Array<any>
): boolean {
export default function areAllImageOrientationsEqual(instances: Array<any>): boolean {
if (!instances?.length) {
return false;
}
const firstImage = instances[0];
const firstImageOrientationPatient = toNumber(
firstImage.ImageOrientationPatient
);
const firstImageOrientationPatient = toNumber(firstImage.ImageOrientationPatient);
for (let i = 1; i < instances.length; i++) {
const instance = instances[i];
const imageOrientationPatient = toNumber(instance.ImageOrientationPatient);
if (
!_isSameOrientation(imageOrientationPatient, firstImageOrientationPatient)
) {
if (!_isSameOrientation(imageOrientationPatient, firstImageOrientationPatient)) {
return false;
}
}
@@ -25,10 +25,7 @@ function _checkSeriesPositionShift(
scanAxisNormal,
averageSpacingBetweenFrames
);
return (
vec3.distance(actualPosition, predictedPosition) >
averageSpacingBetweenFrames
);
return vec3.distance(actualPosition, predictedPosition) > averageSpacingBetweenFrames;
}
/**
@@ -36,24 +33,17 @@ function _checkSeriesPositionShift(
* @param {*} instances
* @returns
*/
export default function areAllImagePositionsEqual(
instances: Array<any>
): boolean {
export default function areAllImagePositionsEqual(instances: Array<any>): boolean {
if (!instances?.length) {
return false;
}
const firstImageOrientationPatient = toNumber(
instances[0].ImageOrientationPatient
);
const firstImageOrientationPatient = toNumber(instances[0].ImageOrientationPatient);
const scanAxisNormal = calculateScanAxisNormal(firstImageOrientationPatient);
const firstImagePositionPatient = toNumber(instances[0].ImagePositionPatient);
const lastIpp = toNumber(
instances[instances.length - 1].ImagePositionPatient
);
const lastIpp = toNumber(instances[instances.length - 1].ImagePositionPatient);
const averageSpacingBetweenFrames =
_getPerpendicularDistance(firstImagePositionPatient, lastIpp) /
(instances.length - 1);
_getPerpendicularDistance(firstImagePositionPatient, lastIpp) / (instances.length - 1);
let previousImagePositionPatient = firstImagePositionPatient;
for (let i = 1; i < instances.length; i++) {
@@ -20,13 +20,10 @@ export default function areAllImageSpacingEqual(
return;
}
const firstImagePositionPatient = toNumber(instances[0].ImagePositionPatient);
const lastIpp = toNumber(
instances[instances.length - 1].ImagePositionPatient
);
const lastIpp = toNumber(instances[instances.length - 1].ImagePositionPatient);
const averageSpacingBetweenFrames =
_getPerpendicularDistance(firstImagePositionPatient, lastIpp) /
(instances.length - 1);
_getPerpendicularDistance(firstImagePositionPatient, lastIpp) / (instances.length - 1);
let previousImagePositionPatient = firstImagePositionPatient;
@@ -40,10 +37,7 @@ export default function areAllImageSpacingEqual(
previousImagePositionPatient
);
const spacingIssue = _getSpacingIssue(
spacingBetweenFrames,
averageSpacingBetweenFrames
);
const spacingIssue = _getSpacingIssue(spacingBetweenFrames, averageSpacingBetweenFrames);
if (spacingIssue) {
const issue = spacingIssue.issue;
@@ -10,14 +10,9 @@ import { DisplaySetMessage, DisplaySetMessageList } from '@ohif/core';
* @param {*} multiFrameInstance
* @param {*} warnings
*/
export default function checkMultiFrame(
multiFrameInstance,
messages: DisplaySetMessageList
): void {
export default function checkMultiFrame(multiFrameInstance, messages: DisplaySetMessageList): void {
if (!hasPixelMeasurements(multiFrameInstance)) {
messages.addMessage(
DisplaySetMessage.CODES.MULTIFRAME_NO_PIXEL_MEASUREMENTS
);
messages.addMessage(DisplaySetMessage.CODES.MULTIFRAME_NO_PIXEL_MEASUREMENTS);
}
if (!hasOrientation(multiFrameInstance)) {
@@ -25,8 +20,6 @@ export default function checkMultiFrame(
}
if (!hasPosition(multiFrameInstance)) {
messages.addMessage(
DisplaySetMessage.CODES.MULTIFRAME_NO_POSITION_INFORMATION
);
messages.addMessage(DisplaySetMessage.CODES.MULTIFRAME_NO_POSITION_INFORMATION);
}
}
@@ -28,9 +28,7 @@ export default function checkSingleFrames(
}
if (!areAllImagePositionsEqual(instances)) {
messages.addMessage(
DisplaySetMessage.CODES.INCONSISTENT_POSITION_INFORMATION
);
messages.addMessage(DisplaySetMessage.CODES.INCONSISTENT_POSITION_INFORMATION);
}
areAllImageSpacingEqual(instances, messages);
}