Merge branch 'feat/v2-main' into dannyrb/ohif-154/restore-command-functionality-for-toolbar
This commit is contained in:
45 files changed
+1055
-197
No files matched your search
+19
-16
@@ -1,12 +1,13 @@
|
||||
// External
|
||||
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 {
|
||||
DialogProvider,
|
||||
Modal,
|
||||
ModalProvider,
|
||||
Notification,
|
||||
SnackbarProvider,
|
||||
ThemeWrapper,
|
||||
ViewportDialogProvider,
|
||||
@@ -59,21 +60,23 @@ function App({ config, defaultExtensions }) {
|
||||
|
||||
return (
|
||||
<AppConfigProvider value={appConfigState}>
|
||||
<Router basename={routerBasename}>
|
||||
<ThemeWrapper>
|
||||
<ViewportGridProvider service={ViewportGridService}>
|
||||
<ViewportDialogProvider service={UIViewportDialogService}>
|
||||
<SnackbarProvider service={UINotificationService}>
|
||||
<DialogProvider service={UIDialogService}>
|
||||
<ModalProvider modal={Modal} service={UIModalService}>
|
||||
{appRoutes}
|
||||
</ModalProvider>
|
||||
</DialogProvider>
|
||||
</SnackbarProvider>
|
||||
</ViewportDialogProvider>
|
||||
</ViewportGridProvider>
|
||||
</ThemeWrapper>
|
||||
</Router>
|
||||
<I18nextProvider i18n={i18n}>
|
||||
<Router basename={routerBasename}>
|
||||
<ThemeWrapper>
|
||||
<ViewportGridProvider service={ViewportGridService}>
|
||||
<ViewportDialogProvider service={UIViewportDialogService}>
|
||||
<SnackbarProvider service={UINotificationService}>
|
||||
<DialogProvider service={UIDialogService}>
|
||||
<ModalProvider modal={Modal} service={UIModalService}>
|
||||
{appRoutes}
|
||||
</ModalProvider>
|
||||
</DialogProvider>
|
||||
</SnackbarProvider>
|
||||
</ViewportDialogProvider>
|
||||
</ViewportGridProvider>
|
||||
</ThemeWrapper>
|
||||
</Router>
|
||||
</I18nextProvider>
|
||||
</AppConfigProvider>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
import React from 'react';
|
||||
|
||||
import { Dropdown, IconButton, Icon, useModal } from '@ohif/ui';
|
||||
|
||||
const PreferencesDropdown = () => {
|
||||
const { show } = useModal();
|
||||
|
||||
const showAboutModal = () => {
|
||||
const modalComponent = () => <div>About modal</div>;
|
||||
show({
|
||||
content: modalComponent,
|
||||
title: 'About',
|
||||
});
|
||||
};
|
||||
|
||||
const showPreferencesModal = () => {
|
||||
const modalComponent = () => <div>Preferences modal</div>;
|
||||
show({
|
||||
content: modalComponent,
|
||||
title: 'Preferences',
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Dropdown
|
||||
showDropdownIcon={false}
|
||||
list={[
|
||||
{ title: 'About', icon: 'info', onClick: showAboutModal },
|
||||
{
|
||||
title: 'Preferences',
|
||||
icon: 'settings',
|
||||
onClick: showPreferencesModal,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<IconButton
|
||||
variant="text"
|
||||
color="inherit"
|
||||
size="initial"
|
||||
className="text-primary-active"
|
||||
>
|
||||
<Icon name="settings" />
|
||||
</IconButton>
|
||||
<IconButton
|
||||
variant="text"
|
||||
color="inherit"
|
||||
size="initial"
|
||||
className="text-primary-active"
|
||||
onClick={() => {}}
|
||||
>
|
||||
<Icon name="chevron-down" />
|
||||
</IconButton>
|
||||
</Dropdown>
|
||||
);
|
||||
};
|
||||
|
||||
export default PreferencesDropdown;
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from './PreferencesDropdown';
|
||||
@@ -1,10 +1,12 @@
|
||||
/**
|
||||
* CSS Grid Reference: http://grid.malven.co/
|
||||
*/
|
||||
import React from 'react';
|
||||
import React, { useEffect } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { ViewportGrid, ViewportPane, useViewportGrid } from '@ohif/ui';
|
||||
import EmptyViewport from './EmptyViewport';
|
||||
import { classes } from '@ohif/core';
|
||||
const { ImageSet } = classes;
|
||||
|
||||
function ViewerViewportGrid(props) {
|
||||
const { servicesManager, viewportComponents, dataSource } = props;
|
||||
@@ -20,6 +22,29 @@ function ViewerViewportGrid(props) {
|
||||
// TODO -> Need some way of selecting which displaySets hit the viewports.
|
||||
const { DisplaySetService } = servicesManager.services;
|
||||
|
||||
useEffect(() => {
|
||||
const { unsubscribe } = DisplaySetService.subscribe(
|
||||
DisplaySetService.EVENTS.DISPLAY_SETS_CHANGED,
|
||||
displaySets => {
|
||||
displaySets.sort((a, b) => {
|
||||
const isImageSet = x => x instanceof ImageSet;
|
||||
return (isImageSet(a) === isImageSet(b)) ? 0 : isImageSet(a) ? -1 : 1;
|
||||
});
|
||||
dispatch({
|
||||
type: 'SET_DISPLAYSET_FOR_VIEWPORT',
|
||||
payload: {
|
||||
viewportIndex: 0,
|
||||
displaySetInstanceUID: displaySets[0].displaySetInstanceUID,
|
||||
},
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
return () => {
|
||||
unsubscribe();
|
||||
};
|
||||
}, []);
|
||||
|
||||
// TODO -> Make a HangingProtocolService
|
||||
const HangingProtocolService = displaySets => {
|
||||
let displaySetInstanceUID;
|
||||
|
||||
@@ -46,14 +46,17 @@ function DataSourceWrapper(props) {
|
||||
// studies.processResults --> <LayoutTemplate studies={} />
|
||||
// But only for LayoutTemplate type of 'list'?
|
||||
// Or no data fetching here, and just hand down my source
|
||||
const [data, setData] = useState();
|
||||
const [data, setData] = useState([]);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
useEffect(() => {
|
||||
// 204: no content
|
||||
async function getData() {
|
||||
setIsLoading(true);
|
||||
const searchResults = await dataSource.query.studies.search(
|
||||
queryFilterValues
|
||||
);
|
||||
setData(searchResults);
|
||||
setIsLoading(false);
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -61,23 +64,19 @@ function DataSourceWrapper(props) {
|
||||
} catch (ex) {
|
||||
console.warn(ex);
|
||||
}
|
||||
console.log('DataSourceWrapper: useEffect');
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [history.location.search]);
|
||||
// queryFilterValues
|
||||
|
||||
// TODO: Better way to pass DataSource?
|
||||
return (
|
||||
<React.Fragment>
|
||||
{data && (
|
||||
<LayoutTemplate
|
||||
{...rest}
|
||||
history={history}
|
||||
data={data}
|
||||
dataSource={dataSource}
|
||||
/>
|
||||
)}
|
||||
</React.Fragment>
|
||||
<LayoutTemplate
|
||||
{...rest}
|
||||
history={history}
|
||||
data={data}
|
||||
dataSource={dataSource}
|
||||
isLoadingData={isLoading}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import React, { Suspense, useState, useEffect } from 'react';
|
||||
import classnames from 'classnames';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Link } from 'react-router-dom';
|
||||
@@ -9,13 +9,14 @@ import filtersMeta from './filtersMeta.js';
|
||||
import { useAppConfig } from '@state';
|
||||
import { useDebounce, useQuery } from '@hooks';
|
||||
|
||||
import PreferencesDropdown from '../../components/PreferencesDropdown';
|
||||
|
||||
import {
|
||||
Icon,
|
||||
StudyListExpandedRow,
|
||||
Button,
|
||||
NavBar,
|
||||
Svg,
|
||||
IconButton,
|
||||
EmptyStudies,
|
||||
StudyListTable,
|
||||
StudyListPagination,
|
||||
@@ -28,15 +29,16 @@ const seriesInStudiesMap = new Map();
|
||||
* TODO:
|
||||
* - debounce `setFilterValues` (150ms?)
|
||||
*/
|
||||
function WorkList({ history, data: studies, dataSource }) {
|
||||
function WorkList({ history, data: studies, isLoadingData, dataSource }) {
|
||||
// ~ Modes
|
||||
const [appConfig] = useAppConfig();
|
||||
// ~ Filters
|
||||
const query = useQuery();
|
||||
const queryFilterValues = _getQueryFilterValues(query);
|
||||
const [filterValues, _setFilterValues] = useState(
|
||||
Object.assign({}, defaultFilterValues, queryFilterValues)
|
||||
);
|
||||
const [filterValues, _setFilterValues] = useState({
|
||||
...defaultFilterValues,
|
||||
...queryFilterValues,
|
||||
});
|
||||
const debouncedFilterValues = useDebounce(filterValues, 200);
|
||||
const { resultsPerPage, pageNumber, sortBy, sortDirection } = filterValues;
|
||||
|
||||
@@ -80,6 +82,7 @@ function WorkList({ history, data: studies, dataSource }) {
|
||||
|
||||
return 0;
|
||||
});
|
||||
|
||||
// ~ Rows & Studies
|
||||
const [expandedRows, setExpandedRows] = useState([]);
|
||||
const [studiesWithSeriesData, setStudiesWithSeriesData] = useState([]);
|
||||
@@ -190,6 +193,7 @@ function WorkList({ history, data: studies, dataSource }) {
|
||||
return filterValues[name] !== defaultFilterValues[name];
|
||||
});
|
||||
};
|
||||
|
||||
const tableDataSource = sortedStudies.map((study, key) => {
|
||||
const rowKey = key + 1;
|
||||
const isExpanded = expandedRows.some(k => k === rowKey);
|
||||
@@ -364,17 +368,7 @@ function WorkList({ history, data: studies, dataSource }) {
|
||||
<span className="mr-3 text-lg text-common-light">
|
||||
FOR INVESTIGATIONAL USE ONLY
|
||||
</span>
|
||||
<IconButton
|
||||
variant="text"
|
||||
color="inherit"
|
||||
className="text-primary-active"
|
||||
onClick={() => {}}
|
||||
>
|
||||
<React.Fragment>
|
||||
<Icon name="settings" />
|
||||
<Icon name="chevron-down" />
|
||||
</React.Fragment>
|
||||
</IconButton>
|
||||
<PreferencesDropdown />
|
||||
</div>
|
||||
</NavBar>
|
||||
<StudyListFilter
|
||||
@@ -404,7 +398,7 @@ function WorkList({ history, data: studies, dataSource }) {
|
||||
</>
|
||||
) : (
|
||||
<div className="flex flex-col items-center justify-center pt-48">
|
||||
<EmptyStudies />
|
||||
<EmptyStudies isLoading={isLoadingData} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
Reference in new issue
Block a user