refactor: StudyListFilter refactor (#1580)
* Creating InputGroup * Updating Views with new InputGroup * Creating Docz for InputGroup * Fixing eslint issues * Renaming inputGroup props naming to be more generic.
This commit is contained in:
parent
5766d4e91a
commit
056f56f5b4
@ -1,5 +1,5 @@
|
|||||||
/** UTILS */
|
/** UTILS */
|
||||||
import * as utils from './src/utils/';
|
import utils from './src/utils';
|
||||||
export { utils };
|
export { utils };
|
||||||
|
|
||||||
/** CONTEXT/HOOKS */
|
/** CONTEXT/HOOKS */
|
||||||
@ -15,22 +15,25 @@ export {
|
|||||||
Button,
|
Button,
|
||||||
ButtonGroup,
|
ButtonGroup,
|
||||||
DateRange,
|
DateRange,
|
||||||
InputDateRange,
|
|
||||||
InputMultiSelect,
|
|
||||||
InputText,
|
|
||||||
InputLabelWrapper,
|
|
||||||
EmptyStudies,
|
EmptyStudies,
|
||||||
Icon,
|
Icon,
|
||||||
IconButton,
|
IconButton,
|
||||||
|
Input,
|
||||||
|
InputDateRange,
|
||||||
|
InputGroup,
|
||||||
|
InputLabelWrapper,
|
||||||
|
InputMultiSelect,
|
||||||
|
InputText,
|
||||||
|
Label,
|
||||||
|
NavBar,
|
||||||
Select,
|
Select,
|
||||||
Svg,
|
Svg,
|
||||||
Input,
|
|
||||||
ThemeWrapper,
|
|
||||||
Table,
|
Table,
|
||||||
TableBody,
|
TableBody,
|
||||||
|
TableCell,
|
||||||
TableHead,
|
TableHead,
|
||||||
TableRow,
|
TableRow,
|
||||||
TableCell,
|
ThemeWrapper,
|
||||||
Typography,
|
Typography,
|
||||||
} from './src/components';
|
} from './src/components';
|
||||||
|
|
||||||
|
|||||||
@ -4,20 +4,15 @@ import 'react-dates/lib/css/_datepicker.css';
|
|||||||
import { DateRangePicker, isInclusivelyBeforeDay } from 'react-dates';
|
import { DateRangePicker, isInclusivelyBeforeDay } from 'react-dates';
|
||||||
import './DateRange.css';
|
import './DateRange.css';
|
||||||
|
|
||||||
import React, { useState } from 'react';
|
import React, { useState, useCallback } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
|
|
||||||
const DateRange = (props) => {
|
const today = moment();
|
||||||
const { onChange, startDate, endDate } = props;
|
const lastWeek = moment().subtract(7, 'day');
|
||||||
const [focusedInput, setFocusedInput] = useState(null);
|
const lastMonth = moment().subtract(1, 'month');
|
||||||
|
const studyDatePresets = [
|
||||||
const today = moment();
|
|
||||||
const lastWeek = moment().subtract(7, 'day');
|
|
||||||
const lastMonth = moment().subtract(1, 'month');
|
|
||||||
|
|
||||||
const studyDatePresets = [
|
|
||||||
{
|
{
|
||||||
text: 'Today',
|
text: 'Today',
|
||||||
start: today,
|
start: today,
|
||||||
@ -33,7 +28,28 @@ const DateRange = (props) => {
|
|||||||
start: lastMonth,
|
start: lastMonth,
|
||||||
end: today,
|
end: today,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const renderYearsOptions = () => {
|
||||||
|
const currentYear = moment().year();
|
||||||
|
const options = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < 20; i++) {
|
||||||
|
const year = currentYear - i;
|
||||||
|
options.push(
|
||||||
|
<option key={year} value={year}>
|
||||||
|
{year}
|
||||||
|
</option>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return options;
|
||||||
|
};
|
||||||
|
|
||||||
|
const DateRange = props => {
|
||||||
|
const { onChange, startDate, endDate } = props;
|
||||||
|
const [focusedInput, setFocusedInput] = useState(null);
|
||||||
|
const renderYearsOptionsCallback = useCallback(renderYearsOptions, []);
|
||||||
|
|
||||||
const renderDatePresets = () => {
|
const renderDatePresets = () => {
|
||||||
return (
|
return (
|
||||||
@ -60,35 +76,30 @@ const DateRange = (props) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
const renderMonthElement = ({ month, onMonthSelect, onYearSelect }) => {
|
const renderMonthElement = ({ month, onMonthSelect, onYearSelect }) => {
|
||||||
const renderYearsOptions = () => {
|
|
||||||
const yearsRange = 20;
|
|
||||||
const options = [];
|
|
||||||
|
|
||||||
for (let i = 0; i < yearsRange; i++) {
|
|
||||||
const year = moment().year() - i;
|
|
||||||
options.push(
|
|
||||||
<option key={year} value={year}>
|
|
||||||
{year}
|
|
||||||
</option>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return options;
|
|
||||||
};
|
|
||||||
|
|
||||||
renderMonthElement.propTypes = {
|
renderMonthElement.propTypes = {
|
||||||
month: PropTypes.object,
|
month: PropTypes.object,
|
||||||
onMonthSelect: PropTypes.func,
|
onMonthSelect: PropTypes.func,
|
||||||
onYearSelect: PropTypes.func,
|
onYearSelect: PropTypes.func,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleMonthChange = event => {
|
||||||
|
onMonthSelect(month, event.target.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleYearChange = event => {
|
||||||
|
onYearSelect(month, event.target.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleOnBlur = () => {};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex justify-center">
|
<div className="flex justify-center">
|
||||||
<div className="my-0 mx-1">
|
<div className="my-0 mx-1">
|
||||||
<select
|
<select
|
||||||
className="DateRangePicker_select"
|
className="DateRangePicker_select"
|
||||||
value={month.month()}
|
value={month.month()}
|
||||||
onChange={(e) => onMonthSelect(month, e.target.value)}
|
onChange={handleMonthChange}
|
||||||
|
onBlur={handleOnBlur}
|
||||||
>
|
>
|
||||||
{moment.months().map((label, value) => (
|
{moment.months().map((label, value) => (
|
||||||
<option key={value} value={value}>
|
<option key={value} value={value}>
|
||||||
@ -98,13 +109,13 @@ const DateRange = (props) => {
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div className="my-0 mx-1">
|
<div className="my-0 mx-1">
|
||||||
{}
|
|
||||||
<select
|
<select
|
||||||
className="DateRangePicker_select"
|
className="DateRangePicker_select"
|
||||||
value={month.year()}
|
value={month.year()}
|
||||||
onChange={(e) => onYearSelect(month, e.target.value)}
|
onChange={handleYearChange}
|
||||||
|
onBlur={handleOnBlur}
|
||||||
>
|
>
|
||||||
{renderYearsOptions()}
|
{renderYearsOptionsCallback()}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -120,7 +131,7 @@ const DateRange = (props) => {
|
|||||||
endDateId={'endDateId'}
|
endDateId={'endDateId'}
|
||||||
onDatesChange={onChange}
|
onDatesChange={onChange}
|
||||||
focusedInput={focusedInput}
|
focusedInput={focusedInput}
|
||||||
onFocusChange={(updatedVal) => setFocusedInput(updatedVal)}
|
onFocusChange={updatedVal => setFocusedInput(updatedVal)}
|
||||||
/** OPTIONAL */
|
/** OPTIONAL */
|
||||||
renderCalendarInfo={renderDatePresets}
|
renderCalendarInfo={renderDatePresets}
|
||||||
renderMonthElement={renderMonthElement}
|
renderMonthElement={renderMonthElement}
|
||||||
@ -130,7 +141,7 @@ const DateRange = (props) => {
|
|||||||
closeDatePicker: 'Close',
|
closeDatePicker: 'Close',
|
||||||
clearDates: 'Clear dates',
|
clearDates: 'Clear dates',
|
||||||
}}
|
}}
|
||||||
isOutsideRange={(day) => !isInclusivelyBeforeDay(day, moment())}
|
isOutsideRange={day => !isInclusivelyBeforeDay(day, moment())}
|
||||||
hideKeyboardShortcutsPanel={true}
|
hideKeyboardShortcutsPanel={true}
|
||||||
numberOfMonths={1}
|
numberOfMonths={1}
|
||||||
showClearDates={false}
|
showClearDates={false}
|
||||||
|
|||||||
162
platform/ui/src/components/InputGroup/InputGroup.jsx
Normal file
162
platform/ui/src/components/InputGroup/InputGroup.jsx
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import classnames from 'classnames';
|
||||||
|
|
||||||
|
import {
|
||||||
|
InputText,
|
||||||
|
InputDateRange,
|
||||||
|
InputMultiSelect,
|
||||||
|
InputLabelWrapper,
|
||||||
|
} from '@ohif/ui';
|
||||||
|
|
||||||
|
const InputGroup = ({
|
||||||
|
inputMeta,
|
||||||
|
values,
|
||||||
|
onValuesChange,
|
||||||
|
sorting,
|
||||||
|
onSortingChange,
|
||||||
|
isSortingEnable,
|
||||||
|
}) => {
|
||||||
|
const { sortBy, sortDirection } = sorting;
|
||||||
|
|
||||||
|
const handleFilterLabelClick = name => {
|
||||||
|
if (isSortingEnable) {
|
||||||
|
let _sortDirection = 'ascending';
|
||||||
|
if (sortBy === name) {
|
||||||
|
if (sortDirection === 'ascending') {
|
||||||
|
_sortDirection = 'descending';
|
||||||
|
} else if (sortDirection === 'descending') {
|
||||||
|
_sortDirection = 'none';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onSortingChange({
|
||||||
|
sortBy: _sortDirection !== 'none' ? name : '',
|
||||||
|
sortDirection: _sortDirection,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderFieldInputComponent = ({
|
||||||
|
name,
|
||||||
|
displayName,
|
||||||
|
inputProps,
|
||||||
|
isSortable,
|
||||||
|
inputType,
|
||||||
|
}) => {
|
||||||
|
const _isSortable = isSortable && isSortingEnable;
|
||||||
|
const _sortDirection = sortBy !== name ? 'none' : sortDirection;
|
||||||
|
|
||||||
|
const onLabelClick = () => {
|
||||||
|
handleFilterLabelClick(name);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleFieldChange = newValue => {
|
||||||
|
onValuesChange({
|
||||||
|
...values,
|
||||||
|
[name]: newValue,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
switch (inputType) {
|
||||||
|
case 'Text':
|
||||||
|
return (
|
||||||
|
<InputText
|
||||||
|
key={name}
|
||||||
|
label={displayName}
|
||||||
|
isSortable={_isSortable}
|
||||||
|
sortDirection={_sortDirection}
|
||||||
|
onLabelClick={onLabelClick}
|
||||||
|
value={values[name]}
|
||||||
|
onChange={handleFieldChange}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
case 'MultiSelect':
|
||||||
|
return (
|
||||||
|
<InputMultiSelect
|
||||||
|
key={name}
|
||||||
|
label={displayName}
|
||||||
|
isSortable={_isSortable}
|
||||||
|
sortDirection={_sortDirection}
|
||||||
|
onLabelClick={onLabelClick}
|
||||||
|
value={values[name]}
|
||||||
|
onChange={handleFieldChange}
|
||||||
|
options={inputProps.options}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
case 'DateRange':
|
||||||
|
return (
|
||||||
|
<InputDateRange
|
||||||
|
key={name}
|
||||||
|
label={displayName}
|
||||||
|
isSortable={_isSortable}
|
||||||
|
sortDirection={_sortDirection}
|
||||||
|
onLabelClick={onLabelClick}
|
||||||
|
value={values[name]}
|
||||||
|
onChange={handleFieldChange}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
case 'None':
|
||||||
|
return (
|
||||||
|
<InputLabelWrapper
|
||||||
|
key={name}
|
||||||
|
label={displayName}
|
||||||
|
isSortable={_isSortable}
|
||||||
|
sortDirection={_sortDirection}
|
||||||
|
onLabelClick={onLabelClick}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<div className="container m-auto relative flex flex-col">
|
||||||
|
<div className="flex flex-row w-full">
|
||||||
|
{inputMeta.map(inputMeta => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={inputMeta.name}
|
||||||
|
className={classnames(
|
||||||
|
'pl-4 first:pl-12',
|
||||||
|
`w-${inputMeta.gridCol}/24`
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{renderFieldInputComponent(inputMeta)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
InputGroup.propTypes = {
|
||||||
|
inputMeta: PropTypes.arrayOf(
|
||||||
|
PropTypes.shape({
|
||||||
|
name: PropTypes.string.isRequired,
|
||||||
|
displayName: PropTypes.string.isRequired,
|
||||||
|
inputType: PropTypes.oneOf(['Text', 'MultiSelect', 'DateRange', 'None'])
|
||||||
|
.isRequired,
|
||||||
|
isSortable: PropTypes.bool.isRequired,
|
||||||
|
gridCol: PropTypes.oneOf([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12])
|
||||||
|
.isRequired,
|
||||||
|
option: PropTypes.arrayOf(
|
||||||
|
PropTypes.shape({
|
||||||
|
value: PropTypes.string,
|
||||||
|
label: PropTypes.string,
|
||||||
|
})
|
||||||
|
),
|
||||||
|
})
|
||||||
|
).isRequired,
|
||||||
|
values: PropTypes.object.isRequired,
|
||||||
|
onValuesChange: PropTypes.func.isRequired,
|
||||||
|
sorting: PropTypes.shape({
|
||||||
|
sortBy: PropTypes.string,
|
||||||
|
sortDirection: PropTypes.oneOf(['ascending', 'descending', 'none']),
|
||||||
|
}).isRequired,
|
||||||
|
onSortingChange: PropTypes.func.isRequired,
|
||||||
|
isSortingEnable: PropTypes.bool.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default InputGroup;
|
||||||
90
platform/ui/src/components/InputGroup/InputGroup.mdx
Normal file
90
platform/ui/src/components/InputGroup/InputGroup.mdx
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
---
|
||||||
|
name: InputGroup
|
||||||
|
menu: Components
|
||||||
|
route: components/InputGroup
|
||||||
|
---
|
||||||
|
|
||||||
|
import { useState } from 'react';
|
||||||
|
import { Playground, Props } from 'docz';
|
||||||
|
import { InputGroup } from '@ohif/ui';
|
||||||
|
|
||||||
|
# Input Group
|
||||||
|
|
||||||
|
## Import
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
import { InputGroup } from '@ohif/ui';
|
||||||
|
```
|
||||||
|
|
||||||
|
## Basic usage
|
||||||
|
|
||||||
|
<Playground>
|
||||||
|
{() => {
|
||||||
|
const [filterValues, setFilterValues] = useState({
|
||||||
|
patient: '',
|
||||||
|
studyDate: {
|
||||||
|
startDate: null,
|
||||||
|
endDate: null,
|
||||||
|
},
|
||||||
|
modality: [],
|
||||||
|
});
|
||||||
|
const [filterSorting, setFilterSorting] = useState({
|
||||||
|
sortBy: '',
|
||||||
|
sortDirection: 'none',
|
||||||
|
});
|
||||||
|
const filtersMeta = [
|
||||||
|
{
|
||||||
|
name: 'patient',
|
||||||
|
displayName: 'Patient Name',
|
||||||
|
inputType: 'Text',
|
||||||
|
isSortable: true,
|
||||||
|
gridCol: 6,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'studyDate',
|
||||||
|
displayName: 'Study date',
|
||||||
|
inputType: 'DateRange',
|
||||||
|
isSortable: true,
|
||||||
|
gridCol: 6,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'modality',
|
||||||
|
displayName: 'Modality',
|
||||||
|
inputType: 'MultiSelect',
|
||||||
|
inputProps: {
|
||||||
|
options: [
|
||||||
|
{ value: 'SEG', label: 'SEG' },
|
||||||
|
{ value: 'CT', label: 'CT' },
|
||||||
|
{ value: 'MR', label: 'MR' },
|
||||||
|
{ value: 'SR', label: 'SR' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
isSortable: true,
|
||||||
|
gridCol: 6,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'numberOfInstance',
|
||||||
|
displayName: 'Number of Instances',
|
||||||
|
inputType: 'None',
|
||||||
|
isSortable: true,
|
||||||
|
gridCol: 6,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
return (
|
||||||
|
<div className="py-4">
|
||||||
|
<InputGroup
|
||||||
|
inputMeta={filtersMeta}
|
||||||
|
values={filterValues}
|
||||||
|
onValuesChange={setFilterValues}
|
||||||
|
sorting={filterSorting}
|
||||||
|
onSortingChange={setFilterSorting}
|
||||||
|
isSortingEnable={true}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
</Playground>
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
<Props of={InputGroup} />
|
||||||
2
platform/ui/src/components/InputGroup/index.js
Normal file
2
platform/ui/src/components/InputGroup/index.js
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
import InputGroup from './InputGroup';
|
||||||
|
export default InputGroup;
|
||||||
@ -24,9 +24,11 @@ const InputLabelWrapper = ({
|
|||||||
return (
|
return (
|
||||||
<label className={classnames(baseLabelClassName, className)}>
|
<label className={classnames(baseLabelClassName, className)}>
|
||||||
<span
|
<span
|
||||||
|
role="button"
|
||||||
className={spanClassName}
|
className={spanClassName}
|
||||||
onClick={onLabelClick}
|
onClick={onLabelClick}
|
||||||
onKeyDown={onLabelClick}
|
onKeyDown={onLabelClick}
|
||||||
|
tabIndex="0"
|
||||||
>
|
>
|
||||||
{label}
|
{label}
|
||||||
{isSortable && (
|
{isSortable && (
|
||||||
|
|||||||
@ -59,6 +59,7 @@ InputMultiSelect.propTypes = {
|
|||||||
sortDirection: PropTypes.oneOf(['ascending', 'descending', 'none'])
|
sortDirection: PropTypes.oneOf(['ascending', 'descending', 'none'])
|
||||||
.isRequired,
|
.isRequired,
|
||||||
onLabelClick: PropTypes.func.isRequired,
|
onLabelClick: PropTypes.func.isRequired,
|
||||||
|
onChange: PropTypes.func.isRequired,
|
||||||
placeholder: PropTypes.string,
|
placeholder: PropTypes.string,
|
||||||
options: PropTypes.arrayOf(
|
options: PropTypes.arrayOf(
|
||||||
PropTypes.shape({
|
PropTypes.shape({
|
||||||
@ -78,7 +79,6 @@ InputMultiSelect.propTypes = {
|
|||||||
label: PropTypes.string,
|
label: PropTypes.string,
|
||||||
}),
|
}),
|
||||||
]),
|
]),
|
||||||
onChange: PropTypes.func.isRequired,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default InputMultiSelect;
|
export default InputMultiSelect;
|
||||||
|
|||||||
@ -6,9 +6,12 @@ import Icon from './Icon';
|
|||||||
import IconButton from './IconButton';
|
import IconButton from './IconButton';
|
||||||
import Input from './Input';
|
import Input from './Input';
|
||||||
import InputDateRange from './InputDateRange';
|
import InputDateRange from './InputDateRange';
|
||||||
|
import InputGroup from './InputGroup';
|
||||||
import InputLabelWrapper from './InputLabelWrapper';
|
import InputLabelWrapper from './InputLabelWrapper';
|
||||||
import InputMultiSelect from './InputMultiSelect';
|
import InputMultiSelect from './InputMultiSelect';
|
||||||
import InputText from './InputText';
|
import InputText from './InputText';
|
||||||
|
import Label from './Label';
|
||||||
|
import NavBar from './NavBar';
|
||||||
import Select from './Select';
|
import Select from './Select';
|
||||||
import Svg from './Svg';
|
import Svg from './Svg';
|
||||||
import Table from './Table';
|
import Table from './Table';
|
||||||
@ -28,9 +31,12 @@ export {
|
|||||||
IconButton,
|
IconButton,
|
||||||
Input,
|
Input,
|
||||||
InputDateRange,
|
InputDateRange,
|
||||||
|
InputGroup,
|
||||||
InputLabelWrapper,
|
InputLabelWrapper,
|
||||||
InputMultiSelect,
|
InputMultiSelect,
|
||||||
InputText,
|
InputText,
|
||||||
|
Label,
|
||||||
|
NavBar,
|
||||||
Select,
|
Select,
|
||||||
Svg,
|
Svg,
|
||||||
Table,
|
Table,
|
||||||
|
|||||||
@ -39,17 +39,6 @@ const ModalProvider = ({ children, modal: Modal, service }) => {
|
|||||||
|
|
||||||
const [options, setOptions] = useState(DEFAULT_OPTIONS);
|
const [options, setOptions] = useState(DEFAULT_OPTIONS);
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the implementation of a modal service that can be used by extensions.
|
|
||||||
*
|
|
||||||
* @returns void
|
|
||||||
*/
|
|
||||||
useEffect(() => {
|
|
||||||
if (service) {
|
|
||||||
service.setServiceImplementation({ hide, show });
|
|
||||||
}
|
|
||||||
}, [hide, service, show]);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show the modal and override its configuration props.
|
* Show the modal and override its configuration props.
|
||||||
*
|
*
|
||||||
@ -69,6 +58,17 @@ const ModalProvider = ({ children, modal: Modal, service }) => {
|
|||||||
DEFAULT_OPTIONS,
|
DEFAULT_OPTIONS,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the implementation of a modal service that can be used by extensions.
|
||||||
|
*
|
||||||
|
* @returns void
|
||||||
|
*/
|
||||||
|
useEffect(() => {
|
||||||
|
if (service) {
|
||||||
|
service.setServiceImplementation({ hide, show });
|
||||||
|
}
|
||||||
|
}, [hide, service, show]);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
content: ModalContent,
|
content: ModalContent,
|
||||||
contentProps,
|
contentProps,
|
||||||
|
|||||||
@ -30,9 +30,12 @@ export const Sidebar = React.forwardRef((props, ref) => {
|
|||||||
<>
|
<>
|
||||||
<div className="block lg:hidden">
|
<div className="block lg:hidden">
|
||||||
<div
|
<div
|
||||||
|
role="button"
|
||||||
onClick={() => props.onClick()}
|
onClick={() => props.onClick()}
|
||||||
|
onKeyDown={() => props.onClick()}
|
||||||
className="fixed left-0 w-full h-full bg-black opacity-80"
|
className="fixed left-0 w-full h-full bg-black opacity-80"
|
||||||
style={{ top: 81, zIndex: 99999 }}
|
style={{ top: 81, zIndex: 99999 }}
|
||||||
|
tabIndex="0"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
|
|||||||
@ -3,4 +3,8 @@ import getMockedStudies from './getMockedStudies';
|
|||||||
import getModalities from './getModalities';
|
import getModalities from './getModalities';
|
||||||
import getInstances from './getInstances';
|
import getInstances from './getInstances';
|
||||||
|
|
||||||
|
const utils = { capitalize, getMockedStudies, getModalities, getInstances };
|
||||||
|
|
||||||
export { capitalize, getMockedStudies, getModalities, getInstances };
|
export { capitalize, getMockedStudies, getModalities, getInstances };
|
||||||
|
|
||||||
|
export default utils;
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import React from 'react';
|
import React, { useState } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
|
|
||||||
@ -67,7 +67,7 @@ const filtersMeta = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const filtersValues = {
|
const defaultFilterValues = {
|
||||||
patientName: '',
|
patientName: '',
|
||||||
mrn: '',
|
mrn: '',
|
||||||
studyDate: {
|
studyDate: {
|
||||||
@ -83,10 +83,18 @@ const filtersValues = {
|
|||||||
resultsPerPage: 25,
|
resultsPerPage: 25,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const isFiltering = (filterValues, defaultFilterValues) => {
|
||||||
|
return Object.keys(defaultFilterValues).some(name => {
|
||||||
|
return filterValues[name] !== defaultFilterValues[name];
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const StudyList = ({ studies, perPage }) => {
|
const StudyList = ({ studies, perPage }) => {
|
||||||
|
const [filterValues, setFilterValues] = useState(defaultFilterValues);
|
||||||
const studiesData = studies.slice(0, perPage);
|
const studiesData = studies.slice(0, perPage);
|
||||||
const numOfStudies = studies.length;
|
const numOfStudies = studies.length;
|
||||||
const isEmptyStudies = numOfStudies === 0;
|
const isEmptyStudies = numOfStudies === 0;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={classnames('bg-black h-full', {
|
className={classnames('bg-black h-full', {
|
||||||
@ -97,7 +105,10 @@ const StudyList = ({ studies, perPage }) => {
|
|||||||
<StudyListFilter
|
<StudyListFilter
|
||||||
numOfStudies={numOfStudies}
|
numOfStudies={numOfStudies}
|
||||||
filtersMeta={filtersMeta}
|
filtersMeta={filtersMeta}
|
||||||
filtersValues={filtersValues}
|
filterValues={filterValues}
|
||||||
|
setFilterValues={setFilterValues}
|
||||||
|
clearFilters={() => setFilterValues(defaultFilterValues)}
|
||||||
|
isFiltering={isFiltering(filterValues, defaultFilterValues)}
|
||||||
/>
|
/>
|
||||||
<StudyListTable
|
<StudyListTable
|
||||||
studies={studiesData}
|
studies={studiesData}
|
||||||
|
|||||||
@ -1,17 +1,9 @@
|
|||||||
import React, { useState } from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import classnames from 'classnames';
|
|
||||||
|
|
||||||
import {
|
import { Button, Icon, Typography, InputGroup } from '@ohif/ui';
|
||||||
Button,
|
|
||||||
Icon,
|
|
||||||
Typography,
|
|
||||||
InputText,
|
|
||||||
InputDateRange,
|
|
||||||
InputMultiSelect,
|
|
||||||
InputLabelWrapper,
|
|
||||||
} from '@ohif/ui';
|
|
||||||
|
|
||||||
|
<<<<<<< HEAD
|
||||||
const isFiltering = (currentFiltersValues, filtersValues) => {
|
const isFiltering = (currentFiltersValues, filtersValues) => {
|
||||||
return Object.keys(currentFiltersValues).some((name) => {
|
return Object.keys(currentFiltersValues).some((name) => {
|
||||||
const filterValue = currentFiltersValues[name];
|
const filterValue = currentFiltersValues[name];
|
||||||
@ -129,10 +121,28 @@ const StudyListFilter = ({ filtersMeta, filtersValues, numOfStudies }) => {
|
|||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
=======
|
||||||
|
const StudyListFilter = ({
|
||||||
|
filtersMeta,
|
||||||
|
filterValues,
|
||||||
|
setFilterValues,
|
||||||
|
clearFilters,
|
||||||
|
isFiltering,
|
||||||
|
numOfStudies,
|
||||||
|
}) => {
|
||||||
|
const { sortBy, sortDirection } = filterValues;
|
||||||
|
const filterSorting = { sortBy, sortDirection };
|
||||||
|
const setFilterSorting = sortingValues => {
|
||||||
|
setFilterValues({
|
||||||
|
...filterValues,
|
||||||
|
...sortingValues,
|
||||||
|
});
|
||||||
|
>>>>>>> 26d8c8ae5... refactor: StudyListFilter refactor (#1580)
|
||||||
};
|
};
|
||||||
|
const isSortingEnable = numOfStudies > 0 && numOfStudies <= 100;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<React.Fragment>
|
||||||
<div>
|
<div>
|
||||||
<div className="bg-primary-dark">
|
<div className="bg-primary-dark">
|
||||||
<div className="container m-auto relative flex flex-col pt-5">
|
<div className="container m-auto relative flex flex-col pt-5">
|
||||||
@ -157,7 +167,7 @@ const StudyListFilter = ({ filtersMeta, filtersValues, numOfStudies }) => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-row">
|
<div className="flex flex-row">
|
||||||
{isFiltering(currentFiltersValues, filtersValues) && (
|
{isFiltering && (
|
||||||
<Button
|
<Button
|
||||||
rounded="full"
|
rounded="full"
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
@ -184,6 +194,7 @@ const StudyListFilter = ({ filtersMeta, filtersValues, numOfStudies }) => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="sticky z-10 border-b-4 border-black" style={{ top: 58 }}>
|
<div className="sticky z-10 border-b-4 border-black" style={{ top: 58 }}>
|
||||||
|
<<<<<<< HEAD
|
||||||
<div className="bg-primary-dark pt-3 pb-3 ">
|
<div className="bg-primary-dark pt-3 pb-3 ">
|
||||||
<div className="container m-auto relative flex flex-col">
|
<div className="container m-auto relative flex flex-col">
|
||||||
<div className="flex flex-row w-full">
|
<div className="flex flex-row w-full">
|
||||||
@ -202,6 +213,17 @@ const StudyListFilter = ({ filtersMeta, filtersValues, numOfStudies }) => {
|
|||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
=======
|
||||||
|
<div className="bg-custom-navyDark pt-3 pb-3 ">
|
||||||
|
<InputGroup
|
||||||
|
inputMeta={filtersMeta}
|
||||||
|
values={filterValues}
|
||||||
|
onValuesChange={setFilterValues}
|
||||||
|
sorting={filterSorting}
|
||||||
|
onSortingChange={setFilterSorting}
|
||||||
|
isSortingEnable={isSortingEnable}
|
||||||
|
/>
|
||||||
|
>>>>>>> 26d8c8ae5... refactor: StudyListFilter refactor (#1580)
|
||||||
</div>
|
</div>
|
||||||
{numOfStudies > 100 && (
|
{numOfStudies > 100 && (
|
||||||
<div className="container m-auto">
|
<div className="container m-auto">
|
||||||
@ -213,28 +235,33 @@ const StudyListFilter = ({ filtersMeta, filtersValues, numOfStudies }) => {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</>
|
</React.Fragment>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
StudyListFilter.defaultProps = {
|
|
||||||
filtersMeta: [],
|
|
||||||
filtersValues: {},
|
|
||||||
numOfStudies: 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
StudyListFilter.propTypes = {
|
StudyListFilter.propTypes = {
|
||||||
filtersMeta: PropTypes.arrayOf(
|
filtersMeta: PropTypes.arrayOf(
|
||||||
PropTypes.shape({
|
PropTypes.shape({
|
||||||
name: PropTypes.string,
|
name: PropTypes.string.isRequired,
|
||||||
dsplayName: PropTypes.string,
|
displayName: PropTypes.string.isRequired,
|
||||||
inputType: PropTypes.oneOf(['Text', 'MultiSelect', 'DateRange', 'None']),
|
inputType: PropTypes.oneOf(['Text', 'MultiSelect', 'DateRange', 'None'])
|
||||||
isSortable: PropTypes.bool,
|
.isRequired,
|
||||||
gridCol: PropTypes.oneOf([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]),
|
isSortable: PropTypes.bool.isRequired,
|
||||||
|
gridCol: PropTypes.oneOf([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12])
|
||||||
|
.isRequired,
|
||||||
|
option: PropTypes.arrayOf(
|
||||||
|
PropTypes.shape({
|
||||||
|
value: PropTypes.string,
|
||||||
|
label: PropTypes.string,
|
||||||
})
|
})
|
||||||
),
|
),
|
||||||
filtersValues: PropTypes.object,
|
})
|
||||||
numOfStudies: PropTypes.number,
|
).isRequired,
|
||||||
|
filterValues: PropTypes.object.isRequired,
|
||||||
|
numOfStudies: PropTypes.number.isRequired,
|
||||||
|
setFilterValues: PropTypes.func.isRequired,
|
||||||
|
clearFilters: PropTypes.func.isRequired,
|
||||||
|
isFiltering: PropTypes.bool.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default StudyListFilter;
|
export default StudyListFilter;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user