initial structure for components rewrite
This commit is contained in:
1 parent
944afe4a3e
commit
c391c007ff
284 files changed
+68087
-17215
No files matched your search
@@ -1,119 +0,0 @@
|
||||
/* NOTE: the order of these styles DO matter */
|
||||
|
||||
/* Will edit everything selected including everything between a range of dates */
|
||||
.CalendarDay__selected_span {
|
||||
background: var(--datePicker-picker-background);
|
||||
color: var(--datePicker-picker-text);
|
||||
border-color: #e4e7e7;
|
||||
}
|
||||
|
||||
/* Will edit selected date or the endpoints of a range of dates */
|
||||
.CalendarDay__selected {
|
||||
background: var(--datePicker-picker-background);
|
||||
color: var(--datePicker-picker-text);
|
||||
border-color: #e4e7e7;
|
||||
}
|
||||
|
||||
/* Will edit when hovered over. _span style also has this property */
|
||||
.CalendarDay__selected:hover,
|
||||
.CalendarDay__default:hover {
|
||||
background: var(--datePicker-picker-background);
|
||||
color: var(--datePicker-picker-text);
|
||||
border-color: #e4e7e7;
|
||||
}
|
||||
|
||||
/* Will edit when the second date (end date) in a range of dates
|
||||
is not yet selected. Edits the dates between your mouse and said date */
|
||||
.CalendarDay__hovered_span:hover,
|
||||
.CalendarDay__hovered_span {
|
||||
background: var(--datePicker-picker-background);
|
||||
color: var(--datePicker-picker-text);
|
||||
border-color: #e4e7e7;
|
||||
}
|
||||
|
||||
/* EXTERIOR INPUT STYLE */
|
||||
/* Container - placement */
|
||||
.DateRangePicker {
|
||||
height: 40px;
|
||||
margin: 0 5px 20px 5px;
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
width: 100%;
|
||||
}
|
||||
/* Container - visual */
|
||||
.DateRangePickerInput {
|
||||
width: calc(100% - 10px); /* Just use padding? */
|
||||
background-color: var(--datePicker-input-background);
|
||||
border-color: var(--datePicker-input-background);
|
||||
color: var(--datePicker-picker-text-input);
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.DateRangePickerInput.DateRangePickerInput__withBorder {
|
||||
border-radius: 4px;
|
||||
background-color: var(--datePicker-input-background);
|
||||
}
|
||||
|
||||
/* Input Container */
|
||||
.DateInput {
|
||||
width: 97px;
|
||||
height: 38px;
|
||||
border-radius: 4px;
|
||||
background-color: var(--datePicker-input-background);
|
||||
}
|
||||
|
||||
/* Actual Input Element */
|
||||
.DateInput > .DateInput_input {
|
||||
border-color: transparent;
|
||||
background-color: transparent;
|
||||
color: var(--datePicker-picker-text-input);
|
||||
height: 38px;
|
||||
font-size: 10pt;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* PRESETS */
|
||||
.PresetDateRangePicker_panel {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.PresetDateRangePicker_button {
|
||||
margin: 0;
|
||||
padding: 4px 8px;
|
||||
color: #00a699;
|
||||
background: var(--datePicker-picker-text-input);
|
||||
}
|
||||
|
||||
.PresetDateRangePicker_button:hover {
|
||||
background: #00a699;
|
||||
color: var(--datePicker-picker-text-input);
|
||||
}
|
||||
|
||||
.CalendarDay__blocked_out_of_range:hover {
|
||||
background: #fff;
|
||||
cursor: not-allowed;
|
||||
color: var(--datePicker-picker-text-disabled);
|
||||
}
|
||||
|
||||
.DateRangePicker_select {
|
||||
background-color: white;
|
||||
color: var(--datePicker-picker-text);
|
||||
border: 1px solid #e4e7e7;
|
||||
border-radius: 4px;
|
||||
display: inline-block;
|
||||
padding: 3px 20px 3px 10px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
appearance: none;
|
||||
|
||||
background-image: linear-gradient(45deg, transparent 50%, gray 50%),
|
||||
linear-gradient(135deg, gray 50%, transparent 50%);
|
||||
background-position: calc(100% - 11px) 11px, calc(100% - 6px) calc(11px);
|
||||
background-size: 5px 5px, 5px 5px;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.DateRangePicker_select:hover {
|
||||
border-color: #c4c4c4;
|
||||
}
|
||||
@@ -1,140 +0,0 @@
|
||||
// If you want to continue using CSS stylesheets and classes...
|
||||
// https://github.com/airbnb/react-dates#initialize
|
||||
import 'react-dates/initialize';
|
||||
import 'react-dates/lib/css/_datepicker.css';
|
||||
import './CustomDateRangePicker.css';
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { DateRangePicker } from 'react-dates';
|
||||
import moment from 'moment';
|
||||
import i18n from '@ohif/i18n';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
function CustomDateRangePicker(props) {
|
||||
moment.locale(i18n.language); // using i18n in the date picker
|
||||
|
||||
const { t } = useTranslation('DatePicker');
|
||||
|
||||
const {
|
||||
onDatesChange,
|
||||
startDate,
|
||||
endDate,
|
||||
presets,
|
||||
...dateRangePickerProps
|
||||
} = props;
|
||||
|
||||
const renderDatePresets = () => {
|
||||
const { presets } = props;
|
||||
|
||||
return (
|
||||
<div className="PresetDateRangePicker_panel">
|
||||
{presets.map(({ text, start, end }) => {
|
||||
const isSelected = startDate === start && endDate === end;
|
||||
|
||||
return (
|
||||
<button
|
||||
key={text}
|
||||
type="button"
|
||||
className={`PresetDateRangePicker_button ${
|
||||
isSelected ? 'PresetDateRangePicker_button__selected' : ''
|
||||
}`}
|
||||
onClick={() =>
|
||||
onDatesChange({
|
||||
startDate: start,
|
||||
endDate: end,
|
||||
preset: true,
|
||||
})
|
||||
}
|
||||
>
|
||||
{text}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
const renderMonthElement = ({ month, onMonthSelect, onYearSelect }) => {
|
||||
const containerStyle = {
|
||||
margin: '0 5px',
|
||||
};
|
||||
|
||||
const renderYearsOptions = () => {
|
||||
const yearsRange = 20;
|
||||
const options = [];
|
||||
|
||||
for (let i = 0; i < yearsRange; i++) {
|
||||
const year = moment().year() - i;
|
||||
options.push(<option value={year}>{year}</option>);
|
||||
}
|
||||
|
||||
return options;
|
||||
};
|
||||
|
||||
renderMonthElement.propTypes = {
|
||||
onMonthSelect: PropTypes.func,
|
||||
onYearSelect: PropTypes.func,
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={{ display: 'flex', justifyContent: 'center' }}>
|
||||
<div style={containerStyle}>
|
||||
<select
|
||||
className="DateRangePicker_select"
|
||||
value={month.month()}
|
||||
onChange={e => onMonthSelect(month, e.target.value)}
|
||||
>
|
||||
{moment.months().map((label, value) => (
|
||||
<option key={value} value={value}>
|
||||
{label}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
<div style={containerStyle}>
|
||||
{}
|
||||
<select
|
||||
className="DateRangePicker_select"
|
||||
value={month.year()}
|
||||
onChange={e => onYearSelect(month, e.target.value)}
|
||||
>
|
||||
{renderYearsOptions()}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<DateRangePicker
|
||||
{...dateRangePickerProps}
|
||||
startDate={startDate}
|
||||
endDate={endDate}
|
||||
renderCalendarInfo={renderDatePresets}
|
||||
onDatesChange={onDatesChange}
|
||||
renderMonthElement={renderMonthElement}
|
||||
startDatePlaceholderText={t('Start Date')}
|
||||
endDatePlaceholderText={t('End Date')}
|
||||
phrases={{
|
||||
closeDatePicker: t('Common:Close'),
|
||||
clearDates: t('Clear dates'),
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
CustomDateRangePicker.propTypes = {
|
||||
presets: PropTypes.arrayOf(
|
||||
PropTypes.shape({
|
||||
text: PropTypes.string,
|
||||
start: PropTypes.required,
|
||||
end: PropTypes.required,
|
||||
})
|
||||
),
|
||||
onDatesChange: PropTypes.func.isRequired,
|
||||
startDate: PropTypes.instanceOf(Date),
|
||||
endDate: PropTypes.instanceOf(Date),
|
||||
month: PropTypes.instanceOf(Date),
|
||||
};
|
||||
|
||||
export default CustomDateRangePicker;
|
||||
@@ -1,38 +0,0 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
|
||||
import { Icon } from './../../elements/Icon';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
class PageToolbar extends PureComponent {
|
||||
static propTypes = {
|
||||
onImport: PropTypes.func,
|
||||
};
|
||||
|
||||
onImport = event => {
|
||||
if (this.props.onImport) {
|
||||
this.props.onImport(event);
|
||||
}
|
||||
};
|
||||
|
||||
getImportTool() {
|
||||
if (this.props.onImport) {
|
||||
return (
|
||||
<div className="addNewStudy btn-file">
|
||||
<label
|
||||
htmlFor="btnImport"
|
||||
style={{ width: '18px' }}
|
||||
onClick={this.onImport}
|
||||
>
|
||||
<Icon name="plus" />
|
||||
</label>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return <div className="studylistToolbar">{this.getImportTool()}</div>;
|
||||
}
|
||||
}
|
||||
|
||||
export { PageToolbar };
|
||||
@@ -1,63 +0,0 @@
|
||||
.pagination-area
|
||||
display: flex;
|
||||
color: var(--text-secondary-color);
|
||||
font-size: 13px
|
||||
font-weight: normal !important
|
||||
|
||||
label
|
||||
font-weight: normal
|
||||
|
||||
select
|
||||
margin: 5px
|
||||
background-color: var(--primary-background-color)
|
||||
color: white
|
||||
|
||||
|
||||
.rows-dropdown
|
||||
width: 25%;
|
||||
padding-right: 15px;
|
||||
padding-left: 15px;
|
||||
|
||||
.pagination-buttons
|
||||
width: 75%;
|
||||
padding-right: 15px;
|
||||
padding-left: 15px;
|
||||
|
||||
.form-group
|
||||
margin-bottom: 15px;
|
||||
|
||||
.rows-per-page label.wrapperLabel
|
||||
display: inline-table !important
|
||||
margin: 0 4px
|
||||
|
||||
select
|
||||
margin: 0px 4px 0px 4px
|
||||
width: 42px
|
||||
|
||||
.page-buttons
|
||||
margin: 0
|
||||
text-align: right
|
||||
font-weight: normal
|
||||
ul.pagination-control
|
||||
margin: 0
|
||||
|
||||
li
|
||||
display: table-cell
|
||||
padding: 5px 2px
|
||||
|
||||
button
|
||||
padding: 4px 8px
|
||||
background-color: var(--primary-background-color)
|
||||
border-color: var(--ui-gray)
|
||||
color: var(--ui-gray-darkest)
|
||||
color: white
|
||||
text-decoration: none
|
||||
|
||||
&:hover:enabled
|
||||
color: var(--active-color)
|
||||
|
||||
.active
|
||||
button
|
||||
background-color: var(--ui-gray)
|
||||
border-color: #ddd
|
||||
color: white
|
||||
@@ -1,18 +0,0 @@
|
||||
import React from 'react';
|
||||
import { Icon } from './../../elements/Icon';
|
||||
// TODO: useTranslation
|
||||
import { withTranslation } from '../../contextProviders';
|
||||
|
||||
function StudyListLoadingText({ t: translate }) {
|
||||
return (
|
||||
<div className="loading-text">
|
||||
{translate('Loading')}... <Icon name="circle-notch" animation="pulse" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const connectedComponent = withTranslation('StudyListLoadingText')(
|
||||
StudyListLoadingText
|
||||
);
|
||||
|
||||
export { connectedComponent as StudyListLoadingText };
|
||||
@@ -1,150 +0,0 @@
|
||||
import React, { useState } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import moment from 'moment';
|
||||
import { isInclusivelyBeforeDay } from 'react-dates';
|
||||
import CustomDateRangePicker from './CustomDateRangePicker.js';
|
||||
import { Icon } from './../../elements/Icon';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const getDateEntry = (datePicked, rangeDatePicked) => {
|
||||
return rangeDatePicked || datePicked || null;
|
||||
};
|
||||
|
||||
const getDateEntryFromRange = (today, numOfDays, edge = 'start') => {
|
||||
if (typeof numOfDays !== 'number') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (edge === 'end') {
|
||||
return today;
|
||||
} else {
|
||||
today.subtract(numOfDays, 'days');
|
||||
}
|
||||
};
|
||||
|
||||
function TableSearchFilter(props) {
|
||||
const {
|
||||
meta,
|
||||
values,
|
||||
onSort,
|
||||
onValueChange,
|
||||
sortFieldName,
|
||||
sortDirection,
|
||||
// TODO: Rename
|
||||
studyListDateFilterNumDays,
|
||||
} = props;
|
||||
|
||||
const { studyDateTo, studyDateFrom } = values || {};
|
||||
const [focusedInput, setFocusedInput] = useState(null);
|
||||
const { t, ready: translationsAreReady } = useTranslation('Common');
|
||||
|
||||
const sortIcons = ['sort', 'sort-up', 'sort-down'];
|
||||
const sortIconForSortField =
|
||||
sortDirection === 'asc' ? sortIcons[1] : sortIcons[2];
|
||||
|
||||
const today = moment();
|
||||
const lastWeek = moment().subtract(7, 'day');
|
||||
const lastMonth = moment().subtract(1, 'month');
|
||||
|
||||
const defaultStartDate = getDateEntryFromRange(
|
||||
today,
|
||||
studyListDateFilterNumDays,
|
||||
'start'
|
||||
);
|
||||
const defaultEndDate = getDateEntryFromRange(
|
||||
today,
|
||||
studyListDateFilterNumDays,
|
||||
'end'
|
||||
);
|
||||
|
||||
const studyDatePresets = [
|
||||
{
|
||||
text: t('Today'),
|
||||
start: today,
|
||||
end: today,
|
||||
},
|
||||
{
|
||||
text: t('Last 7 days'),
|
||||
start: lastWeek,
|
||||
end: today,
|
||||
},
|
||||
{
|
||||
text: t('Last 30 days'),
|
||||
start: lastMonth,
|
||||
end: today,
|
||||
},
|
||||
];
|
||||
|
||||
return translationsAreReady
|
||||
? meta.map((field, i) => {
|
||||
const { displayText, fieldName, inputType } = field;
|
||||
const isSortField = sortFieldName === fieldName;
|
||||
const sortIcon = isSortField ? sortIconForSortField : sortIcons[0];
|
||||
|
||||
return (
|
||||
<th key={`${fieldName}-${i}`}>
|
||||
<label
|
||||
htmlFor={`filter-${fieldName}`}
|
||||
onClick={() => onSort(fieldName)}
|
||||
>
|
||||
{`${displayText}`}
|
||||
<Icon name={sortIcon} style={{ fontSize: '12px' }} />
|
||||
</label>
|
||||
{inputType === 'text' && (
|
||||
<input
|
||||
type="text"
|
||||
id={`filter-${fieldName}`}
|
||||
className="form-control studylist-search"
|
||||
value={values[fieldName]}
|
||||
onChange={e => onValueChange(fieldName, e.target.value)}
|
||||
/>
|
||||
)}
|
||||
{inputType === 'date-range' && (
|
||||
// https://github.com/airbnb/react-dates
|
||||
<CustomDateRangePicker
|
||||
// Required
|
||||
startDate={getDateEntry(studyDateFrom, defaultStartDate)}
|
||||
startDateId="start-date"
|
||||
endDate={getDateEntry(studyDateTo, defaultEndDate)}
|
||||
endDateId="end-date"
|
||||
// TODO: We need a dynamic way to determine which fields values to update
|
||||
onDatesChange={({ startDate, endDate, preset = false }) => {
|
||||
onValueChange('studyDateFrom', startDate);
|
||||
onValueChange('studyDateTo', endDate);
|
||||
}}
|
||||
focusedInput={focusedInput}
|
||||
onFocusChange={updatedVal => setFocusedInput(updatedVal)}
|
||||
// Optional
|
||||
numberOfMonths={1} // For med and small screens? 2 for large?
|
||||
showClearDates={true}
|
||||
anchorDirection="left"
|
||||
presets={studyDatePresets}
|
||||
hideKeyboardShortcutsPanel={true}
|
||||
isOutsideRange={day => !isInclusivelyBeforeDay(day, moment())}
|
||||
/>
|
||||
)}
|
||||
</th>
|
||||
);
|
||||
})
|
||||
: null;
|
||||
}
|
||||
|
||||
TableSearchFilter.propTypes = {
|
||||
meta: PropTypes.arrayOf(
|
||||
PropTypes.shape({
|
||||
displayText: PropTypes.string.isRequired,
|
||||
fieldName: PropTypes.string.isRequired,
|
||||
inputType: PropTypes.oneOf(['text', 'date-range']).isRequired,
|
||||
size: PropTypes.number.isRequired,
|
||||
})
|
||||
).isRequired,
|
||||
values: PropTypes.object.isRequired,
|
||||
onSort: PropTypes.func.isRequired,
|
||||
sortFieldName: PropTypes.string,
|
||||
sortDirection: PropTypes.oneOf([null, 'asc', 'desc']),
|
||||
};
|
||||
|
||||
TableSearchFilter.defaultProps = {};
|
||||
|
||||
export { TableSearchFilter };
|
||||
export default TableSearchFilter;
|
||||
@@ -1,4 +0,0 @@
|
||||
export { StudyList } from './StudyList.js';
|
||||
export { TableSearchFilter } from './TableSearchFilter.js';
|
||||
export { TablePagination } from './TablePagination.js';
|
||||
export { PageToolbar } from './PageToolbar.js';
|
||||
Reference in new issue
Block a user