UNPKG

saagie-ui

Version:

Saagie UI from Saagie Design System

185 lines (158 loc) 5.45 kB
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">&lt;</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">&gt;</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;