@availity/reactstrap-validation-date
Version:
Wrapper for react-date-range to work with availity-reactstrap-validation
232 lines (202 loc) • 5.95 kB
JavaScript
/* eslint-disable react/default-props-match-prop-types */
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { Button, InputGroupAddon, Popover } from 'reactstrap';
import dayjs from 'dayjs';
import isBetween from 'dayjs/plugin/isBetween';
import customParseFormat from 'dayjs/plugin/customParseFormat';
import {
inputType,
isoDateFormat,
} from 'availity-reactstrap-validation/lib/AvValidator/utils';
import Icon from '@availity/icon';
import { Calendar } from 'react-date-range';
import { AvInput } from 'availity-reactstrap-validation';
import './styles.scss';
dayjs.extend(isBetween);
dayjs.extend(customParseFormat);
const getRelativeDate = (validate, key) => {
if (validate && validate.dateRange && validate.dateRange[key]) {
const value = validate.dateRange[key];
if (value.units) {
return dayjs().add(value.value, value.units);
}
return value;
}
return null;
};
const theme = {
DateRange: { background: '#ffffff' },
Calendar: { color: '#4d4f53' },
MonthButton: { background: 'none' },
MonthArrowPrev: { borderRightColor: '#4d4f53' },
MonthArrowNext: { borderLeftColor: '#4d4f53' },
DaySelected: {
background: '#2261b5',
borderColor: '#143a6c',
},
DayActive: {
background: '#2261b5',
boxShadow: 'none',
},
DayInRange: {
background: '#0093e8',
color: '#fff',
},
DayHover: {
background: '#e8ebeb',
color: '#4d4f53',
},
};
class AvDate extends Component {
static propTypes = {
...AvInput.propTypes,
calendarIcon: PropTypes.node,
hideIcon: PropTypes.bool,
};
static contextTypes = {
FormCtrl: PropTypes.object.isRequired,
};
static defaultProps = {
type: 'text',
datepicker: true,
calendarIcon: <Icon name="calendar" />,
hideIcon: false,
};
static getDerivedStateFromProps = ({ value }, prevState) => {
if (value !== undefined && value !== prevState.value) {
return { value };
}
return null;
};
getRef = el => {
this.inputRef = el;
if (this.props.innerRef) {
if (typeof this.props.innerRef === 'function') {
this.props.innerRef(el);
} else {
this.props.innerRef.current = el;
}
}
};
reset() {
this.setState({ value: '' });
}
valueParser(value) {
if (this.state.format === isoDateFormat) return value;
const formats = [this.state.format, 'MMDDYYYY', 'YYYYMMDD'];
for (const i in formats) {
const date = dayjs(value, formats[i]);
if (date.isValid()) return date.format(isoDateFormat);
}
return value;
}
valueFormatter(value) {
const formats = [isoDateFormat, this.state.format, 'MMDDYYYY', 'YYYYMMDD'];
for (const i in formats) {
const date = dayjs(value, formats[i]);
if (date.isValid()) return date.format(this.state.format);
}
return value;
}
constructor(props) {
super(props);
this.valueParser = this.valueParser.bind(this);
this.valueFormatter = this.valueFormatter.bind(this);
this.state = { open: false };
if (props.type.toLowerCase() === 'date' && inputType.date) {
this.state.format = isoDateFormat;
} else {
this.state.format =
(props.validate && props.validate.date && props.validate.date.format) ||
'MM/DD/YYYY';
}
}
togglePicker = () => {
this.setState({ open: this.props.disabled ? false : !this.state.open });
};
onFieldChange = event => {
event.persist();
const value = event && event.target ? event.target.value : event;
this.setState({ value, open: false }, () => {
if (this.props.onChange) this.props.onChange(event, value);
});
};
onPickerChange = value => {
if (!this.context.FormCtrl.isTouched(this.props.name))
this.context.FormCtrl.setTouched(this.props.name);
this.setState(
{ value: value.format(this.state.format), open: false },
() => {
if (this.props.onChange)
this.props.onChange(
{ target: this.inputRef },
value.format(this.state.format)
);
}
);
};
render() {
const { datepicker, calendarIcon, hideIcon, ...props } = this.props;
const id = `${(this.props.id || this.props.name).replace(
/[^a-zA-Z0-9]/gi,
''
)}-btn`;
const input = (
<AvInput
placeholder={this.state.format.toLowerCase()}
{...props}
innerRef={this.getRef}
value={this.state.value || ''}
onChange={this.onFieldChange}
valueFormatter={this.valueFormatter}
valueParser={this.valueParser}
validate={{ date: true, ...this.props.validate }}
/>
);
if (!datepicker) return input;
return (
<div className="input-group">
{input}
{!hideIcon && (
<InputGroupAddon addonType="append">
<Button
id={id}
color="light"
type="button"
onClick={this.togglePicker}
disabled={props.disabled}
style={{
zIndex: 'auto',
}}
>
{calendarIcon}
<span className="sr-only">Toggle Calendar</span>
</Button>
</InputGroupAddon>
)}
<Popover
placement="top"
target={id}
isOpen={this.state.open}
toggle={this.togglePicker}
className="popover-calendar"
>
<Calendar
date={this.state.value}
onChange={this.onPickerChange}
format={this.state.format}
theme={this.props.theme || theme}
minDate={
this.props.min || getRelativeDate(this.props.validate, 'start')
}
maxDate={
this.props.max || getRelativeDate(this.props.validate, 'end')
}
/>
</Popover>
</div>
);
}
}
export default AvDate;