diff --git a/platform/ui/index.js b/platform/ui/index.js
index 164ee9850..ac7f13282 100644
--- a/platform/ui/index.js
+++ b/platform/ui/index.js
@@ -1,5 +1,5 @@
/** UTILS */
-import * as utils from './src/utils/';
+import utils from './src/utils';
export { utils };
/** CONTEXT/HOOKS */
@@ -15,22 +15,25 @@ export {
Button,
ButtonGroup,
DateRange,
- InputDateRange,
- InputMultiSelect,
- InputText,
- InputLabelWrapper,
EmptyStudies,
Icon,
IconButton,
+ Input,
+ InputDateRange,
+ InputGroup,
+ InputLabelWrapper,
+ InputMultiSelect,
+ InputText,
+ Label,
+ NavBar,
Select,
Svg,
- Input,
- ThemeWrapper,
Table,
TableBody,
+ TableCell,
TableHead,
TableRow,
- TableCell,
+ ThemeWrapper,
Typography,
} from './src/components';
diff --git a/platform/ui/src/components/DateRange/DateRange.jsx b/platform/ui/src/components/DateRange/DateRange.jsx
index 6cc20d2c3..86814b121 100644
--- a/platform/ui/src/components/DateRange/DateRange.jsx
+++ b/platform/ui/src/components/DateRange/DateRange.jsx
@@ -4,36 +4,52 @@ import 'react-dates/lib/css/_datepicker.css';
import { DateRangePicker, isInclusivelyBeforeDay } from 'react-dates';
import './DateRange.css';
-import React, { useState } from 'react';
+import React, { useState, useCallback } from 'react';
import PropTypes from 'prop-types';
import moment from 'moment';
-const DateRange = (props) => {
+const today = moment();
+const lastWeek = moment().subtract(7, 'day');
+const lastMonth = moment().subtract(1, 'month');
+const studyDatePresets = [
+ {
+ text: 'Today',
+ start: today,
+ end: today,
+ },
+ {
+ text: 'Last 7 days',
+ start: lastWeek,
+ end: today,
+ },
+ {
+ text: 'Last 30 days',
+ start: lastMonth,
+ end: today,
+ },
+];
+
+const renderYearsOptions = () => {
+ const currentYear = moment().year();
+ const options = [];
+
+ for (let i = 0; i < 20; i++) {
+ const year = currentYear - i;
+ options.push(
+
+ );
+ }
+
+ return options;
+};
+
+const DateRange = props => {
const { onChange, startDate, endDate } = props;
const [focusedInput, setFocusedInput] = useState(null);
-
- const today = moment();
- const lastWeek = moment().subtract(7, 'day');
- const lastMonth = moment().subtract(1, 'month');
-
- const studyDatePresets = [
- {
- text: 'Today',
- start: today,
- end: today,
- },
- {
- text: 'Last 7 days',
- start: lastWeek,
- end: today,
- },
- {
- text: 'Last 30 days',
- start: lastMonth,
- end: today,
- },
- ];
+ const renderYearsOptionsCallback = useCallback(renderYearsOptions, []);
const renderDatePresets = () => {
return (
@@ -60,35 +76,30 @@ const DateRange = (props) => {
);
};
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(
-
- );
- }
-
- return options;
- };
-
renderMonthElement.propTypes = {
month: PropTypes.object,
onMonthSelect: PropTypes.func,
onYearSelect: PropTypes.func,
};
+ const handleMonthChange = event => {
+ onMonthSelect(month, event.target.value);
+ };
+
+ const handleYearChange = event => {
+ onYearSelect(month, event.target.value);
+ };
+
+ const handleOnBlur = () => {};
+
return (
- {}
@@ -120,7 +131,7 @@ const DateRange = (props) => {
endDateId={'endDateId'}
onDatesChange={onChange}
focusedInput={focusedInput}
- onFocusChange={(updatedVal) => setFocusedInput(updatedVal)}
+ onFocusChange={updatedVal => setFocusedInput(updatedVal)}
/** OPTIONAL */
renderCalendarInfo={renderDatePresets}
renderMonthElement={renderMonthElement}
@@ -130,7 +141,7 @@ const DateRange = (props) => {
closeDatePicker: 'Close',
clearDates: 'Clear dates',
}}
- isOutsideRange={(day) => !isInclusivelyBeforeDay(day, moment())}
+ isOutsideRange={day => !isInclusivelyBeforeDay(day, moment())}
hideKeyboardShortcutsPanel={true}
numberOfMonths={1}
showClearDates={false}
diff --git a/platform/ui/src/components/InputGroup/InputGroup.jsx b/platform/ui/src/components/InputGroup/InputGroup.jsx
new file mode 100644
index 000000000..8dba0ed98
--- /dev/null
+++ b/platform/ui/src/components/InputGroup/InputGroup.jsx
@@ -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 (
+
+ );
+ case 'MultiSelect':
+ return (
+
+ );
+ case 'DateRange':
+ return (
+
+ );
+ case 'None':
+ return (
+
+ );
+ default:
+ break;
+ }
+ };
+ return (
+
+
+ {inputMeta.map(inputMeta => {
+ return (
+
+ {renderFieldInputComponent(inputMeta)}
+
+ );
+ })}
+
+
+ );
+};
+
+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;
diff --git a/platform/ui/src/components/InputGroup/InputGroup.mdx b/platform/ui/src/components/InputGroup/InputGroup.mdx
new file mode 100644
index 000000000..076b75a34
--- /dev/null
+++ b/platform/ui/src/components/InputGroup/InputGroup.mdx
@@ -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
+
+
+ {() => {
+ 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 (
+
+
+
+ );
+ }}
+
+
+## Properties
+
+
diff --git a/platform/ui/src/components/InputGroup/index.js b/platform/ui/src/components/InputGroup/index.js
new file mode 100644
index 000000000..2c20c333d
--- /dev/null
+++ b/platform/ui/src/components/InputGroup/index.js
@@ -0,0 +1,2 @@
+import InputGroup from './InputGroup';
+export default InputGroup;
diff --git a/platform/ui/src/components/InputLabelWrapper/InputLabelWrapper.jsx b/platform/ui/src/components/InputLabelWrapper/InputLabelWrapper.jsx
index 3ed1868ad..f80dfccdb 100644
--- a/platform/ui/src/components/InputLabelWrapper/InputLabelWrapper.jsx
+++ b/platform/ui/src/components/InputLabelWrapper/InputLabelWrapper.jsx
@@ -24,9 +24,11 @@ const InputLabelWrapper = ({
return (