react-calendar-custom-date
Version:
A React component for choosing dates and date ranges.
247 lines (235 loc) • 7.47 kB
JavaScript
/* eslint-disable no-fallthrough */
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import { startOfDay, format, isSameDay, isAfter, isBefore, endOfDay } from 'date-fns';
class DayCell extends Component {
constructor(props, context) {
super(props, context);
this.state = {
hover: false,
active: false,
};
this.getClassNames = this.getClassNames.bind(this);
this.handleMouseEvent = this.handleMouseEvent.bind(this);
this.handleKeyEvent = this.handleKeyEvent.bind(this);
this.renderSelectionPlaceholders = this.renderSelectionPlaceholders.bind(this);
this.renderPreviewPlaceholder = this.renderPreviewPlaceholder.bind(this);
}
handleKeyEvent(event) {
const { day } = this.props;
switch (event.keyCode) {
case 13: //space
case 32: //enter
if (event.type === 'keydown') {
this.props.onMouseDown(day);
} else {
this.props.onMouseUp(day);
}
break;
}
}
handleMouseEvent(event) {
const { day, disabled, onPreviewChange } = this.props;
const stateChanges = {};
if (disabled) {
onPreviewChange();
return;
}
switch (event.type) {
case 'mouseenter':
this.props.onMouseEnter(day);
onPreviewChange(day);
stateChanges.hover = true;
break;
case 'blur':
case 'mouseleave':
stateChanges.hover = false;
break;
case 'mousedown':
stateChanges.active = true;
this.props.onMouseDown(day);
break;
case 'mouseup':
event.stopPropagation();
stateChanges.active = false;
this.props.onMouseUp(day);
break;
case 'focus':
onPreviewChange(day);
break;
}
if (Object.keys(stateChanges).length) {
this.setState(stateChanges);
}
}
getClassNames() {
const {
isPassive,
isToday,
isWeekend,
isStartOfWeek,
isEndOfWeek,
isStartOfMonth,
isEndOfMonth,
disabled,
styles,
} = this.props;
return classnames(styles.day, {
[styles.dayPassive]: isPassive,
[styles.dayDisabled]: disabled,
[styles.dayToday]: isToday,
[styles.dayWeekend]: isWeekend,
[styles.dayStartOfWeek]: isStartOfWeek,
[styles.dayEndOfWeek]: isEndOfWeek,
[styles.dayStartOfMonth]: isStartOfMonth,
[styles.dayEndOfMonth]: isEndOfMonth,
[styles.dayHovered]: this.state.hover,
[styles.dayActive]: this.state.active,
});
}
renderPreviewPlaceholder() {
const { preview, day, styles } = this.props;
if (!preview) return null;
const startDate = preview.startDate ? endOfDay(preview.startDate) : null;
const endDate = preview.endDate ? startOfDay(preview.endDate) : null;
const isInRange =
(!startDate || isAfter(day, startDate)) && (!endDate || isBefore(day, endDate));
const isStartEdge = !isInRange && isSameDay(day, startDate);
const isEndEdge = !isInRange && isSameDay(day, endDate);
return (
<span
className={classnames({
[styles.dayStartPreview]: isStartEdge,
[styles.dayInPreview]: isInRange,
[styles.dayEndPreview]: isEndEdge,
})}
style={{ color: preview.color }}
/>
);
}
renderSelectionPlaceholders() {
const { styles, ranges, day } = this.props;
if (this.props.displayMode === 'date') {
let isSelected = isSameDay(this.props.day, this.props.date);
return isSelected ? (
<span className={styles.selected} style={{ color: this.props.color }} />
) : null;
}
const inRanges = ranges.reduce((result, range) => {
let startDate = range.startDate;
let endDate = range.endDate;
if (startDate && endDate && isBefore(endDate, startDate)) {
[startDate, endDate] = [endDate, startDate];
}
startDate = startDate ? endOfDay(startDate) : null;
endDate = endDate ? startOfDay(endDate) : null;
const isInRange =
(!startDate || isAfter(day, startDate)) && (!endDate || isBefore(day, endDate));
const isStartEdge = !isInRange && isSameDay(day, startDate);
const isEndEdge = !isInRange && isSameDay(day, endDate);
if (isInRange || isStartEdge || isEndEdge) {
return [
...result,
{
isStartEdge,
isEndEdge: isEndEdge,
isInRange,
...range,
},
];
}
return result;
}, []);
return inRanges.map((range, i) => (
<span
key={i}
className={classnames({
[styles.startEdge]: range.isStartEdge,
[styles.endEdge]: range.isEndEdge,
[styles.inRange]: range.isInRange,
})}
style={{ color: range.color || this.props.color }}
/>
));
}
render() {
const { styles, cellInfoClassName, cellInfo } = this.props;
let dayInfo = [];
if (this.props && this.props !== 'undefined' && cellInfo) {
dayInfo = this.props.cellInfo.filter(data => {
return isSameDay(new Date(data.date), new Date(this.props.day));
});
}
return (
<button
type="button"
onMouseEnter={this.handleMouseEvent}
onMouseLeave={this.handleMouseEvent}
onFocus={this.handleMouseEvent}
onMouseDown={this.handleMouseEvent}
onMouseUp={this.handleMouseEvent}
onBlur={this.handleMouseEvent}
onPauseCapture={this.handleMouseEvent}
onKeyDown={this.handleKeyEvent}
onKeyUp={this.handleKeyEvent}
className={this.getClassNames(styles)}
{...(this.props.disabled || this.props.isPassive ? { tabIndex: -1 } : {})}
style={{ color: this.props.color }}>
{this.renderSelectionPlaceholders()}
{this.renderPreviewPlaceholder()}
<span
className={
dayInfo.length > 0 ? `${styles.dayNumber} cell-info-wrapper` : styles.dayNumber
}>
<span>{format(this.props.day, 'D')}</span>
{dayInfo.length > 0 && (
<span
className={cellInfoClassName}
style={{ display: 'inline-block', marginTop: '15px' }}>
{dayInfo[0].value}
</span>
)}
</span>
</button>
);
}
}
DayCell.defaultProps = {};
export const rangeShape = PropTypes.shape({
startDate: PropTypes.object,
endDate: PropTypes.object,
color: PropTypes.string,
key: PropTypes.string,
autoFocus: PropTypes.bool,
disabled: PropTypes.bool,
showDateDisplay: PropTypes.bool,
});
DayCell.propTypes = {
day: PropTypes.object.isRequired,
date: PropTypes.object,
ranges: PropTypes.arrayOf(rangeShape),
preview: PropTypes.shape({
startDate: PropTypes.object,
endDate: PropTypes.object,
}),
onPreviewChange: PropTypes.func,
previewColor: PropTypes.string,
disabled: PropTypes.bool,
isPassive: PropTypes.bool,
isToday: PropTypes.bool,
isWeekend: PropTypes.bool,
isStartOfWeek: PropTypes.bool,
isEndOfWeek: PropTypes.bool,
isStartOfMonth: PropTypes.bool,
isEndOfMonth: PropTypes.bool,
color: PropTypes.string,
displayMode: PropTypes.oneOf(['dateRange', 'date']),
styles: PropTypes.object,
onMouseDown: PropTypes.func,
onMouseUp: PropTypes.func,
onMouseEnter: PropTypes.func,
cellInfo: PropTypes.array,
cellInfoClassName: PropTypes.string,
};
export default DayCell;