Creating combo input with label, naming and input for Text, Select and DateRange (#1553)
* Issues: OHIF-46, OHIF-47, OHIF-48
This commit is contained in:
1 parent
3fe3634475
commit
de484275e8
18 files changed
+646
-218
No files matched your search
@@ -0,0 +1,52 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { DateRange, InputLabelWrapper } from '@ohif/ui';
|
||||
|
||||
const InputDateRange = ({
|
||||
label,
|
||||
isSortable,
|
||||
sortDirection,
|
||||
onLabelClick,
|
||||
value,
|
||||
onChange,
|
||||
}) => {
|
||||
const { startDate, endDate } = value;
|
||||
return (
|
||||
<InputLabelWrapper
|
||||
label={label}
|
||||
isSortable={isSortable}
|
||||
sortDirection={sortDirection}
|
||||
onLabelClick={onLabelClick}
|
||||
>
|
||||
<div className="relative">
|
||||
<DateRange
|
||||
startDate={startDate}
|
||||
endDate={endDate}
|
||||
onChange={onChange}
|
||||
/>
|
||||
</div>
|
||||
</InputLabelWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
InputDateRange.defaultProps = {
|
||||
value: {},
|
||||
};
|
||||
|
||||
InputDateRange.propTypes = {
|
||||
label: PropTypes.string.isRequired,
|
||||
isSortable: PropTypes.bool.isRequired,
|
||||
sortDirection: PropTypes.oneOf(['ascending', 'descending', 'none'])
|
||||
.isRequired,
|
||||
onLabelClick: PropTypes.func.isRequired,
|
||||
value: PropTypes.shape({
|
||||
/** Start date moment object */
|
||||
startDate: PropTypes.object, // moment date is an object
|
||||
/** End date moment object */
|
||||
endDate: PropTypes.object, // moment date is an object
|
||||
}),
|
||||
onChange: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default InputDateRange;
|
||||
@@ -0,0 +1,43 @@
|
||||
---
|
||||
name: InputDateRange
|
||||
menu: Components
|
||||
route: components/InputDateRange
|
||||
---
|
||||
|
||||
import { useState } from 'react';
|
||||
import { Playground, Props } from 'docz';
|
||||
import InputDateRange from './';
|
||||
|
||||
# Input Date Range
|
||||
|
||||
## Import
|
||||
|
||||
```javascript
|
||||
import { InputDateRange } from '@ohif/ui';
|
||||
```
|
||||
|
||||
## Basic usage
|
||||
|
||||
<Playground>
|
||||
{() => {
|
||||
const [dates, setDates] = useState({
|
||||
startDate: null,
|
||||
endDate: null,
|
||||
});
|
||||
return (
|
||||
<div className="p-4 flex flex-col items-center">
|
||||
<div className="w-56">
|
||||
<InputDateRange
|
||||
label="Dates"
|
||||
value={dates}
|
||||
onChange={dates => setDates(dates)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
</Playground>
|
||||
|
||||
## Properties
|
||||
|
||||
<Props of={InputDateRange} />
|
||||
@@ -0,0 +1,2 @@
|
||||
import InputDateRange from './InputDateRange';
|
||||
export default InputDateRange;
|
||||
@@ -0,0 +1,63 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import classnames from 'classnames';
|
||||
|
||||
import { Icon } from '@ohif/ui';
|
||||
|
||||
const baseLabelClassName =
|
||||
'flex flex-col flex-1 text-white text-lg pl-1 select-none';
|
||||
const spanClassName = 'flex flex-row items-center cursor-pointer';
|
||||
const sortIconMap = {
|
||||
ascending: 'sorting-active-up',
|
||||
descending: 'sorting-active-down',
|
||||
none: 'sorting',
|
||||
};
|
||||
|
||||
const InputLabelWrapper = ({
|
||||
label,
|
||||
isSortable,
|
||||
sortDirection,
|
||||
onLabelClick,
|
||||
className,
|
||||
children,
|
||||
}) => {
|
||||
return (
|
||||
<label className={classnames(baseLabelClassName, className)}>
|
||||
<span
|
||||
className={spanClassName}
|
||||
onClick={onLabelClick}
|
||||
onKeyDown={onLabelClick}
|
||||
>
|
||||
{label}
|
||||
{isSortable && (
|
||||
<Icon
|
||||
name={sortIconMap[sortDirection]}
|
||||
className={classnames(
|
||||
'mx-2 w-2',
|
||||
sortDirection !== 'none'
|
||||
? 'text-custom-aquaBright'
|
||||
: 'text-custom-blue'
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
</span>
|
||||
<span>{children}</span>
|
||||
</label>
|
||||
);
|
||||
};
|
||||
|
||||
InputLabelWrapper.defaultProps = {
|
||||
className: '',
|
||||
};
|
||||
|
||||
InputLabelWrapper.propTypes = {
|
||||
label: PropTypes.string.isRequired,
|
||||
isSortable: PropTypes.bool.isRequired,
|
||||
sortDirection: PropTypes.oneOf(['ascending', 'descending', 'none'])
|
||||
.isRequired,
|
||||
onLabelClick: PropTypes.func.isRequired,
|
||||
className: PropTypes.string,
|
||||
children: PropTypes.node,
|
||||
};
|
||||
|
||||
export default InputLabelWrapper;
|
||||
@@ -0,0 +1,2 @@
|
||||
import InputLabelWrapper from './InputLabelWrapper';
|
||||
export default InputLabelWrapper;
|
||||
@@ -0,0 +1,84 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { Select, InputLabelWrapper } from '@ohif/ui';
|
||||
|
||||
const InputMultiSelect = ({
|
||||
label,
|
||||
isSortable,
|
||||
sortDirection,
|
||||
onLabelClick,
|
||||
value,
|
||||
placeholder,
|
||||
options,
|
||||
onChange,
|
||||
}) => {
|
||||
return (
|
||||
<InputLabelWrapper
|
||||
label={label}
|
||||
isSortable={isSortable}
|
||||
sortDirection={sortDirection}
|
||||
onLabelClick={onLabelClick}
|
||||
>
|
||||
<Select
|
||||
placeholder={placeholder}
|
||||
className="mt-2"
|
||||
options={options}
|
||||
value={value}
|
||||
isMulti={true}
|
||||
isClearable={false}
|
||||
isSearchable={false}
|
||||
closeMenuOnSelect={false}
|
||||
hideSelectedOptions={false}
|
||||
onChange={(inputvalues, { action }) => {
|
||||
switch (action) {
|
||||
case 'select-option':
|
||||
case 'remove-value':
|
||||
case 'deselect-option':
|
||||
case 'clear':
|
||||
onChange(inputvalues);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</InputLabelWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
InputMultiSelect.defaultProps = {
|
||||
value: [],
|
||||
placeholder: '',
|
||||
options: [],
|
||||
};
|
||||
|
||||
InputMultiSelect.propTypes = {
|
||||
label: PropTypes.string.isRequired,
|
||||
isSortable: PropTypes.bool.isRequired,
|
||||
sortDirection: PropTypes.oneOf(['ascending', 'descending', 'none'])
|
||||
.isRequired,
|
||||
onLabelClick: PropTypes.func.isRequired,
|
||||
placeholder: PropTypes.string,
|
||||
options: PropTypes.arrayOf(
|
||||
PropTypes.shape({
|
||||
value: PropTypes.string,
|
||||
label: PropTypes.string,
|
||||
})
|
||||
),
|
||||
value: PropTypes.oneOfType([
|
||||
PropTypes.arrayOf(
|
||||
PropTypes.shape({
|
||||
value: PropTypes.string,
|
||||
label: PropTypes.string,
|
||||
})
|
||||
),
|
||||
PropTypes.shape({
|
||||
value: PropTypes.string,
|
||||
label: PropTypes.string,
|
||||
}),
|
||||
]),
|
||||
onChange: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default InputMultiSelect;
|
||||
@@ -0,0 +1,48 @@
|
||||
---
|
||||
name: InputMultiSelect
|
||||
menu: Components
|
||||
route: components/InputMultiSelect
|
||||
---
|
||||
|
||||
import { useState } from 'react';
|
||||
import { Playground, Props } from 'docz';
|
||||
import InputMultiSelect from './';
|
||||
|
||||
# Input Multi Select
|
||||
|
||||
## Import
|
||||
|
||||
```javascript
|
||||
import { InputMultiSelect } from '@ohif/ui';
|
||||
```
|
||||
|
||||
## Basic usage
|
||||
|
||||
<Playground>
|
||||
{() => {
|
||||
const [values, setValues] = useState([]);
|
||||
return (
|
||||
<div className="flex flex-col flex-1 p-4 items-center">
|
||||
<div className="w-56">
|
||||
<InputMultiSelect
|
||||
label="Modality"
|
||||
options={[
|
||||
{ value: 'SEG', label: 'SEG' },
|
||||
{ value: 'CT', label: 'CT' },
|
||||
{ value: 'MR', label: 'MR' },
|
||||
{ value: 'SR', label: 'SR' },
|
||||
]}
|
||||
value={values}
|
||||
onChange={values => {
|
||||
setValues(values);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
</Playground>
|
||||
|
||||
## Properties
|
||||
|
||||
<Props of={InputMultiSelect} />
|
||||
@@ -0,0 +1,2 @@
|
||||
import InputMultiSelect from './InputMultiSelect';
|
||||
export default InputMultiSelect;
|
||||
@@ -0,0 +1,48 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { Input, InputLabelWrapper } from '@ohif/ui';
|
||||
|
||||
const InputText = ({
|
||||
label,
|
||||
isSortable,
|
||||
sortDirection,
|
||||
onLabelClick,
|
||||
value,
|
||||
onChange,
|
||||
}) => {
|
||||
return (
|
||||
<InputLabelWrapper
|
||||
label={label}
|
||||
isSortable={isSortable}
|
||||
sortDirection={sortDirection}
|
||||
onLabelClick={onLabelClick}
|
||||
>
|
||||
<Input
|
||||
className="border-custom-blue mt-2 bg-black"
|
||||
type="text"
|
||||
containerClassName="mr-2"
|
||||
value={value}
|
||||
onChange={event => {
|
||||
onChange(event.target.value);
|
||||
}}
|
||||
/>
|
||||
</InputLabelWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
InputText.defaultProps = {
|
||||
value: '',
|
||||
};
|
||||
|
||||
InputText.propTypes = {
|
||||
label: PropTypes.string.isRequired,
|
||||
isSortable: PropTypes.bool.isRequired,
|
||||
sortDirection: PropTypes.oneOf(['ascending', 'descending', 'none'])
|
||||
.isRequired,
|
||||
onLabelClick: PropTypes.func.isRequired,
|
||||
value: PropTypes.string,
|
||||
onChange: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default InputText;
|
||||
@@ -0,0 +1,41 @@
|
||||
---
|
||||
name: InputText
|
||||
menu: Components
|
||||
route: components/InputText
|
||||
---
|
||||
|
||||
import { useState } from 'react';
|
||||
import { Playground, Props } from 'docz';
|
||||
import InputText from './';
|
||||
|
||||
# Input Text
|
||||
|
||||
## Import
|
||||
|
||||
```javascript
|
||||
import { InputText } from '@ohif/ui';
|
||||
```
|
||||
|
||||
## Basic usage
|
||||
|
||||
<Playground>
|
||||
{() => {
|
||||
const [text, setText] = useState('');
|
||||
return (
|
||||
<div className="flex flex-col flex-1 p-4 items-center">
|
||||
<div className="w-56">
|
||||
<InputText
|
||||
label='Name'
|
||||
value={text}
|
||||
onChange={value => {setText(value)}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}}
|
||||
|
||||
</Playground>
|
||||
|
||||
## Properties
|
||||
|
||||
<Props of={InputText} />
|
||||
@@ -0,0 +1,2 @@
|
||||
import InputText from './InputText';
|
||||
export default InputText;
|
||||
@@ -6,12 +6,13 @@
|
||||
@apply border-gray-500;
|
||||
}
|
||||
|
||||
.customSelect__wrapper .customSelect__control:focus {
|
||||
.customSelect__wrapper .customSelect__control:focus,
|
||||
.customSelect__wrapper .customSelect__control--menu-is-open {
|
||||
@apply border-gray-500 outline-none;
|
||||
}
|
||||
|
||||
.customSelect__wrapper .customSelect__control--menu-is-open {
|
||||
@apply border-gray-500 outline-none;
|
||||
.customSelect--is-disabled .customSelect__control--is-disabled {
|
||||
@apply pointer-events-none;
|
||||
}
|
||||
|
||||
.customSelect__wrapper .customSelect__indicator-separator {
|
||||
@@ -23,17 +24,25 @@
|
||||
}
|
||||
|
||||
.customSelect__wrapper .customSelect__option {
|
||||
@apply text-black;
|
||||
@apply text-black flex flex-row items-center;
|
||||
}
|
||||
|
||||
.customSelect__wrapper .customSelect__option:focus {
|
||||
@apply bg-custom-blue;
|
||||
}
|
||||
|
||||
.customSelect__wrapper .customSelect__option--is-selected {
|
||||
@apply bg-transparent;
|
||||
}
|
||||
|
||||
.customSelect__wrapper .customSelect__single-value {
|
||||
@apply text-white;
|
||||
}
|
||||
|
||||
.customSelect--is-disabled .customSelect__control--is-disabled {
|
||||
@apply pointer-events-none;
|
||||
}
|
||||
|
||||
.customSelect__wrapper.customSelect--is-disabled {
|
||||
@apply cursor-not-allowed pointer-events-auto;
|
||||
}
|
||||
|
||||
.customSelect__wrapper .customSelect__value-container--is-multi {
|
||||
@apply px-3 py-2 inline-block truncate;
|
||||
}
|
||||
@@ -1,52 +1,93 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import classnames from 'classnames';
|
||||
import ReactSelect from 'react-select';
|
||||
import ReactSelect, { components } from 'react-select';
|
||||
|
||||
import './Select.css';
|
||||
|
||||
const MultiValue = props => {
|
||||
const values = props.selectProps.value;
|
||||
const lastValue = values[values.length - 1];
|
||||
let label = props.data.label;
|
||||
if (lastValue.label !== label) {
|
||||
label += ', ';
|
||||
}
|
||||
|
||||
return <span>{label}</span>;
|
||||
};
|
||||
|
||||
const Option = props => {
|
||||
return (
|
||||
<div>
|
||||
<components.Option {...props}>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={props.isSelected}
|
||||
className="w-6 h-6 mr-2"
|
||||
onChange={e => null}
|
||||
/>
|
||||
<label>{props.value} </label>
|
||||
</components.Option>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const Select = ({
|
||||
autoFocus,
|
||||
className,
|
||||
closeMenuOnSelect,
|
||||
hideSelectedOptions,
|
||||
isClearable,
|
||||
isDisabled,
|
||||
isMulti,
|
||||
isSearchable,
|
||||
name,
|
||||
onChange,
|
||||
options,
|
||||
placeholder,
|
||||
noOptionsMessage,
|
||||
value,
|
||||
}) => {
|
||||
const _components = isMulti ? { Option, MultiValue } : {};
|
||||
|
||||
return (
|
||||
<ReactSelect
|
||||
autoFocus={autoFocus}
|
||||
className={classnames(
|
||||
className,
|
||||
'flex flex-col flex-1 mt-2 customSelect__wrapper'
|
||||
'flex flex-col flex-1 customSelect__wrapper'
|
||||
)}
|
||||
classNamePrefix="customSelect"
|
||||
isDisabled={isDisabled}
|
||||
isClearable={isClearable}
|
||||
isMulti={isMulti}
|
||||
isSearchable={isSearchable}
|
||||
name={name}
|
||||
onChange={onChange}
|
||||
options={options}
|
||||
closeMenuOnSelect={closeMenuOnSelect}
|
||||
hideSelectedOptions={hideSelectedOptions}
|
||||
components={_components}
|
||||
placeholder={placeholder}
|
||||
noOptionsMessage={noOptionsMessage}
|
||||
options={options}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
></ReactSelect>
|
||||
);
|
||||
};
|
||||
|
||||
Select.defaultProps = {
|
||||
className: '',
|
||||
closeMenuOnSelect: true,
|
||||
hideSelectedOptions: true,
|
||||
isClearable: true,
|
||||
isDisabled: false,
|
||||
isMulti: false,
|
||||
isSearchable: true,
|
||||
};
|
||||
|
||||
Select.propTypes = {
|
||||
autoFocus: PropTypes.bool,
|
||||
className: PropTypes.string,
|
||||
closeMenuOnSelect: PropTypes.bool,
|
||||
hideSelectedOptions: PropTypes.bool,
|
||||
isClearable: PropTypes.bool,
|
||||
isDisabled: PropTypes.bool,
|
||||
isMulti: PropTypes.bool,
|
||||
isSearchable: PropTypes.bool,
|
||||
name: PropTypes.string,
|
||||
onChange: PropTypes.func,
|
||||
onChange: PropTypes.func.isRequired,
|
||||
options: PropTypes.arrayOf(
|
||||
PropTypes.shape({
|
||||
value: PropTypes.string,
|
||||
@@ -54,8 +95,18 @@ Select.propTypes = {
|
||||
})
|
||||
),
|
||||
placeholder: PropTypes.string,
|
||||
noOptionsMessage: PropTypes.func,
|
||||
value: PropTypes.string,
|
||||
value: PropTypes.oneOfType([
|
||||
PropTypes.arrayOf(
|
||||
PropTypes.shape({
|
||||
value: PropTypes.string,
|
||||
label: PropTypes.string,
|
||||
})
|
||||
),
|
||||
PropTypes.shape({
|
||||
value: PropTypes.string,
|
||||
label: PropTypes.string,
|
||||
}),
|
||||
]),
|
||||
};
|
||||
|
||||
export default Select;
|
||||
@@ -19,13 +19,13 @@ import { Select } from '@ohif/ui';
|
||||
|
||||
<Playground>
|
||||
<div className="flex flex-col flex-1 p-4 items-center">
|
||||
<div className="w-20">
|
||||
<div className="w-56">
|
||||
<Select
|
||||
options={[
|
||||
{ value: 'one', label: 'one' },
|
||||
{ value: 'two', label: 'two' },
|
||||
{ value: 'three', label: 'three' },
|
||||
{ value: 'four', label: 'four' },
|
||||
{ value: 'ONE', label: 'ONE' },
|
||||
{ value: 'TWO', label: 'TWO' },
|
||||
{ value: 'THREE', label: 'THREE' },
|
||||
{ value: 'FOUR', label: 'FOUR' },
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
@@ -36,14 +36,14 @@ import { Select } from '@ohif/ui';
|
||||
|
||||
<Playground>
|
||||
<div className="flex flex-col flex-1 p-4 items-center">
|
||||
<div className="w-20">
|
||||
<div className="w-56">
|
||||
<Select
|
||||
isDisabled
|
||||
options={[
|
||||
{ value: 'one', label: 'one' },
|
||||
{ value: 'two', label: 'two' },
|
||||
{ value: 'three', label: 'three' },
|
||||
{ value: 'four', label: 'four' },
|
||||
{ value: 'ONE', label: 'ONE' },
|
||||
{ value: 'TWO', label: 'TWO' },
|
||||
{ value: 'THREE', label: 'THREE' },
|
||||
{ value: 'FOUR', label: 'FOUR' },
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
@@ -54,7 +54,7 @@ import { Select } from '@ohif/ui';
|
||||
|
||||
<Playground>
|
||||
<div className="flex flex-col flex-1 p-4 items-center">
|
||||
<div className="w-20">
|
||||
<div className="w-56">
|
||||
<Select
|
||||
noOptionsMessage={() => {
|
||||
'No Options';
|
||||
|
||||
@@ -5,7 +5,10 @@ import EmptyStudies from './EmptyStudies';
|
||||
import Icon from './Icon';
|
||||
import IconButton from './IconButton';
|
||||
import Input from './Input';
|
||||
import NavBar from './NavBar';
|
||||
import InputDateRange from './InputDateRange';
|
||||
import InputLabelWrapper from './InputLabelWrapper';
|
||||
import InputMultiSelect from './InputMultiSelect';
|
||||
import InputText from './InputText';
|
||||
import Select from './Select';
|
||||
import Svg from './Svg';
|
||||
import Table from './Table';
|
||||
@@ -24,7 +27,10 @@ export {
|
||||
Icon,
|
||||
IconButton,
|
||||
Input,
|
||||
NavBar,
|
||||
InputDateRange,
|
||||
InputLabelWrapper,
|
||||
InputMultiSelect,
|
||||
InputText,
|
||||
Select,
|
||||
Svg,
|
||||
Table,
|
||||
|
||||
Reference in new issue
Block a user