saagie-ui
Version:
Saagie UI from Saagie Design System
185 lines (158 loc) • 5.45 kB
JavaScript
import React, { useEffect, useState } from 'react';
import PropTypes from 'prop-types';
import dayjs from 'dayjs';
import customParseFormat from 'dayjs/plugin/customParseFormat';
import classnames from 'classnames';
dayjs.extend(customParseFormat);
const propTypes = {
defaultClassName: PropTypes.oneOfType([PropTypes.string, PropTypes.arrayOf(PropTypes.string)]),
/**
* Class name for the DatePicker.
*/
className: PropTypes.string,
/**
* The custom tag for the root of the DatePicker.
*/
tag: PropTypes.oneOfType([PropTypes.func, PropTypes.string]),
/**
* The default date to be selected in the calendar.
*/
selected: PropTypes.oneOfType([PropTypes.string, PropTypes.instanceOf(dayjs)]),
/**
* Set the new date value to be selected in the calendar.
*/
onChange: PropTypes.func,
/**
* Set the date format.
*/
defaultFormat: PropTypes.string,
};
const defaultProps = {
defaultClassName: 'sui-o-date-picker',
className: '',
tag: 'div',
selected: dayjs(),
onChange: () => {},
defaultFormat: 'YYYY/MM/DD',
};
export const DatePicker = ({
defaultClassName,
className,
tag: Tag,
selected,
onChange,
defaultFormat,
}) => {
const [selectedDate, setSelectedDate] = useState(
dayjs(selected, defaultFormat, true).isValid() ? dayjs(selected, defaultFormat) : dayjs(),
);
const classes = classnames(defaultClassName, className);
const nowDate = dayjs();
const calendarHeader = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
const handleDateChange = (date) => {
setSelectedDate(date);
};
// Subtract 1 month from the "selectedDate" and set the day to the 1st of that month
const handlePreviousMonth = () => {
const previousMonth = dayjs(selectedDate).subtract(1, 'month').date(1);
setSelectedDate(previousMonth);
};
// Add 1 month to the "selectedDate" and set the day to the 1st of that month
const handleNextMonth = () => {
const nextMonth = dayjs(selectedDate).add(1, 'month').date(1);
setSelectedDate(nextMonth);
};
const getCalendarDays = () => {
const firstDayOfMonth = dayjs(selectedDate).startOf('month');
const lastDayOfMonth = dayjs(selectedDate).endOf('month');
const daysInMonth = lastDayOfMonth.date();
const startingDay = firstDayOfMonth.day() - 1;
const days = [];
// Calculate the days of the previous month
const prevMonth = dayjs(selectedDate).subtract(1, 'month').date(1);
const prevMonthDays = prevMonth.endOf('month').date();
// Add the days of the previous month
for (let i = startingDay - 1; i >= 0; i -= 1) {
days.push(prevMonth.date(prevMonthDays - i));
}
// Add the days of the current month
for (let i = 1; i <= daysInMonth; i += 1) {
days.push(firstDayOfMonth.date(i));
}
// Add the days of the next month
for (let i = 1; days.length % 7 !== 0; i += 1) {
days.push(dayjs(selectedDate).add(1, 'month').date(i));
}
return days;
};
const renderCalendarDays = () => {
const calendarDays = [];
const allDaysInMonth = getCalendarDays();
const daysCount = allDaysInMonth.length;
for (let i = 0; i < daysCount; i += 7) {
const weekDays = allDaysInMonth.slice(i, i + 7);
const weekRow = (
<tr key={i}>
{weekDays.map((day) => {
const isToday = day.isSame(nowDate, 'day');
const isSelected = day.isSame(selectedDate, 'day');
const isCurrentMonth = day.month() === selectedDate.month();
const style = {
color: (!isSelected && isToday && '#fff') || (!isCurrentMonth ? '#a0b2ca' : ''),
// eslint-disable-next-line no-nested-ternary
background: (!isSelected && isToday && '#0172f5') || (isSelected ? '#e9ecf1' : ''),
fontWeight: isSelected ? 'bold' : '',
};
return (
<td
key={day.valueOf()}
role="gridcell"
onClick={() => handleDateChange(day)}
style={style}
className="sui-o-date-picker__days"
>
{day.date()}
</td>
);
})}
</tr>
);
calendarDays.push(weekRow);
}
return calendarDays;
};
useEffect(() => {
if (!selectedDate.isSame(selected) && dayjs(selected, defaultFormat, true).isValid()) {
setSelectedDate(dayjs(selected, defaultFormat));
}
}, [selected]);
// To prevent the parent of the date change
useEffect(() => {
onChange(selectedDate.format(defaultFormat));
}, [selectedDate]);
return (
<Tag className={classes}>
<div className="sui-g-grid as--no-wrap as--between sui-h-pb-md">
<button type="button" onClick={handlePreviousMonth} className="sui-g-grid__item as--pull"><</button>
<span className="sui-g-grid__item as--auto">
{selectedDate.format('MMMM YYYY')}
</span>
<button type="button" onClick={handleNextMonth} className="sui-g-grid__item as--push">></button>
</div>
<table role="grid">
<thead>
<tr>
{calendarHeader.map((day) => (
<th key={day}>{day}</th>
))}
</tr>
</thead>
<tbody>
{renderCalendarDays()}
</tbody>
</table>
</Tag>
);
};
DatePicker.propTypes = propTypes;
DatePicker.defaultProps = defaultProps;